diff --git a/.gitignore b/.gitignore index 7d5ec4f91..d5d75af1c 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /.kprove* /.krun* result* +.mypy_cache \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 7696e80d5..4e6044fcd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,6 +26,7 @@ RUN apt update \ pkg-config \ protobuf-compiler \ python3 \ + python3-pip \ zlib1g-dev RUN curl -sSL https://get.haskellstack.org/ | sh @@ -35,6 +36,8 @@ RUN apt-get update \ && apt-get upgrade --yes \ && apt-get install --yes nodejs +RUN pip3 install dataclasses dacite + ARG USER_ID=1000 ARG GROUP_ID=1000 RUN groupadd --gid $GROUP_ID user \ diff --git a/Jenkinsfile b/Jenkinsfile index 764bdb005..d886f23c7 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -38,6 +38,7 @@ pipeline { stage('Interactive') { steps { sh 'make test-interactive' } } stage('Node') { steps { sh 'make test-node -j4 TEST_PORT=9001' } } stage('Sourcemappings') { steps { sh 'make test-sourcemap' } } + stage('Report Generation') { steps { sh 'make test-generate-report' } } } } } diff --git a/Makefile b/Makefile index a35b1565e..4d543badd 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,7 @@ export PATH:=$(IELE_BIN):$(PATH) .PHONY: all clean distclean libff protobuf coverage secp256k1 cryptopp \ build build-interpreter build-vm build-check build-haskell build-node build-testnode build-assembler \ install install-interpreter install-vm install-kiele install-check uninstall \ - split-tests split-vm-tests split-blockchain-tests test-node test-iele-coverage \ + split-tests split-vm-tests split-blockchain-tests test-node test-iele-coverage test-generate-report \ test-evm test-vm test-blockchain test-wellformed test-illformed test-bad-packet test-interactive test-sourcemap \ test-iele test-iele-haskell test-iele-failing test-iele-slow test-iele-node assemble-iele-test test .SECONDARY: @@ -153,7 +153,7 @@ TEST_PORT = 10000 TEST_ARGS = --no-unparse TEST_DIR = $(IELE_DIR)/tests -test: split-tests test-vm test-iele test-iele-haskell test-iele-coverage test-wellformed test-illformed test-interactive test-node test-sourcemap +test: split-tests test-vm test-iele test-iele-haskell test-iele-coverage test-wellformed test-illformed test-interactive test-node test-sourcemap test-generate-report split-tests: split-vm-tests split-blockchain-tests @@ -252,6 +252,11 @@ test-node: TEST_PORT=9001 test-node: $(TEST_DIR)/node-test.sh $(MAKEFLAGS) --port $(TEST_PORT) +report_tests := $(wildcard $(TEST_DIR)/reports/*/report.json) +test-generate-report: $(report_tests:.json=.html) +$(TEST_DIR)/reports/%/report.html: $(TEST_DIR)/reports/%/report.json + cd $(dir $@) && kiele generate-report report.json -o report.html + $(TEST_DIR)/VMTests/%: TEST_MODE = VMTESTS %.iele.test-wellformed: TEST_SCHEDULE = DANSE %.iele.test-illformed: TEST_SCHEDULE = DANSE @@ -435,6 +440,8 @@ install_libs := \ $(iele_interpreter_libs) \ $(iele_haskell_libs) \ kore-json.py \ + kiele-generate-report.py \ + static-report.html \ version $(IELE_RUNNER): $(IELE_DIR)/kiele @@ -449,10 +456,20 @@ $(IELE_LIB)/kore-json.py: $(IELE_DIR)/kore-json.py @mkdir -p $(dir $@) $(INSTALL) $< $@ +$(IELE_LIB)/kiele-generate-report.py: $(IELE_DIR)/kiele-generate-report.py + @mkdir -p $(dir $@) + $(INSTALL) $< $@ + +$(IELE_LIB)/static-report.html: $(IELE_DIR)/static-report.html + @mkdir -p $(dir $@) + $(INSTALL) $< $@ + $(INSTALL_BIN)/iele-interpreter: $(patsubst %, $(INSTALL_LIB)/%, $(iele_interpreter_libs)) $(INSTALL_BIN)/iele-check: $(patsubst %, $(INSTALL_LIB)/%, $(iele_check_libs)) $(INSTALL_BIN)/kiele: $(INSTALL_LIB)/kore-json.py +$(INSTALL_BIN)/kiele: $(INSTALL_LIB)/kiele-generate-report.py +$(INSTALL_BIN)/kiele: $(INSTALL_LIB)/static-report.html $(INSTALL_BIN)/kiele: $(INSTALL_LIB)/version $(INSTALL_BIN)/%: $(IELE_BIN)/% diff --git a/kiele b/kiele index ca008242c..521b1929d 100755 --- a/kiele +++ b/kiele @@ -99,6 +99,24 @@ run_vm() { "${cmd[@]}" } +run_generate_report() { + if [[ -f $(dirname $(which kiele))/static-report.html ]]; then + report_template=$(dirname $(which kiele))/static-report.html + else + report_template=$INSTALL_LIB/static-report.html + fi + output_path="" + while [[ $# -gt 0 ]]; do + arg="$1" + case $arg in + -o|--output) output_path="$2" ; shift 2 ;; + *) shift ;; + esac + done + echo "Generating report" + echo $(kiele-generate-report.py $report_template $run_file $output_path) generated +} + # Main # ---- @@ -111,14 +129,16 @@ if [[ "$run_command" == 'help' ]] || [[ "$run_command" == '--help' ]] ; then $0 krun [--backend (standalone|haskell|node|check)] * * $0 check * * $0 vm * + $0 generate-report [-o|--output ] $0 [help|--help|version|--version] - $0 assemble : Translate IELE assembly - $0 krun : Run given input using 'krun' - $0 check : Run the IELE well-formedness checker on - $0 vm : Run the KIELE VM server. + $0 assemble : Translate IELE assembly + $0 krun : Run given input using 'krun' + $0 check : Run the IELE well-formedness checker on + $0 vm : Run the KIELE VM server. + $0 generate-report : Generate the static HTML report $0 help : Display this help message. $0 version : Display the versions of KIELE in use. @@ -129,6 +149,8 @@ if [[ "$run_command" == 'help' ]] || [[ "$run_command" == '--help' ]] ; then is an argument you want to pass to K is one of (--no-unparse|--debug|--host|--port|--mode|--coverage |--schedule|--module|--definition|--depth) + is the path to the report json file + is the output HTML report file path (default: \$uuid.html) " exit 0 fi @@ -187,10 +209,11 @@ if [[ "$run_command" != 'vm' ]]; then fi case "$run_command" in - assemble) run_assemble "$@" ;; - interpret) run_interpret "$@" ;; - krun) run_krun "$@" ;; - check) run_check "$@" ;; - vm) run_vm "$@" ;; + assemble) run_assemble "$@" ;; + interpret) run_interpret "$@" ;; + krun) run_krun "$@" ;; + check) run_check "$@" ;; + vm) run_vm "$@" ;; + generate-report) run_generate_report "$@";; *) $0 help ; fatal "Unknown command: $run_command" ;; esac diff --git a/kiele-generate-report.py b/kiele-generate-report.py new file mode 100755 index 000000000..19a45ed2c --- /dev/null +++ b/kiele-generate-report.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python3 + +from dataclasses import dataclass, asdict +import json +from typing import Dict, List, Optional, Tuple +from textwrap import wrap +import uuid +import os +from dacite.core import from_dict +from shutil import copyfile, rmtree +from urllib.parse import quote +import sys +from datetime import datetime + + +# All class below are translated from the Haskell code at +# the PR: https://github.com/runtimeverification/firefly-web/pull/306/ + +@dataclass +class Report: + status: str + tag: Optional[str] + hasFireflyLog: bool + created: str + reportId: str + token: str + coverage: str + commit: Optional[str] + type: str + + +@dataclass +class IeleContract: + name: str + sourceMap: str + coverage: str + + +""" +key is the contract hash +""" +IeleContracts = Dict[str, IeleContract] + + +@dataclass +class IeleReport: + contents: str + contracts: IeleContracts + + +""" +key is the source file name +""" +IeleReports = Dict[str, IeleReport] + + +@dataclass +class Source: + fileId: int + filename: str + sourceLines: List[str] + + +@dataclass +class CoveredState: + tag: str + contents: Optional[List[int]] # (covered, total) + + +Coverages = List[Tuple[int, List[Tuple[int, CoveredState]]]] + + +@dataclass +class CoverageMap: + """ + Solidity files source code + """ + ieleSources: List[Source] + """ + Solidity coverage data + """ + ieleCoverage: Coverages # Dict[int, Dict[int, CoveredState]] + + +@dataclass +class ContractArtifact: + contractName: str + sourceName: Optional[str] + coverageMap: CoverageMap + coverage: int + fileId: int + + +@dataclass +class CoverageSummary: + bytecodeHash: str + account: Optional[str] + contractName: Optional[str] + sourceName: Optional[str] + """ + Coverage percent + """ + coverage: int + """ + Id of source file the contract was defined in + """ + mainContract: Optional[int] + + +def make_coverage_summaries(artifacts: List[ContractArtifact]) -> List[CoverageSummary]: + summaries: List[CoverageSummary] = [] + for artifact in artifacts: + summaries.append(CoverageSummary(bytecodeHash=artifact.contractName, account=None, contractName=artifact.contractName, + sourceName=artifact.sourceName, coverage=artifact.coverage, mainContract=artifact.fileId)) + return summaries + + +def make_coverage_map(source_name: str, content: str, file_id: int, source_map: str, coverage: str) -> Tuple[CoverageMap, int]: + lines: List[int] = list(map(lambda s: int( + s.split(":")[0]) - 1, source_map.split(";"))) + states = get_states(coverage) + coverage_ = calculate_coverage(states) + coverage_map: CoverageMap = CoverageMap( + ieleSources=[Source( + fileId=file_id, filename=source_name, sourceLines=content.splitlines())], + ieleCoverage=[(file_id, list(zip(lines, states)))] + ) + return (coverage_map, coverage_) + + +def get_states(coverage: str) -> List[CoveredState]: + chunks: List[str] = wrap(coverage.replace("0x", "", 1), 2) + states: List[CoveredState] = [] + for chunk in chunks: + if chunk == "00": + states.append(CoveredState(tag="NotCovered", contents=None)) + elif chunk == "01" or chunk == "0D": + states.append(CoveredState(tag="Covered", contents=None)) + else: + states.append(CoveredState(tag="Weak", contents=[0, 0])) + return states + + +def calculate_coverage(states: List[CoveredState]) -> int: + covered = 0 + for state in states: + if state.tag == "Covered" or state.tag == "Weak": + covered += 1 + return int(covered * 100 / len(states)) + + +def convert_iele_reports_to_contract_artifacts(iele_reports: IeleReports) -> List[ContractArtifact]: + artifacts: List[ContractArtifact] = [] + file_id = 0 + for source_name in iele_reports: + file_id += 1 + iele_report = iele_reports[source_name] + contents = iele_report.contents + contracts = iele_report.contracts + for hash in contracts: + contract = contracts[hash] + (coverage_map, coverage) = make_coverage_map(source_name, + contents, file_id, contract.sourceMap, contract.coverage) + artifacts.append(ContractArtifact(contractName=contract.name, sourceName=source_name, + coverageMap=coverage_map, coverage=coverage, fileId=file_id)) + return artifacts + + +def write_json_file(file_path: str, json: str): + f = open(file_path, "w") + f.write(json) + f.close() + + +def generate_static_report(report_template_path: str, reports_json_path: str, output_report_path: str = ""): + report_id = str(uuid.uuid4()) + os.makedirs("./reports", exist_ok=True) + + # TODO: Maybe allow the this path to be passed as argument? + f = open(reports_json_path, "r") + j = json.loads(f.read()) + f.close() + + iele_reports: IeleReports = {} + for source_name in j: + iele_reports[source_name] = from_dict( + data_class=IeleReport, data=j[source_name]) + artifacts = convert_iele_reports_to_contract_artifacts(iele_reports) + if len(artifacts) == 0: + raise Exception("no artifacts found") + report_base_path = "./reports/" + report_id + os.makedirs(report_base_path, exist_ok=False) + + # Write the summary file + summaries = make_coverage_summaries(artifacts) + f = open(os.path.join(report_base_path, "./summary.json"), "w") + f.write(json.dumps(list(map(lambda summary: asdict(summary), summaries)))) + f.close() + + # Write coverage information files + for artifact in artifacts: + hash = artifact.contractName + source_name = artifact.sourceName + coverage_map = artifact.coverageMap + write_json_file(os.path.join(report_base_path, quote( + source_name + "-" + hash + "-iele.json", safe="")), json.dumps(asdict(coverage_map))) + + # Copy original + original_path = os.path.join(report_base_path, "./original") + os.makedirs(original_path) + copyfile(reports_json_path, + os.path.join(original_path, "./reports.json")) + + # Create ${report_id}.json file + firefly_log_exists = os.path.exists("./firefly.log") + if firefly_log_exists: + copyfile("./firefly.log", os.path.join(original_path, "./firefly.log")) + report: Report = Report(status="success", tag=None, hasFireflyLog=firefly_log_exists, created=datetime.today( + ).strftime('%Y-%m-%dT%H:%M:%SZ'), reportId=report_id, token="(generated)", coverage="ParseSuccess", commit=None, type="iele") + write_json_file(os.path.join(report_base_path, report_id + + ".json"), json.dumps(asdict(report))) + + # Create JavaScript code + js_code = "window.FIREFLY_REPORT_FILES = {}" + onlyfiles = [f for f in os.listdir(report_base_path) if os.path.isfile(os.path.join(report_base_path, f))] + [os.path.join( + "original", f) for f in os.listdir(os.path.join(report_base_path, "./original")) if not f.endswith(".zip")] + for f in onlyfiles: + file = open(os.path.join(report_base_path, f), "r") + content = file.read() + file.close() + if not f.endswith(".json"): + content = json.dumps(content) + js_code += "\nwindow.FIREFLY_REPORT_FILES[\"" + \ + report_id + "/" + f + "\"] = " + content + "\n" + + # Inject the js code into the HTML report template file. + f = open(report_template_path, "r") + template = f.read() + f.close() + report_html = template.replace( + "", "\n") + + if output_report_path.strip() == "": + output_report_path = report_id + ".html" + + f = open(output_report_path, "w") + f.write(report_html) + f.close() + + # Clean up the report directory + rmtree(report_base_path) + if len(os.listdir("./reports")) == 0: + rmtree("./reports") + + return output_report_path + +report_template_path = sys.argv[1] +reports_json_path = sys.argv[2] +output_report_path = "" +if len(sys.argv) > 3: + output_report_path = sys.argv[3] +print(generate_static_report(report_template_path, reports_json_path, output_report_path)) diff --git a/package/debian/Dockerfile b/package/debian/Dockerfile index 404e7ed73..bb4581338 100644 --- a/package/debian/Dockerfile +++ b/package/debian/Dockerfile @@ -33,6 +33,8 @@ RUN apt-get update \ RUN curl -sSL https://get.haskellstack.org/ | sh +RUN pip3 install dataclasses dacite + ARG USER_ID=1000 ARG GROUP_ID=1000 RUN groupadd -g $GROUP_ID user \ diff --git a/static-report.html b/static-report.html new file mode 100644 index 000000000..ad6af96b5 --- /dev/null +++ b/static-report.html @@ -0,0 +1,176 @@ + + + + + + + + + + + Developer tooling for Ethereum smart contracts | Firefly Blockchain | Runtime Verification Inc + + + + + + + + +
+
+
+
+
+ + + diff --git a/tests/reports/firefly-demo/report.json b/tests/reports/firefly-demo/report.json new file mode 100644 index 000000000..2c823fddf --- /dev/null +++ b/tests/reports/firefly-demo/report.json @@ -0,0 +1,77 @@ +{ + "iele-examples/erc20.iele": { + "contents": "// Copyright (c) 2017 Runtime Verification, Inc. All Rights Reserved.\n//\n// This contract implements the ERC-20 token standard.\ncontract ERC20 {\n\n // constant used for shifts\n @addressShift = 160\n\n // account storage location for the supply variable. This variable holds\n // the current total supply of the token\n @supplyKey = 0\n\n // The account storage is divided into two regions:\n // - the balance region maps each account address to its balance\n // - the allowance region maps each pair of owner and spender accounts to the\n // allowance that the owner approves to be transferred by the spender\n // Account storage keys that correspond to each of the regions use different\n // bit prefixes as follows\n @balanceRegion = 1\n @allowanceRegion = 2\n\n // hashes of event names\n @approvalEvent = 0x7134692B230B9E1FFA39098904722134159652B09C5BC41D88D6698779D228FF\n @transferEvent = 0xF099CD8BDE557814842A3121E8DDFD433A539B8C9F14BF31EBF108D12E6196E9\n\n// computes a unique key in the specified account storage region for a\n// particular account address. %region represents a unique bit prefix on the\n// key that will be ored with the specified address.\ndefine @mapKey(%region, %address) {\n %addressMask = 20\n %realAddress = twos %addressMask, %address\n %shifted = shift %region, @addressShift\n %ored = or %shifted, %realAddress\n ret %ored\n}\n\n// sets the balance of the given account to the given value\ndefine @setBalance(%account, %value) {\n // get the key for the account in the balance region\n %key = call @mapKey(@balanceRegion, %account)\n\n // store the given value as the balance of the account\n sstore %value, %key\n ret void\n}\n\n// sets the approved allowance to be transfered from the given owner by the\n// given spender\ndefine @setAllowance(%owner, %spender, %allowance) {\n // get the key for the pair of owner and spender accounts in the allowance\n // region\n %key1 = call @mapKey(@allowanceRegion, %owner)\n %key2 = call @mapKey(%key1, %spender)\n\n // store the given value as the approved allowance to be transfered from the\n // owner by the spender\n sstore %allowance, %key2\n ret void\n}\n\n// logs an approval event\ndefine @log.Approval(%owner, %spender, %allowance) {\n // store the allowance value in the local execution memory\n %logCell = 0\n store %allowance, %logCell\n\n // log an approval event, where the owner approves the allowance value to be\n // transferred by the spender\n log %logCell, @approvalEvent, %owner, %spender\n ret void\n}\n\n// logs a transfer event\ndefine @log.Transfer(%from, %to, %value) {\n // store the transferred value in the local execution memory\n %logCell = 0\n store %value, %logCell\n\n // log a transfer event between two accounts along with the transferred\n // value\n log %logCell, @transferEvent, %from, %to\n ret void\n}\n\n// returns the total supply of the token\ndefine public @totalSupply() {\n %supply = sload @supplyKey\n ret %supply\n}\n\n// initializes an ERC-20 compliant token with the given total supply\ndefine @init(%supply) {\n // store the total supply\n sstore %supply, @supplyKey\n\n // set the balance of the creator to the amount of the total supply\n %caller = call @iele.caller()\n call @setBalance(%caller, %supply)\n ret void\n}\n\n// returns the current balance of an account\ndefine public @balanceOf(%id) {\n // get the key for the account in the balance region\n %balanceRegion = 1\n %key = call @mapKey(%balanceRegion, %id)\n\n // get and return the account's current balance\n %balance = sload %key\n ret %balance\n}\n\n// returns the currently approved allowance to be transferred from the owner\n// by the spender\ndefine public @allowance(%owner, %spender) {\n // get the key for the pair of owner and spender accounts in the allowance\n // region\n %allowanceRegion = 2\n %key1 = call @mapKey(%allowanceRegion, %owner)\n %key2 = call @mapKey(%key1, %spender)\n\n // get and return the current allowance\n %allowance = sload %key2\n ret %allowance\n}\n\n// approves the given allowance to be transferred from the caller by the given\n// spender account\ndefine public @approve(%spender, %allowance) {\n // ensure that the given allowance is not negative, this is only needed\n // because IELE doesn't support unsigned integers\n %lt = cmp lt %allowance, 0\n br %lt, throw\n\n // get the account address of the caller (the owner)\n %owner = call @iele.caller() // written as msg.sender in Viper\n\n // approve the allowance to be transferred from the owner by the spender\n call @setAllowance(%owner, %spender, %allowance)\n\n // log the approval\n call @log.Approval(%owner, %spender, %allowance)\n\n // should always return true\n ret true\n\nthrow:\n call @iele.invalid()\n}\n\n// transfers the given value from the caller to the given target account\ndefine public @transfer(%to, %value) {\n // ensure that the given value is not negative, this is only needed because\n // IELE doesn't support unsigned integers\n %lt = cmp lt %value, 0\n br %lt, throw\n\n // get the account address of the caller (the source account)\n %from = call @iele.caller()\n\n // get the current balance of the source and ensure that it can cover the\n // value to be transferred\n %balanceFrom = call @balanceOf(%from)\n %lt = cmp lt %balanceFrom, %value\n br %lt, throw\n\n // set the new balance of the source after subtracting the value to be\n // transferred\n %balanceFrom = sub %balanceFrom, %value\n call @setBalance(%from, %balanceFrom)\n\n // set the new balance of the target by adding the value to be transferred to\n // its existing balance\n %balanceTo = call @balanceOf(%to)\n %balanceTo = add %balanceTo, %value // no worries of overflow\n call @setBalance(%to, %balanceTo)\n\n // log the transfer\n call @log.Transfer(%from, %to, %value)\n\n // should either return true or throw in case of insufficient funds in the\n // source\n ret true\n\nthrow:\n call @iele.invalid()\n}\n\n// transfers the given value from the given source to the given target account,\n// the approved allowance to be transferred from the source by the caller should\n// cover the value to be transferred\ndefine public @transferFrom(%from, %to, %value) {\n // ensure that the given value is not negative, this is only needed because\n // IELE doesn't support unsigned integers\n %lt = cmp lt %value, 0\n br %lt, throw\n\n // get the account address of the caller\n %caller = call @iele.caller()\n\n // get the current balance of the source and ensure that it can cover the\n // value to be transferred\n %balanceFrom = call @balanceOf(%from)\n %lt = cmp lt %balanceFrom, %value\n br %lt, throw\n\n // get the current allowance for this caller and ensure that it can cover\n // the value to be transferred\n %allowance = call @allowance(%from, %caller)\n %lt = cmp lt %allowance, %value\n br %lt, throw\n\n // set the new balance of the source after subtracting the value to be\n // transferred\n %balanceFrom = sub %balanceFrom, %value\n call @setBalance(%from, %balanceFrom)\n\n // set the new allowance for this caller after subtracting the value to be\n // transferred\n %allowance = sub %allowance, %value\n call @setAllowance(%from, %caller, %allowance)\n\n // set the new balance of the target by adding the value to be transferred to\n // its existing balance\n %balanceTo = call @balanceOf(%to)\n %balanceTo = add %balanceTo, %value // no worries of overflow\n call @setBalance(%to, %balanceTo)\n\n // log the transfer\n call @log.Transfer(%from, %to, %value)\n\n // should either return true or throw in case of insufficient funds in the\n // source or insufficient allowance for this caller\n ret true\n\nthrow:\n call @iele.invalid() // throwing exception\n}\n\n}\n", + "contracts": { + "0x147fa4fb4e630f0fb1fab51f6eaa2cbb34ec0a9500c80f04d28b1999a7b7c408": { + "name": "ERC20", + "sourceMap": "30:3:30:20;31:3:31:45;32:3:32:42;32:3:32:42;33:3:33:36;34:3:34:12;40:3:40:48;40:3:40:48;43:3:43:22;44:3:44:11;52:3:52:49;52:3:52:49;53:3:53:40;57:3:57:27;58:3:58:11;64:3:64:15;65:3:65:29;69:3:69:49;69:3:69:49;70:3:70:11;76:3:76:15;77:3:77:25;81:3:81:43;81:3:81:43;82:3:82:11;87:3:87:29;87:3:87:29;88:3:88:14;94:3:94:29;94:3:94:29;97:3:97:32;98:3:98:37;99:3:99:11;105:3:105:21;106:3:106:43;109:3:109:24;110:3:110:15;118:3:118:23;119:3:119:49;120:3:120:40;123:3:123:27;124:3:124:17;132:3:132:29;132:3:132:29;133:3:133:16;136:3:136:31;139:3:139:51;142:3:142:51;145:3:145:11;145:3:145:11;148:3:148:23;155:3:155:25;155:3:155:25;156:3:156:16;159:3:159:30;163:3:163:40;164:3:164:36;165:3:165:16;169:3:169:42;170:3:170:40;174:3:174:36;175:3:175:38;176:3:176:36;179:3:179:41;183:3:183:11;183:3:183:11;186:3:186:23;195:3:195:25;195:3:195:25;196:3:196:16;199:3:199:32;203:3:203:40;204:3:204:36;205:3:205:16;209:3:209:47;210:3:210:34;211:3:211:16;215:3:215:42;216:3:216:40;220:3:220:38;221:3:221:49;225:3:225:36;226:3:226:38;227:3:227:36;230:3:230:41;234:3:234:11;234:3:234:11;237:3:237:23", + "coverage": "0x01000101000100000100010100010001000101000100010100010100010100010001000100010001000100000100010001010001010001000100010001000100010100010100010001000100010001000100010001000001" + } + } + }, + "iele-examples/factorial.iele": { + "contents": "contract Factorial {\n\n define public @factorial(%n) {\n // ensure that %n is larger than or equal to 0.\n %lt = cmp lt %n, 0\n br %lt, throw\n\n %result = 1\n\n condition:\n %cond = cmp le %n, 0\n br %cond, after_loop\n\n loop_body:\n %result = mul %result, %n\n %n = sub %n, 1\n br condition\n\n after_loop:\n ret %result\n\n throw:\n call @iele.invalid()\n }\n\n define @init() {}\n}\n", + "contracts": { + "0x0ba15590a6d39f5ee97f58b11a45e9d5a5343f5b9c399fe7fec12114f3420fd4": { + "name": "Factorial", + "sourceMap": "5:5:5:23;5:5:5:23;6:5:6:18;8:5:8:16;11:5:11:25;11:5:11:25;12:5:12:25;15:5:15:30;16:5:16:24;16:5:16:24;17:5:17:17;20:5:20:16;23:5:23:25", + "coverage": "0x01010001000001000101000100" + } + } + }, + "iele-examples/forwarder.iele": { + "contents": "// Copyright (c) 2017 Runtime Verification, Inc. All Rights Reserved.\n//\n// This contract implements a forwarder that forwards any funds sent to\n// the account it is deployed with to the account that created it.\ncontract Forwarder {\n\n // initializes a forwarder by storing in the account storage the account\n // number of the creator, this is the account to which received funds should\n // be forwarded\n define @init() {\n %parent = call @iele.caller()\n %zero = 0\n sstore %parent, %zero\n }\n\n // deposit forwards the recieved funds to the creator of this account\n define public @deposit() {\n // get the received funds\n %value = call @iele.callvalue()\n\n // get the creator account, where we should forward funds, from the storage\n %zero = 0\n %sender = sload %zero\n\n // forward funds by calling deposit at the creator account\n %gas = call @iele.gas()\n %status = call @deposit at %sender () send %value , gaslimit %gas\n br %status, throw // contract call failed\n ret void\n\n throw:\n call @iele.invalid()\n }\n}\n", + "contracts": { + "0x25adcada9724c05b64ed1b2ffc7acabd69a7479e7a620c9c8ba49d9133534650": { + "name": "Forwarder", + "sourceMap": "11:5:11:34;12:5:12:14;13:5:13:26;19:5:19:36;22:5:22:14;23:5:23:26;26:5:26:28;27:5:27:70;28:5:28:22;29:5:29:13;32:5:32:25", + "coverage": "0x0100010001000100010001" + } + } + }, + "iele-examples/forwardingWallet-copycreate.iele": { + "contents": "// Copyright (c) 2017 Runtime Verification, Inc. All Rights Reserved.\n//\n// This example showcases the dynamic contract creation feature of IELE. The\n// Wallet contract can dynamically create accounts and deploy a full copy of a\n// contract already deployed in an existing account. The deployed code is not\n// statically known but it is not dynamically generated either.\n\n// This contract implements a wallet that can serve deposit and withdrawal\n// requests as well as setup new accounts that forward funds sent to them to\n// this wallet. Only the owner can withdraw and/or setup new forwarders. The\n// contract deployed with a new forwarder account is found with (and copied\n// from) a provided account. An example of such a contract can be found in the\n// forwarder.iele example.\ncontract Wallet {\n\n // initializes a wallet account by storing the provided account address in\n // %owner as the wallet's owner and the provided account address in %forwarder\n // as the account from where the forwarder contract can be copied.\n define @init(%owner, %forwarder) {\n sstore %owner, 0\n sstore %forwarder, 1\n }\n\n // deposits the sent value to the wallet account's balance\n define public @deposit() {}\n\n // withdraws amount equal to %value from the wallet and sends it to the\n // account address %to\n define public @withdraw(%to, %value) {\n // get the wallet's owner from storage\n %owner = sload 0\n\n // ensure that the caller (the withdrawer) is the wallet's owner\n %caller = call @iele.caller()\n %isnotowner = cmp ne %caller, %owner\n br %isnotowner, throw\n\n // withdraw the desired amount and send it to the desired account\n %gas = call @iele.gas()\n %status = call @deposit at %to () send %value , gaslimit %gas\n br %status, throw // throw if call fails\n ret void\n\n throw:\n call @iele.invalid()\n }\n\n // creates a new account that simply forwards any funds sent to it to this\n // wallet and returns the address of the created forwarder account\n define public @newForwarder() {\n // get the wallet's owner from storage\n %owner = sload 0\n\n // ensure that the caller is the wallet's owner\n %caller = call @iele.caller()\n %isnotowner = cmp ne %caller, %owner\n br %isnotowner, throw\n\n // get the address of the account with the forwarder contract to copy\n %forwarder = sload 1\n\n // create a new account, deploy the forwarder contract copied from the\n // designated account and return the new account's address\n %status, %addr = copycreate %forwarder () send 0\n br %status, throw\n ret %addr\n\n throw:\n call @iele.invalid()\n }\n}\n", + "contracts": { + "0x65cf550b25d54d64ae527a1f1e950624051e420970d200c524cf269d57d74c3e": { + "name": "Wallet", + "sourceMap": "20:5:20:21;20:5:20:21;21:5:21:25;21:5:21:25;31:5:31:21;31:5:31:21;34:5:34:34;35:5:35:41;36:5:36:26;39:5:39:28;40:5:40:66;41:5:41:22;42:5:42:13;45:5:45:25;52:5:52:21;52:5:52:21;55:5:55:34;56:5:56:41;57:5:57:26;60:5:60:25;60:5:60:25;64:5:64:53;64:5:64:53;65:5:65:22;66:5:66:14;69:5:69:25", + "coverage": "0x0101000001010001000100010001000001000100000101000100" + } + } + }, + "iele-examples/forwardingWallet.iele": { + "contents": "// Copyright (c) 2017 Runtime Verification, Inc. All Rights Reserved.\n//\n// This examples showcases the dynamic contract creation feature of IELE. The\n// Wallet contract can dynamically create accounts with the Forwarder contract\n// deployed, since the code for the Forwarder contract is statically available.\n\n// This contract implements a forwarder that forwards any funds sent to\n// the account it is deployed with to the account that created it.\ncontract Forwarder {\n\n // initializes a forwarded by storing in the account storage the account\n // number of the creator, this is the account to which received funds should\n // be forwarded\n define @init() {\n %parent = call @iele.caller()\n sstore %parent, 0\n }\n\n // deposit forwards the received funds to the creator of this account\n define public @deposit() {\n // get the received funds\n %value = call @iele.callvalue()\n\n // get the creator account, where we should forward funds, from the storage\n %sender = sload 0\n\n // forward funds by calling deposit at the creator account\n %gas = call @iele.gas()\n %status = call @deposit at %sender () send %value , gaslimit %gas\n br %status, throw\n ret void\n\n throw:\n call @iele.invalid()\n }\n}\n\n// This contract implements a wallet that can serve deposit and withdrawal\n// requests as well as setup new accounts that forward funds sent to them to\n// this wallet. Only the owner can withdraw and/or setup new forwarders.\ncontract Wallet {\n external contract Forwarder\n\n // initializes a wallet account by storing the provided account address as\n // the wallet's owner\n define @init(%owner) {\n sstore %owner, 0\n }\n\n // deposits the sent value to the wallet account's balance\n define public @deposit() {}\n\n // withdraws amount equal to %value from the wallet and sends it to the\n // account address %to\n define public @withdraw(%to, %value) {\n // get the wallet's owner from storage\n %owner = sload 0\n\n // ensure that the caller (the withdrawer) is the wallet's owner\n %caller = call @iele.caller()\n %isnotowner = cmp ne %caller, %owner\n br %isnotowner, throw\n\n // withdraw the desired amount and send it to the desired account\n %gas = call @iele.gas()\n %status = call @deposit at %to () send %value , gaslimit %gas\n br %status, throw\n ret void\n\n throw:\n call @iele.invalid()\n }\n\n // creates a new account that simply forwards any funds sent to it to this\n // wallet and returns the address of the created forwarder account\n define public @newForwarder() {\n // get the wallet's owner from storage\n %owner = sload 0\n\n // ensure that the caller is the wallet's owner\n %caller = call @iele.caller()\n %isnotowner = cmp ne %caller, %owner\n br %isnotowner, throw\n\n // create a new account, deploy the Forwarder contract with it and\n // return its address\n %status, %addr = create Forwarder () send 0\n br %status, throw\n ret %addr\n\n throw:\n call @iele.invalid()\n }\n}\n", + "contracts": { + "0x25adcada9724c05b64ed1b2ffc7acabd69a7479e7a620c9c8ba49d9133534650": { + "name": "Forwarder", + "sourceMap": "15:5:15:34;16:5:16:22;16:5:16:22;22:5:22:36;25:5:25:22;25:5:25:22;28:5:28:28;29:5:29:70;30:5:30:22;31:5:31:13;34:5:34:25", + "coverage": "0x0100000100000100010001" + }, + "0x3e1f630c98ec696c7935dcc11ffeaebf41d26f2c0cfce951ddc8d4f506c27683": { + "name": "Wallet", + "sourceMap": "47:5:47:21;47:5:47:21;57:5:57:21;57:5:57:21;60:5:60:34;61:5:61:41;62:5:62:26;65:5:65:28;66:5:66:66;67:5:67:22;68:5:68:13;71:5:71:25;78:5:78:21;78:5:78:21;81:5:81:34;82:5:82:41;83:5:83:26;87:5:87:48;87:5:87:48;88:5:88:22;89:5:89:14;92:5:92:25", + "coverage": "0x01010000010001000100010001010001000101000100" + } + } + }, + "iele-examples/simpleOpenAuction.iele": { + "contents": "// Copyright (c) 2017 Runtime Verification, Inc. All Rights Reserved.\n//\n// This contract implements a simple open auction. When this contract is\n// deployed with an account, the account is hosting an auction and can\n// manage incoming bids, as well as close the auction and settle the transfer\n// of funds from the final highest bidder to the auction's beneficiary.\ncontract SimpleOpenAuction {\n\n// variable locations - the following account storage locations hold the\n// described information:\n\n // account address of the beneficiary of the auction\n @beneficiary = 0\n\n // timestamp of the start of the auction, bids received before that time\n // will not be accepted\n @auction.start = 1\n\n // timestamp of the end of the auction, bids received after that time\n // will not be accepted\n @auction.end = 2\n\n // account address of the current highest bidder\n @highest.bidder = 3\n\n // value of the current highest bid\n @highest.bid = 4\n\n // set to 1 after the auction has been settled (the auction end time has passed and\n // the beneficiary has received the final highest bid, set to 0 otherwise\n @settled = 5\n\n// initializes an auction with the given beneficiary and bidding time duration\ndefine @init(%beneficiary, %bidding.time) {\n // init beneficiary\n sstore %beneficiary, @beneficiary\n\n // init auction start timestamp\n %auction.start = call @iele.timestamp()\n sstore %auction.start, @auction.start\n\n // init auction end timestamp\n %auction.end = add %auction.start, %bidding.time\n sstore %auction.end, @auction.end\n\n // init highest bidder, highest bid, and settled\n sstore 0, @highest.bidder\n sstore 0, @highest.bid\n sstore 0, @settled\n}\n\n// sends a bid with value equal to the value sent with the corresponding\n// account call\n// if the bid becomes the new highest bid returns with exit status 0, else\n// reverts with exit status -1\ndefine public @bid() {\n // check if auction is active\n %timestamp = call @iele.timestamp()\n %auction.end = sload @auction.end\n %auction.closed = cmp ge %timestamp, %auction.end\n br %auction.closed, revert.bid\n\n // check if incoming bid is higher than current highest bid\n %bid.value = call @iele.callvalue()\n %highest.bid = sload @highest.bid\n %low.bid = cmp le %bid.value, %highest.bid\n br %low.bid, revert.bid\n\n // check if we need to refund current highest bidder\n %no.refund.needed = iszero %highest.bid\n br %no.refund.needed, update.bid\n\nrefund.bid:\n // refund current highest bidder\n %highest.bidder = sload @highest.bidder\n %gas = call @iele.gas()\n %status = call @deposit at %highest.bidder() send %highest.bid, gaslimit %gas\n br %status, throw\n\nupdate.bid:\n // store the new highest bidder and bid\n %new.highest.bidder = call @iele.caller()\n sstore %new.highest.bidder, @highest.bidder\n sstore %bid.value, @highest.bid\n\n // return with exit status 0\n ret void\n\nrevert.bid:\n // the auction was closed or incoming bid was not high enough, revert so\n // that the bidder gets their bid back\n revert -1\n\nthrow:\n call @iele.invalid()\n}\n\n// settles the auction by sending the final highest bid value to the\n// beneficiary and returns the address of the final highest bidder's\n// account\n// if the auction is still active, reverts with exit status -1\ndefine public @settle.auction() {\n // check if auction has been closed\n %timestamp = call @iele.timestamp()\n %auction.end = sload @auction.end\n %auction.active = cmp lt %timestamp, %auction.end\n br %auction.active, auction.active\n\n // check if auction has been already settled\n %already.settled = sload @settled\n br %already.settled, exit\n\n // send highest bid to beneficiary and settle the auction\n %highest.bid = sload @highest.bid\n %beneficiary = sload @beneficiary\n %gas = call @iele.gas()\n %status = call @deposit at %beneficiary() send %highest.bid, gaslimit %gas\n br %status, throw\n sstore 1, @settled\n\nexit:\n // return the address of the final highest bidder's account\n %highest.bidder = sload @highest.bidder\n ret %highest.bidder\n\nauction.active:\n // auction is still active, revert with exit status -1\n revert -1\n\nthrow:\n call @iele.invalid()\n}\n\n}\n", + "contracts": { + "0x967516488e9ffc0578279d4dbc7330da8a9a779cf4f57aa9a57920ee0db79c2c": { + "name": "SimpleOpenAuction", + "sourceMap": "36:3:36:36;36:3:36:36;39:3:39:42;40:3:40:40;40:3:40:40;43:3:43:51;44:3:44:36;44:3:44:36;47:3:47:28;47:3:47:28;47:3:47:28;48:3:48:25;48:3:48:25;48:3:48:25;49:3:49:21;49:3:49:21;49:3:49:21;58:3:58:38;59:3:59:36;59:3:59:36;60:3:60:52;61:3:61:33;64:3:64:38;65:3:65:36;65:3:65:36;66:3:66:45;67:3:67:26;70:3:70:42;71:3:71:35;75:3:75:42;75:3:75:42;76:3:76:26;77:3:77:80;78:3:78:20;82:3:82:44;83:3:83:46;83:3:83:46;84:3:84:34;84:3:84:34;87:3:87:11;92:3:92:12;92:3:92:12;95:3:95:23;104:3:104:38;105:3:105:36;105:3:105:36;106:3:106:52;107:3:107:37;110:3:110:36;110:3:110:36;111:3:111:28;114:3:114:36;114:3:114:36;115:3:115:36;115:3:115:36;116:3:116:26;117:3:117:77;118:3:118:20;119:3:119:21;119:3:119:21;119:3:119:21;123:3:123:42;123:3:123:42;124:3:124:22;128:3:128:12;128:3:128:12;131:3:131:23", + "coverage": "0x01010001010001010000000101010000000100000100010000010001000101000100010000010100010100010000010001010001010000010001000000010100010100" + } + } + }, + "iele-examples/sum.iele": { + "contents": "// int sum(int n) {\n// int result;\n// result = 0;\n// while (n > 0) {\n// result = result + n;\n// n = n - 1;\n// }\n// return result; }\n\n\ncontract Sum {\n\n define public @sum(%n) {\n // ensure that %n is larger than or equal to 0.\n %lt = cmp lt %n, 0\n br %lt, throw\n\n %result = 0\n\n condition:\n %cond = cmp le %n, 0\n br %cond, after_loop\n\n loop_body:\n %result = add %result, %n\n %n = sub %n, 1\n br condition\n\n after_loop:\n ret %result\n\n throw:\n call @iele.invalid()\n }\n\n define @init() {}\n}\n", + "contracts": { + "0xb950254d1258a682c61c9a491017c8f1ab38bc5c0adddff928cdd35305b44d68": { + "name": "Sum", + "sourceMap": "15:5:15:23;15:5:15:23;16:5:16:18;18:5:18:16;21:5:21:25;21:5:21:25;22:5:22:25;25:5:25:30;26:5:26:24;26:5:26:24;27:5:27:17;30:5:30:16;33:5:33:25", + "coverage": "0x01010001000001000101000100" + } + } + } +}