├── .envrc ├── .gitattributes ├── .github └── workflows │ ├── build.yml │ └── deploy-book.yaml ├── .gitignore ├── .gitmodules ├── .hlint.yaml ├── LICENSE ├── Makefile ├── README.md ├── act.cabal ├── doc ├── book.toml ├── mermaid-init.js ├── mermaid.min.js └── src │ ├── SUMMARY.md │ ├── backends.md │ ├── coq.md │ ├── equiv.md │ ├── hevm.md │ ├── internals.md │ ├── introduction.md │ ├── language.md │ └── smt.md ├── examples ├── binarySearch │ └── binarySearch.act ├── contractCreation │ ├── modest.act │ └── modest.sol ├── deposit │ ├── deposit-spec.md │ └── inc_merkle_tree.png ├── homogeneous │ ├── homogeneous.act │ ├── homogeneous.smt2 │ ├── homogeneous.sol │ └── statemachinequery.smt2 ├── multipleContracts │ ├── multi.act │ └── multi.sol └── transfer │ └── transfer.act ├── flake.lock ├── flake.nix ├── funding.json ├── hie.yaml ├── lib └── ActLib.v ├── nix ├── sources.json └── sources.nix ├── src ├── Act │ ├── CLI.hs │ ├── Consistency.hs │ ├── Coq.hs │ ├── Decompile.hs │ ├── Dev.hs │ ├── Enrich.hs │ ├── Error.hs │ ├── HEVM.hs │ ├── HEVM_utils.hs │ ├── Lex.x │ ├── Makefile │ ├── Parse.y │ ├── Print.hs │ ├── SMT.hs │ ├── Syntax.hs │ ├── Syntax │ │ ├── Annotated.hs │ │ ├── TimeAgnostic.hs │ │ ├── Timing.hs │ │ ├── Typed.hs │ │ ├── Types.hs │ │ └── Untyped.hs │ ├── Traversals.hs │ └── Type.hs ├── CLI │ └── Main.hs └── Test │ ├── Decompile.hs │ └── Main.hs ├── tests ├── coq │ ├── ERC20 │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── Theory.v │ │ ├── _CoqProject │ │ └── erc20.act │ ├── exponent │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── Theory.v │ │ ├── _CoqProject │ │ ├── exponent.act │ │ └── exponent.sol │ ├── multi │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── Theory.v │ │ ├── _CoqProject │ │ └── multi.act │ ├── safemath │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── Theory.v │ │ ├── _CoqProject │ │ └── safemath.act │ ├── token │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── Theory.v │ │ ├── _CoqProject │ │ └── token.act │ └── transitions │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── Theory.v │ │ ├── _CoqProject │ │ ├── state_machine.act │ │ ├── state_machine.smt2 │ │ └── state_machine.sol ├── frontend │ ├── env │ │ └── all_env_vars.act │ ├── fail │ │ ├── case-fail1.act │ │ ├── case-fail2.act │ │ ├── cast.act │ │ ├── cycle.act │ │ ├── cycle2.act │ │ ├── emptystorage │ │ │ ├── circular.act │ │ │ ├── mapkey.act │ │ │ ├── mapkeyexp.act │ │ │ ├── mapval.act │ │ │ ├── mapvalexp.act │ │ │ ├── var.act │ │ │ └── varexp.act │ │ ├── missingdef0.act │ │ ├── missingdef1.act │ │ ├── missingdef2.act │ │ ├── multipledef │ │ │ ├── multipledef.act │ │ │ └── threedefs.act │ │ ├── three_behaviours.act │ │ ├── timed_calldata.act │ │ ├── two_behaviours.act │ │ ├── two_interfaces.act │ │ ├── typecheck │ │ │ ├── integer_postcond.act │ │ │ ├── mismatched_equality.act │ │ │ └── silly_types.act │ │ └── untimed_entry.act │ └── pass │ │ ├── array │ │ ├── array.act │ │ └── array.act.parsed.hs │ │ ├── case │ │ ├── case.act │ │ ├── case.act.parsed.hs │ │ └── case.act.typed.json │ │ ├── creation │ │ ├── create.act │ │ ├── create.act.parsed.hs │ │ └── create.act.typed.json │ │ ├── dss │ │ ├── vat.act │ │ └── vat.act.parsed.hs │ │ ├── multi │ │ ├── multi.act │ │ ├── multi.act.parsed.hs │ │ └── multi.act.typed.json │ │ ├── negative-literals.act │ │ ├── safemath │ │ ├── safemathraw.act │ │ ├── safemathraw.act.ir.json │ │ ├── safemathraw.act.parsed.hs │ │ ├── safemathraw.act.parsed.hs.custom │ │ └── safemathraw.act.typed.json │ │ ├── smoke │ │ ├── smoke.act │ │ ├── smoke.act.parsed.hs │ │ ├── smoke.act.parsed.hs.custom │ │ └── smoke.act.typed.json │ │ ├── staticstore │ │ ├── staticstore.act │ │ └── staticstore.act.parsed.hs │ │ ├── storageread0.act │ │ ├── storageread1.act │ │ ├── storageread2.act │ │ ├── token │ │ ├── token.sol.json │ │ ├── transfer.act │ │ ├── transfer.act.parsed.hs │ │ └── transfer.act.typed.json │ │ └── two_behaviours_on_two_contracts.act ├── hevm │ ├── fail │ │ ├── multi │ │ │ ├── multi.act │ │ │ └── multi.sol │ │ ├── redundant │ │ │ ├── redundant.act │ │ │ └── redundant.sol │ │ ├── shape-2 │ │ │ ├── shape-2.act │ │ │ └── shape-2.sol │ │ ├── shape │ │ │ ├── shape.act │ │ │ └── shape.sol │ │ ├── simple │ │ │ ├── simple.act │ │ │ └── simple.sol │ │ └── unspecced_storage │ │ │ ├── unspecced_storage.act │ │ │ └── unspecced_storage.sol │ └── pass │ │ ├── amm │ │ ├── amm.act │ │ └── amm.sol │ │ ├── cast-3 │ │ ├── cast-3.act │ │ └── cast-3.sol │ │ ├── cast-4 │ │ ├── cast-4.act │ │ └── cast-4.sol │ │ ├── cast-5 │ │ ├── cast-5.act │ │ └── cast-5.sol │ │ ├── cast-6 │ │ ├── cast-6.act │ │ └── cast-6.sol │ │ ├── cast │ │ ├── cast.act │ │ └── cast.sol │ │ ├── inputs │ │ ├── inputs.act │ │ └── inputs.sol │ │ ├── layout1 │ │ ├── layout1.act │ │ └── layout1.sol │ │ ├── layout2 │ │ ├── layout2.act │ │ └── layout2.sol │ │ ├── layout3 │ │ ├── layout3.act │ │ └── layout3.sol │ │ ├── layout4 │ │ ├── layout4.act │ │ └── layout4.sol │ │ ├── maps │ │ ├── maps.act │ │ └── maps.sol │ │ ├── multi │ │ ├── multi.act │ │ └── multi.sol │ │ ├── multi2 │ │ ├── multi2.act │ │ └── multi2.sol │ │ ├── multi3 │ │ ├── multi3.act │ │ └── multi3.sol │ │ ├── multi4 │ │ ├── multi4.act │ │ └── multi4.sol │ │ ├── safemath │ │ ├── .json │ │ ├── safemath.act │ │ └── safemath.sol │ │ ├── shape │ │ ├── shape.act │ │ └── shape.sol │ │ ├── simple │ │ ├── simple.act │ │ └── simple.sol │ │ ├── state_machine │ │ ├── state_machine.act │ │ └── state_machine.sol │ │ ├── transfer-simple │ │ ├── transfer-simple.act │ │ └── transfer-simple.sol │ │ └── transfer │ │ ├── transfer.act │ │ └── transfer.sol ├── invariants │ ├── fail │ │ ├── all_types.act │ │ ├── concretebase.act │ │ ├── concreteexponent.act │ │ ├── constructor-type-bounds.act │ │ ├── constructor_indirect_assignment.act │ │ ├── constructor_self_reference.act │ │ ├── duplicated_argument_name.act │ │ ├── ethEnv.act │ │ ├── non-inductive.act │ │ └── symbolicexponent.act │ └── pass │ │ ├── all_types.act │ │ ├── amm.act │ │ ├── case.act │ │ ├── concretebase.act │ │ ├── concreteexponent.act │ │ ├── constructor-type-bounds.act │ │ ├── ethEnv.act │ │ ├── homogeneous.act │ │ ├── ite.act │ │ ├── log.act │ │ ├── mapping-assignments.act │ │ ├── mutex.act │ │ ├── state_machine.act │ │ ├── type_constraints.act │ │ └── type_constraints_env.act ├── postconditions │ ├── fail │ │ ├── bad_assignment.act │ │ └── bad_assignment_prestate.act │ └── pass │ │ ├── amm.act │ │ ├── assignment.act │ │ ├── assignment_prestate.act │ │ ├── assignment_return.act │ │ └── increase.act └── safemath │ └── safemath.sol.json └── tools ├── act.py ├── respec └── solidity_to_act.py /.envrc: -------------------------------------------------------------------------------- 1 | # vim: set ft=sh: 2 | use_flake 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: "Build" 2 | on: 3 | pull_request: 4 | push: 5 | jobs: 6 | build: 7 | strategy: 8 | matrix: 9 | os: [ ubuntu-latest, macos-latest ] 10 | # we need this to map platform names as used by github to 11 | # the attribute names defined in release.nix 12 | include: 13 | - os: ubuntu-latest 14 | os_attr: linux 15 | - os: macos-latest 16 | os_attr: darwin 17 | fail-fast: false 18 | runs-on: ${{ matrix.os }} 19 | steps: 20 | - uses: actions/checkout@v2 21 | - uses: cachix/install-nix-action@v22 22 | with: 23 | skip_adding_nixpkgs_channel: false 24 | - uses: cachix/cachix-action@v12 25 | with: 26 | name: dapp 27 | skipPush: true 28 | signingKey: '' 29 | - name: test 30 | run: nix develop --ignore-environment --command bash -c "cabal update && make test-ci" 31 | - name: build 32 | run: nix build -L .#act 33 | -------------------------------------------------------------------------------- /.github/workflows/deploy-book.yaml: -------------------------------------------------------------------------------- 1 | name: Deploy Book 2 | on: 3 | push: 4 | branches: 5 | - master 6 | jobs: 7 | build-and-deploy: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: cachix/install-nix-action@v22 12 | - uses: cachix/cachix-action@v12 13 | with: 14 | name: dapp 15 | 16 | - name: build docs 17 | run: nix develop --ignore-environment --command make docs 18 | 19 | - name: publish docs 20 | uses: JamesIves/github-pages-deploy-action@4.1.4 21 | with: 22 | branch: gh-pages 23 | folder: doc/book 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # generated code 2 | src/Act/Lex.hs 3 | src/Act/Parse.hs 4 | dist-newstyle/ 5 | tests/**/*.out 6 | 7 | # build results 8 | bin/ 9 | result* 10 | 11 | # cabal 12 | *.cabal.local 13 | cabal.project.local* 14 | src/dist/ 15 | 16 | # coq 17 | *.vo 18 | *.vok 19 | *.vos 20 | *.glob 21 | **/CoqMakefile 22 | **/CoqMakefile.conf 23 | **/.CoqMakefile.d 24 | **/.lia.cache 25 | *.aux 26 | **/.coqdeps.d 27 | 28 | # mdbook 29 | doc/book 30 | 31 | # direnv 32 | .direnv/ 33 | 34 | *~ 35 | *# -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/act/287d6c99e970dc7b037330bc998ab734eb6796e7/.gitmodules -------------------------------------------------------------------------------- /.hlint.yaml: -------------------------------------------------------------------------------- 1 | - ignore: {name: "Use fewer imports"} 2 | - ignore: {name: "Use <$>"} 3 | - ignore: {name: "Use if"} 4 | - ignore: {name: "Redundant bracket"} 5 | - ignore: {name: "Reduce duplication"} 6 | - ignore: {name: "Redundant return"} 7 | - ignore: {name: "Use head"} 8 | - ignore: {name: "Use unwords"} 9 | - ignore: {name: "Use <$>"} 10 | - ignore: {name: "Use elem"} 11 | - ignore: {name: "Use infix"} 12 | - ignore: {name: "Use section"} 13 | - ignore: {name: "Move brackets to avoid $"} 14 | - ignore: {name: "Use zipWith"} 15 | - ignore: {name: "Redundant flip"} 16 | - ignore: {name: "Redundant $!"} 17 | - ignore: {name: "Redundant <$>"} 18 | - ignore: {name: "Eta reduce"} 19 | - ignore: {name: "Use underscore"} 20 | - ignore: {name: "Functor law"} 21 | - ignore: {name: "Use record patterns"} 22 | - error: {lhs: return, rhs: pure} 23 | 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := compiler 2 | .PHONY: parser compiler docs 3 | 4 | parser: src/Act/Lex.hs src/Act/Parse.hs 5 | 6 | src/Act/Parse.hs: src/Act/Parse.y src/Act/Syntax/Untyped.hs 7 | happy src/Act/Parse.y 8 | 9 | src/Act/Lex.hs: src/Act/Lex.x 10 | alex src/Act/Lex.x 11 | 12 | # builds the rest of the haskell files (compiler) 13 | bin/act: src/CLI/*.hs src/Act/*.hs 14 | cabal v2-install --installdir=bin --overwrite-policy=always 15 | 16 | repl: src/Act/Lex.hs src/Act/Parse.hs 17 | cabal v2-repl 18 | 19 | compiler: bin/act 20 | 21 | docs: 22 | cd doc && mdbook build 23 | 24 | docs-serve: 25 | cd doc && mdbook serve 26 | 27 | frontend_pass=$(wildcard tests/frontend/pass/*/*.act) 28 | frontend_fail=$(wildcard tests/frontend/fail/*/*.act) 29 | 30 | parser_pass=$(frontend_pass) 31 | typing_pass=$(filter-out $(failing_typing), $(parser_pass)) 32 | # supposed to fail 33 | typing_fail=$(frontend_fail) 34 | parser_fail=$(filter-out $(typing_fail), $(frontend_fail)) 35 | 36 | invariant_specs=$(wildcard tests/invariants/*/*.act) 37 | invariant_pass=$(filter-out $(invariant_buggy), $(wildcard tests/invariants/pass/*.act) $(typing_pass)) 38 | invariant_fail=$(wildcard tests/invariants/fail/*.act) 39 | 40 | postcondition_specs=$(wildcard tests/postconditions/*/*.act) 41 | postcondition_pass=$(wildcard tests/postconditions/pass/*.act) $(typing_pass) 42 | postcondition_fail=$(wildcard tests/postconditions/fail/*.act) 43 | 44 | # supposed to pass, but timeout 45 | hevm_buggy=tests/hevm/pass/transfer/transfer.act 46 | # supposed to pass 47 | hevm_pass=$(filter-out $(hevm_buggy), $(wildcard tests/hevm/pass/*/*.act)) 48 | # supposed to fail 49 | hevm_fail=$(wildcard tests/hevm/fail/*/*.act) 50 | # supposed to pass 51 | hevm_slow=tests/hevm/pass/amm/amm.act tests/hevm/pass/amm-2/amm-2.act 52 | # supposed to pass, no slow tests 53 | hevm_fast=$(filter-out $(hevm_slow), $(hevm_pass)) 54 | 55 | # supposed to pass 56 | failing_typing=tests/frontend/pass/array/array.act tests/frontend/pass/dss/vat.act tests/frontend/pass/creation/createMultiple.act tests/frontend/pass/staticstore/staticstore.act 57 | 58 | 59 | coq-examples = tests/coq/transitions tests/coq/safemath tests/coq/exponent tests/coq/token tests/coq/ERC20 tests/coq/multi 60 | 61 | .PHONY: test-coq $(coq-examples) 62 | test-coq: compiler $(coq-examples) 63 | $(coq-examples): 64 | make -C $@ clean 65 | make -C $@ 66 | 67 | test-parse: parser compiler $(parser_pass:=.parse.pass) $(parser_fail:=.parse.fail) 68 | test-type: parser compiler $(typing_pass:=.type.pass) $(typing_fail:=.type.fail) 69 | test-invariant: parser compiler $(invariant_pass:=.invariant.pass) $(invariant_fail:=.invariant.fail) 70 | test-postcondition: parser compiler $(postcondition_pass:=.postcondition.pass) $(postcondition_fail:=.postcondition.fail) 71 | test-hevm: parser compiler $(hevm_pass:=.hevm.pass) $(hevm_fail:=.hevm.fail) 72 | test-hevm-fast: parser compiler $(hevm_fast:=.hevm.pass.fast) $(hevm_fail:=.hevm.fail) 73 | test-cabal: src/*.hs 74 | cabal v2-run test 75 | 76 | # Just checks parsing 77 | tests/%.parse.pass: 78 | ./bin/act parse --file tests/$* > tests/$*.parsed.hs.out 79 | diff tests/$*.parsed.hs.out tests/$*.parsed.hs 80 | rm tests/$*.parsed.hs.out 81 | 82 | tests/%.parse.fail: 83 | ./bin/act parse --file tests/$* && exit 1 || echo 0 84 | 85 | tests/%.type.pass: 86 | ./bin/act type --file tests/$* | jq . > tests/$*.typed.json.out 87 | diff tests/$*.typed.json.out tests/$*.typed.json 88 | rm tests/$*.typed.json.out 89 | 90 | tests/%.type.fail: 91 | ./bin/act type --file tests/$* && exit 1 || echo 0 92 | 93 | tests/%.invariant.pass: 94 | ./bin/act prove --file tests/$* 95 | ./bin/act prove --solver cvc5 --file tests/$* 96 | 97 | tests/%.invariant.fail: 98 | ./bin/act prove --file tests/$* && exit 1 || echo 0 99 | ./bin/act prove --solver cvc5 --file tests/$* && exit 1 || echo 0 100 | 101 | tests/%.postcondition.pass: 102 | ./bin/act prove --file tests/$* 103 | ./bin/act prove --solver cvc5 --file tests/$* 104 | 105 | tests/%.postcondition.fail: 106 | ./bin/act prove --file tests/$* && exit 1 || echo 0 107 | ./bin/act prove --solver cvc5 --file tests/$* && exit 1 || echo 0 108 | 109 | tests/hevm/pass/%.act.hevm.pass: 110 | $(eval CONTRACT := $(shell awk '/contract/{ print $$2 }' tests/hevm/pass/$*.sol)) 111 | ./bin/act hevm --spec tests/hevm/pass/$*.act --sol tests/hevm/pass/$*.sol --smttimeout 100000000 112 | 113 | tests/hevm/fail/%.act.hevm.fail: 114 | $(eval CONTRACT := $(shell awk '/contract/{ print $$2 }' tests/hevm/fail/$*.sol)) 115 | ./bin/act hevm --spec tests/hevm/fail/$*.act --sol tests/hevm/fail/$*.sol && exit 1 || echo 0 116 | 117 | tests/hevm/pass/%.act.hevm.pass.fast: 118 | $(eval CONTRACT := $(shell awk '/contract/{ print $$2 }' tests/hevm/pass/$*.sol)) 119 | ./bin/act hevm --spec tests/hevm/pass/$*.act --sol tests/hevm/pass/$*.sol --smttimeout 100000000 120 | 121 | test-ci: test-parse test-type test-invariant test-postcondition test-coq test-hevm 122 | test: test-ci test-cabal 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Act 2 | 3 | Act is a formal specification language, designed to allow for the construction of an exhaustive, 4 | mathematically rigorous description of evm programs. Act allows diverse toolchains to interoperate 5 | on a single specification, with each generating and exchanging different kinds of knowledge. It has 6 | a built-in analysis engine that can automatically prove properties about the specification itself, 7 | as well as an integrated symbolic execution engine (based on hevm) that can prove equivalence 8 | between a specification and a given bytecode object. Finally, specifications can be exported into 9 | higher level reasoning tools (e.g. theorem provers, economic analysis tooling), allowing for the 10 | verification of properties of almost arbitrary complexity, all with a proof chain right down to the 11 | bytecode level. 12 | 13 | It extends on the previous [Act](https://github.com/dapphub/klab/blob/master/acts.md) project. 14 | 15 | More in depth documentation can be found in [The Act Book](https://ethereum.github.io/act/). 16 | 17 | # Building 18 | 19 | With nix: 20 | 21 | ```sh 22 | nix build 23 | ``` 24 | 25 | # Developing 26 | 27 | Enter a nix-shell to get the dependencies of the project: 28 | 29 | ```sh 30 | nix develop 31 | ``` 32 | 33 | you can then use `cabal` as normal: 34 | 35 | ```sh 36 | cd src 37 | cabal build # build 38 | cabal repl # enter a repl instance 39 | ``` 40 | 41 | to execute the unit tests: 42 | 43 | ```sh 44 | make test # run all tests 45 | cd src && cabal v2-test # run haskell tests 46 | ``` 47 | 48 | To update the project dependencies run: 49 | 50 | ```sh 51 | nix flake update 52 | ``` 53 | -------------------------------------------------------------------------------- /act.cabal: -------------------------------------------------------------------------------- 1 | cabal-version: 2.2 2 | 3 | name: act 4 | version: 0.1.0.0 5 | author: Martin Lundfall 6 | maintainer: martin.lundfall@protonmail.com 7 | 8 | flag ci 9 | description: Sets flags for compilation in CI 10 | default: False 11 | manual: True 12 | 13 | common common 14 | default-extensions: 15 | ApplicativeDo 16 | ImportQualifiedPost 17 | LambdaCase 18 | OverloadedLabels 19 | DataKinds 20 | GADTs 21 | build-depends: base >= 4.9 && < 5, 22 | aeson >= 1.0, 23 | containers >= 0.5, 24 | hevm >= 0.51.3, 25 | lens >= 4.17.1, 26 | text >= 1.2, 27 | array >= 0.5.3.0, 28 | optparse-generic >= 1.3, 29 | vector >= 0.12.0.3, 30 | bytestring >= 0.10.8, 31 | mtl >= 2.2.2, 32 | utf8-string >= 1.0.1.1, 33 | process >= 1.6.5.0, 34 | ansi-wl-pprint >= 0.6.9, 35 | regex-tdfa, 36 | validation >= 1.1.1, 37 | ordered-containers >= 0.2.2, 38 | extra, 39 | singletons, 40 | deriving-compat, 41 | async >= 2.2.4, 42 | data-dword >= 0.3.2.1, 43 | prettyprinter, 44 | prettyprinter-ansi-terminal, 45 | if flag(ci) 46 | ghc-options: -O2 -Wall -Werror -Wno-orphans -Wno-unticked-promoted-constructors 47 | else 48 | ghc-options: -Wall -Wno-orphans -Wno-unticked-promoted-constructors 49 | 50 | library 51 | import: common 52 | build-tool-depends: happy:happy, alex:alex 53 | hs-source-dirs: src 54 | default-language: Haskell2010 55 | exposed-modules: 56 | Act.CLI 57 | Act.Decompile 58 | Act.Error 59 | Act.Print 60 | Act.SMT 61 | Act.Syntax.Annotated 62 | Act.Syntax.TimeAgnostic 63 | Act.Lex 64 | Act.Parse 65 | Act.Coq 66 | Act.Syntax 67 | Act.Syntax.Untyped 68 | Act.Syntax.Typed 69 | Act.Syntax.Types 70 | Act.Syntax.Timing 71 | Act.Traversals 72 | Act.Type 73 | Act.Enrich 74 | Act.Dev 75 | Act.HEVM 76 | Act.HEVM_utils 77 | Act.Consistency 78 | 79 | executable act 80 | import: common 81 | main-is: Main.hs 82 | hs-source-dirs: src/CLI 83 | default-language: Haskell2010 84 | build-depends: act 85 | if os(darwin) 86 | extra-libraries: c++ 87 | 88 | Test-Suite test 89 | import: common 90 | type: exitcode-stdio-1.0 91 | default-language: Haskell2010 92 | main-is: Main.hs 93 | hs-source-dirs: src/Test 94 | other-modules: Decompile 95 | build-depends: act, 96 | pretty-simple >= 2.2, 97 | quickcheck-instances >= 0.3, 98 | quickcheck-transformer >= 0.3, 99 | tasty-hunit >= 0.10, 100 | tasty-expected-failure, 101 | tasty-quickcheck >= 0.10, 102 | QuickCheck >= 2.13.2, 103 | tasty >= 1.2, 104 | here >= 1.2 105 | if os(darwin) 106 | extra-libraries: c++ 107 | -------------------------------------------------------------------------------- /doc/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["dxo"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "The Act Book" 7 | 8 | [preprocessor] 9 | [preprocessor.mermaid] 10 | command = "mdbook-mermaid" 11 | [preprocessor.katex] 12 | after = ["links"] 13 | 14 | [output] 15 | [output.html] 16 | additional-js =["mermaid.min.js", "mermaid-init.js"] 17 | -------------------------------------------------------------------------------- /doc/mermaid-init.js: -------------------------------------------------------------------------------- 1 | mermaid.initialize({startOnLoad:true}); 2 | -------------------------------------------------------------------------------- /doc/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | [Introduction](./introduction.md) 4 | 5 | - [Language Reference](./language.md) 6 | - [Backends](./backends.md) 7 | - [SMT](./smt.md) 8 | - [Coq](./coq.md) 9 | - [Hevm](./hevm.md) 10 | - [Internals](./internals.md) 11 | - [HEVM Equivalence](./equiv.md) 12 | -------------------------------------------------------------------------------- /doc/src/backends.md: -------------------------------------------------------------------------------- 1 | # Backends 2 | 3 | Act currently implements 3 backends natively: 4 | 5 | - [`SMT`](./smt.md): automated proof of postcondition and inductive invariants against the spec 6 | - [`Coq`](./coq.md): manual proof of arbitrary properties against the spec 7 | - [`Hevm`](./hevm.md): automated equivalence proof between the spec and an implementation in evm 8 | -------------------------------------------------------------------------------- /doc/src/equiv.md: -------------------------------------------------------------------------------- 1 | # Checking equivalence with EVM bytecode 2 | 3 | EVM bytecode can be formally verified to implement an Act spec. This 4 | means that each successful behavior of the bytecode should be covered 5 | by the Act spect. To check equivalence the Act spec is translated to 6 | Expr, the intermediate representation of HEVM, and the EVM bytecode is 7 | symbolically executed to obtain its Expr representation. Then 8 | equivalence can be checked with the equivalence checker of HEVM. 9 | 10 | The Expr representation of an Act program is a list of *Success* 11 | nodes, that contain the possible successful results of the 12 | computation. The Expr representation of the EVM bytecode can also be 13 | flattened to a list of result nodes from which we only keep the 14 | successful executions, filtering out failed and partial execution 15 | paths. An informative warning will be thrown when partial executions 16 | are encountered. 17 | 18 | A success node in Expr, `Success cond res storage`, is a leaf in the 19 | Expr tree representation and contains the path conditions, `cond` that 20 | lead to the leaf, the result buffer `res`, and the end state 21 | `storage`. 22 | 23 | 24 | ## Equivalence checks 25 | To check equivalence between the two Expr representations the 26 | following checks are performed. 27 | 28 | ### Result equivalence 29 | The two list of `Success` nodes are checked for equivalence using 30 | the HEVM equivalence checker. For each pair of nodes in the two lists, 31 | we check that for all inputs that satisfy the combined path conditions the 32 | result and final storage the same. 33 | 34 | ### Input space equivalence 35 | Since the input space of the two lists is not necessarily exhaustive, 36 | some inputs may lead to failed execution paths that are not 37 | present in the list. We therefore need to check that the input space of the two 38 | lists are the same. That is, there must not be inputs that satisfy 39 | some path condition in the first list but not the second and vice verse. 40 | 41 | Say that the Act program has the Expr representation 42 | `[Success c1 r1 s1, ..., Success cn rn sn` 43 | and the the EVM bytecode has the Expr representation 44 | `[Success c1' r1' s1', ..., Success cn' rn' sn'` 45 | 46 | then we need to check that `c1 \/ .. \/ cn <-> c1' \/ .. \/ cn'` that 47 | is, we require that `c1 \/ .. \/ cn /\ ~ (c1' \/ .. \/ cn')` and `c1' 48 | \/ .. \/ cn' /\ ~ (c1 \/ .. \/ cn)` are both unsatisfiable. 49 | 50 | ### Exhaustiveness checks for bytecode 51 | TODO 52 | -------------------------------------------------------------------------------- /doc/src/hevm.md: -------------------------------------------------------------------------------- 1 | # Hevm 2 | 3 | Act leverages the symbolic execution engine in hevm to provide a backend that can prove equivalence 4 | between a contract specification and an implementation of that specification in EVM. 5 | 6 | Two claims are generated for each behaviour, Pass and Fail. The Pass claim states that if all 7 | preconditions in the iff block are true, then all executions will succeed, storage will be updated 8 | according to the storage block, and the specified return value will, in fact, be returned. The Fail 9 | claim states that should any of the preconditions be false, all executions will revert. 10 | 11 | In both cases we begin the proof by constraining calldata to be of the form specified in the 12 | behaviours’ interface blocks, as well as making the relevant assumptions depending on whether the 13 | claim is Pass or Fail, and then symbolically executing the bytecode object with storage held to be 14 | completely abstract. 15 | 16 | This produces a tree of potential executions where each node represents a potential branching point, 17 | and each leaf represents a possible final state of the contract after the execution of a single 18 | call. 19 | 20 | In the case of a Fail claim, we can then check that each leaf represents a state in which execution 21 | has reverted, while for a Pass claim we can check that storage has been updated as expected, and 22 | that the contents of the return buffer matches what was specified in the behaviour’s returns block. 23 | 24 | As an example, consider the following contract: 25 | 26 | ``` 27 | contract Simple { 28 | uint val; 29 | 30 | function set(uint x) external payable returns (uint) { 31 | require(x > 100); 32 | val = x; 33 | return x; 34 | } 35 | } 36 | ``` 37 | 38 | We can represent this in Act as: 39 | 40 | ``` 41 | constructor of Simple 42 | interface constructor() 43 | 44 | creates 45 | 46 | uint val := 0 47 | behaviour set of Simple 48 | interface set(uint x) 49 | 50 | iff 51 | 52 | x > 100 53 | 54 | storage 55 | 56 | val => x 57 | 58 | returns x 59 | ``` 60 | 61 | Act needs to have access to the storage layout metadata output by solc to compute the index in storage for each variable mentioned in the spec, so we need to pass a solc output json when trying to prove equivalence. 62 | 63 | ``` 64 | > act hevm --spec src/simple.act --soljson out/dapp.sol.json 65 | checking postcondition... 66 | Q.E.D. 67 | Successfully proved set(Pass), 2 cases. 68 | checking postcondition... 69 | Q.E.D. 70 | Successfully proved set(Fail), 2 cases. 71 | ==== SUCCESS ==== 72 | All behaviours implemented as specified ∎. 73 | ``` 74 | 75 | If we try to prove equivalence of the spec and a faulty implementation like the one below: 76 | 77 | ```solidity 78 | contract Simple { 79 | uint val; 80 | 81 | function set(uint x) external payable returns (uint) { 82 | require(x > 100); 83 | if (x == 2000) { 84 | val = x + 1; 85 | } else { 86 | val = x; 87 | } 88 | return x; 89 | } 90 | } 91 | ``` 92 | 93 | Then Act will give us a counterexample showing a case where the implementation differs from the specification: 94 | 95 | ``` 96 | > act hevm --spec src/simple.act --soljson out/dapp.sol.json 97 | checking postcondition... 98 | Calldata: 99 | 0x60fe47b100000000000000000000000000000000000000000000000000000000000007d0 100 | Caller: 101 | 0x0000000000000000000000000000000000000000 102 | Callvalue: 103 | 0 104 | Failed to prove set(Pass) 105 | checking postcondition... 106 | Q.E.D. 107 | Successfully proved set(Fail), 2 cases. 108 | ==== FAILURE ==== 109 | 1 out of 2 claims unproven: 110 | 111 | 1 Failed to prove set(Pass) 112 | ``` 113 | -------------------------------------------------------------------------------- /doc/src/internals.md: -------------------------------------------------------------------------------- 1 | # Internals 2 | 3 | ## The Act AST Type 4 | 5 | All act specs are transformed into typed AST instances. The definition of this AST type can be found 6 | in [Syntax/TimeAgnostic.hs](https://github.com/ethereum/act/blob/master/src/Syntax/TimeAgnostic.hs). 7 | 8 | The top level datatype here is a `Claim`, and the output of the frontend states is a list of 9 | (`Timed`) claims. A `Claim` is a sum type (enum) with four potential values: 10 | 11 | - `C`: constructor 12 | - `B`: behaviour 13 | - `I`: invariant 14 | - `S`: typing information for storage variables 15 | 16 | ### Pass and Fail Claims 17 | 18 | Act specs are intended to be fully exhaustive, for this reason we generate two claims from each 19 | constructor / behaviour in the spec: 20 | 21 | 1. `Pass`: The Pass claim states that if all preconditions in the iff block are true, then all executions will succeed, storage will be updated according to the storage block, and the specified return value will, in fact, be returned. 22 | 1. `Fail`: The Fail claim states that should any of the preconditions be false, all executions will revert. 23 | 24 | Taken together a succesfull proof of these claims ensures that a given bytecode object implements 25 | only the behaviour specified in act *and nothing else*. 26 | 27 | ### Timed vs Untimed Instances 28 | 29 | AST instances can be either `Timed` (explicitly timed storage references) or `Untimed` (implicitly 30 | timed storage references), and this distinction is reflected at the type level (the `t` parameter in 31 | most of the data type definitions). 32 | 33 | In some parts of an act spec (`returns` and `ensures` blocks), storage references must be qualified 34 | with either the `pre` or `post` operator to disambiguate references to storage in the pre or post 35 | state. In other blocks storage references are either implicitly refering to both pre and post states 36 | (`invariants`), or to the pre state only (e.g. `storage`). In order to simplify implementation in 37 | the various backends, there is a frontend stage (`annotate`) that makes all implicit timings 38 | explicit. The type parameter allows us to enforce the invariant that all backend stages always 39 | operate on `Timed` AST instances. 40 | 41 | ## Compilation Pipeline 42 | 43 | The act compilation pipeline is as follows: 44 | 45 | ```mermaid 46 | flowchart LR; 47 | Source --> Lexer 48 | Lexer --> Parser; 49 | Parser --> Type[Type Checker]; 50 | Type --> Annotate; 51 | Annotate --> Enrich; 52 | Enrich --> SMT; 53 | Enrich --> Coq; 54 | Enrich --> Hevm; 55 | Enrich --> K; 56 | ``` 57 | 58 | ### Lexer 59 | 60 | Converts the source code into a sequence of tokens that can be consumed by the parser. We use 61 | [alex](https://www.haskell.org/alex/) to generate a lexer from the specification in 62 | [Lex.x](https://github.com/ethereum/act/blob/master/src/Lex.x). 63 | 64 | ### Parser 65 | 66 | Parses a sequence of tokens into an untyped AST. We use [happy](https://www.haskell.org/happy/) to 67 | generate a parser from the specification in 68 | [Parse.y](https://github.com/ethereum/act/blob/master/src/Parse.y). The untyped AST is defined in 69 | [Syntax/Untyped.hs](https://github.com/ethereum/act/blob/master/src/Syntax/Untyped.hs). 70 | 71 | ### Type Checker 72 | 73 | The type checker takes an untyped AST, checks that none of the typing rules are violated, and 74 | produces a typed AST. The typechecker is defined in 75 | [Type.hs](https://github.com/ethereum/act/blob/master/src/Type.hs) and the core typed AST definition 76 | is in 77 | [Syntax/TimeAgnostic.hs](https://github.com/ethereum/act/blob/master/src/Syntax/TimeAgnostic.hs). 78 | The meaning of the timing parameter to the AST is described below in 79 | [Timed vs Untimed AST instances](#timed-vs-untimed-ast-instances). 80 | 81 | ### Annotation 82 | 83 | The annotate stage makes any implicit timings explicit. This stage is implemented in 84 | [Syntax/Annotated.hs](https://github.com/ethereum/act/blob/master/src/Syntax/Annotated.hs). 85 | 86 | ### Enrichment 87 | 88 | The enrich stage adds preconditions to all behaviour / constructor / invariant instances based on 89 | the types of all variable references. This staged is implemented in 90 | [Enrich.hs](https://github.com/ethereum/act/blob/master/src/Enrich.hs). 91 | 92 | ## Error Handling 93 | 94 | Stages that need to present errors to the user should return an instance of the `Error` type defined 95 | in [Error.hs](https://github.com/ethereum/act/blob/master/src/Error.hs). This is a modified instance 96 | of [Data.Validation](https://hackage.haskell.org/package/validation-1.1.2) specialised to the act 97 | context. This type allows us to accumulate multiple error messsages (useful if e.g. multiple syntax 98 | errors are present), and we also have unified routines to pretty print these messages. 99 | 100 | -------------------------------------------------------------------------------- /doc/src/introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | Act is a high level specification language for evm programs. The core aim is to allow for easy 4 | refinement. We want to make it as easy as possible for development teams to define a high level 5 | specification, which can then either be used "upwards" to prove higher level properties or 6 | "downwards" to demonstrate that an implementation in EVM bytecode conforms to the spec. 7 | 8 | Act currently integrates with the following tools: 9 | 10 | - Hevm: automated refinement proof between the act spec and a given evm bytecode object 11 | - SMT: automated proof of invariants and postconditions 12 | - Coq: manual proof of high level properties against a model derived from the Act spec 13 | 14 | ## Overview 15 | 16 | At a very high level Act is a kind of mathy english over the EVM, where contracts are defined as a 17 | set of pure functions taking a given EVM state (i.e. storage & blockchain context) and some calldata 18 | and producing a new EVM state (`(EVM, Calldata) -> EVM`). 19 | 20 | A specification of a contract written in Act consists of a constructor and a set of behaviours: 21 | 22 | The constructor specification defines the structure of the contract’s state, the initial value of 23 | the state, and a list of invariants that the contract should satisfy. 24 | 25 | Each behaviour specification determines how a contract method updates the state, the method’s return 26 | value (if any), and any conditions that must be satisfied in order for the state update to be 27 | applied. 28 | 29 | Alternatively, they can be thought of as an initial state and a set of state transitions, 30 | determining an inductively defined state transition system. 31 | 32 | ## Types 33 | 34 | The types of Act consist of three basic primitives: Integers, Booleans and Bytestrings. Integers are 35 | unbounded, with true integer operations. However, as our integer expressions will often represent 36 | words in the EVM, we allow ourselves a slight abuse of notation and denote by uintN/intN integers 37 | together with the constraint that the value fits into a uintN/intN. If any operation could over- or 38 | underflow this bound, the constraint will be unfulfilled and the specification will fail to prove. 39 | 40 | Using conventional ABI types for typing also allows us to specify function signatures in a concise 41 | way. As an example, consider this specification of a trivial contract that adds two numbers and 42 | stores the result on chain: 43 | 44 | ``` 45 | constructor of Add 46 | interface constructor() 47 | 48 | creates 49 | 50 | uint result := 0 51 | 52 | behaviour add of Add 53 | interface add(uint x, uint y) 54 | 55 | iff in range uint 56 | 57 | x + y 58 | 59 | storage 60 | 61 | result => x + y 62 | 63 | returns x + y 64 | ``` 65 | 66 | Expressed in English and math, this specification would read: 67 | 68 | The contract `Add` has a single state variable, named `result`, which is an integer such that `0 <= result < 2^256`. 69 | 70 | Given any pair of integers `x` and `y`, s.t. `0 <= x < 2^256` and `0 <= y < 2^256`, an ABI encoded call to the contract `Add` with the signature `add(uint256,uint256)`, with its respective arguments named `x` and `y`, will: 71 | 72 | - store `x + y` in `result` and return `x + y` if `0 <= x + y < 2^256` 73 | - revert otherwise 74 | 75 | -------------------------------------------------------------------------------- /examples/binarySearch/binarySearch.act: -------------------------------------------------------------------------------- 1 | behaviour init of BinarySearch 2 | interface constructor() 3 | 4 | creates 5 | uint256[] array := [] 6 | 7 | invariants 8 | sorted(array) 9 | 10 | where 11 | 12 | sorted : uint256[] -> bool 13 | sorted([]) := true 14 | sorted([x]) := true 15 | sorted(x::y::xs) := x <= y and sorted(y::xs) 16 | 17 | 18 | // Returns the index of elem if elem in array, 19 | // otherwise returns -1. 20 | behaviour search of BinarySearch 21 | interface search(uint256 elem) 22 | 23 | returns binarySearch(array, elem, 0, array.length) 24 | 25 | ensures 26 | 27 | if elem in array 28 | then array[index] == elem 29 | else index == -1 30 | 31 | where 32 | 33 | index := binarySearch(array, elem, 0, array.length) 34 | 35 | binarySearch : uint256[] -> uint256 -> uint256 -> uint256 -> uint256 36 | binarySearch(uint256[] a, uint256 elem, uint256 left, uint256 right) := 37 | if left >= right 38 | then -1 39 | else 40 | if a[(right + left) / 2] == elem 41 | then (right + left) / 2 42 | else 43 | if a[(right + left) / 2] < elem 44 | then binarySearch(a, elem, (right + left) / 2 + 1, right) 45 | else binarySearch(a, elem, left, (right + left) / 2 - 1) 46 | -------------------------------------------------------------------------------- /examples/contractCreation/modest.act: -------------------------------------------------------------------------------- 1 | // A modest example specifying the creation of a new contract, 2 | // in this case the contract subject at question. 3 | // Specifying the creation of a contract serves multiple purposes: 4 | // from a formal semantics perspective, it defines the base case 5 | // for the inductive system that is a smart contract. From a syntactic 6 | // perspective, it defines the storage layout of the contract, by explicit 7 | // typing of storage variables. 8 | 9 | behaviour creation of Modest 10 | interface constructor() 11 | 12 | creates Modest 13 | // storage variables must be given an explicit initial value 14 | // if they are not assigned a value in the constructor code, 15 | // this is 0. TODO (is this annoying?) 16 | uint x := 1 17 | address y := CALLER 18 | 19 | -------------------------------------------------------------------------------- /examples/contractCreation/modest.sol: -------------------------------------------------------------------------------- 1 | contract Modest { 2 | uint x = 1; 3 | address owner; 4 | constructor() { 5 | owner = msg.sender; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /examples/deposit/deposit-spec.md: -------------------------------------------------------------------------------- 1 | # Background 2 | 3 | This is a specification of the [Eth2.0 deposit contract](https://github.com/ethereum/eth2.0-specs/blob/dev/deposit_contract/contracts/validator_registration.v.py), based on previous work by Daejun Park. 4 | 5 | The contract allows for participants to lock up at least 32 ether in order to become a validator on the eth2.0 chain. Their deposits are fitted into a sparse merkle tree allowing efficient inclusion proofs to be submitted to the beacon chain. In order to optimize storage and runtime efficiency, the contract implements a so called "incremental merkle tree algorithm" which only stores the "frontier" of the merkle tree, pictured as red nodes in the figure below. 6 | ![Incremental merkle tree](https://github.com/ethereum/smart-contract-spec-lang/tree/bnfc/examples/deposit/inc_merkle_tree.png "Incremental merkle tree") 7 | This provides sufficient information to calculate the root hash of the tree. In order to reconstruct the full tree, it is expected that nodes rely on calldata from previous transactions, or the events emitted by the contract. 8 | 9 | # Specification 10 | 11 | The two main functions of the contract are `deposit` and `get_deposit_root`. 12 | 13 | ## Deposit 14 | ```act 15 | behaviour deposit of ValidatorRegistration 16 | interface deposit(bytes[48] pubkey, bytes[32] withdrawal_credentials, bytes[96] signature, bytes32 deposit_data_root) 17 | 18 | storage 19 | 20 | branch[index_of_first_one(deposit_count, 0)] => sha256 21 | sha256( 22 | sha256(pubkey ++ "0x00000000000000000000000000000000") ++ withdrawal_credentials) 23 | ++ sha256(to_bytes(CALLVALUE / 10^9, 64) ++ sha256( 24 | sha256(signature[0..64]) ++ sha256(signature[65..96] ++ "0x0000000000000000000000000000000000000000000000000000000000000000"))) 25 | ) 26 | deposit_count => deposit_count + 1 27 | 28 | where 29 | 30 | index_of_first_one(N, M) = if N % 2 == 1 then M else index_of_first_one(N / 2, M + 1) 31 | 32 | iff in range uint32 33 | 34 | depost_count + 1 35 | ``` 36 | 37 | ## Get deposit root 38 | 39 | ```act 40 | behaviour get_deposit_root of ValidatorRegistration 41 | interface get_deposit_root() 42 | 43 | returns sha256(hashLoR(branch, zero_hashes, deposit_count) 44 | ++ to_bytes(deposit_count, 64) ++ "0x000000000000000000000000000000000000000000000000") 45 | 46 | where 47 | 48 | hashLoR(frontier, zs, count) = foldr (\(i, (br, z)) acc -> if deposit_count / 2^i % 2 == 1 then sha256(br, acc) else sha256(acc, z) $ zip [1..] $ zip zeros br 49 | ``` 50 | 51 | # Properties 52 | 53 | The incremental merkle algorithm implemented by the functions `deposit` and `get_deposit_root` should be equivalent to a "naïve algorithm", which simply inserts deposits into the tree, storing every leaf node and hashes them to get the root. 54 | We can express this property as claim about a series of consecutive calls to deposit. 55 | 56 | In other words, given any finite list of calls, `[C_0, C_1, ... C_n]` to the contract, for every element whose calldata matches the function signature of the `deposit` function, if we put the argument of those calls in a sparse merkle tree, the root of that tree should be the same as what we would get from calling the `get_deposit_root` function. 57 | 58 | More formally, our claim is the following. 59 | 60 | `Theorem (Incremental updating)` 61 | 62 | For every list of calls `C = [C_0, C_1, ... C_n]` applied to the contracts initial state, let the sublist `D = [D | length(C.calldata) == 4 + 32 + 96 + 32 and C.calldata[0..4] == keccak256("deposit(bytes,bytes,bytes, bytes32)")]` consist of the valid calls to the `deposit` function. Then the partial merkle tree whose nodes up to `n` are given by `DepositData_root(D_i.calldata[4..])` has the same root as the result of a call to `get_deposit_root()`. 63 | 64 | `Proof:` 65 | Since the only calls that modify state are valid calls to `deposit`, it suffices to consider `D` to get the contract state after `C`. What remains to show is that the incremental merkle tree algorithm yields the same result as the root of a partial merkle tree. This has been demonstrated by Daejun in [https://github.com/runtimeverification/verified-smart-contracts/blob/deposit/deposit/formal-incremental-merkle-tree-algorithm.pdf]. 66 | 67 | Formal statement concept art: 68 | 69 | ```act 70 | property inc_merkle of deposit 71 | 72 | for all 73 | 74 | x : uint 75 | A : bytes[208][2^x] 76 | 77 | have 78 | 79 | merkle_root(A) == get_deposit_root(apply([deposit(a) | a in A], initial_state)) 80 | 81 | where 82 | 83 | merkle_root(A) := ... 84 | ``` 85 | 86 | In the above, assume that `apply` is an act keyword. An alternative is to make every behaviour result in a stateful function, in which case `apply` is essentially a `fold`. 87 | -------------------------------------------------------------------------------- /examples/deposit/inc_merkle_tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/act/287d6c99e970dc7b037330bc998ab734eb6796e7/examples/deposit/inc_merkle_tree.png -------------------------------------------------------------------------------- /examples/homogeneous/homogeneous.act: -------------------------------------------------------------------------------- 1 | behaviour init of Homogeneous 2 | interface constructor() 3 | 4 | creates 5 | uint256 x := 3 6 | uint256 y := 5 7 | uint256 z := 15 8 | 9 | invariants 10 | x * y == z 11 | 12 | 13 | behaviour f of Homogeneous 14 | interface f(uint256 scalar) 15 | 16 | iff 17 | x * scalar > x 18 | z * scalar > z 19 | 20 | storage 21 | x => x * scalar 22 | z => z * scalar 23 | 24 | 25 | behaviour g of Homogeneous 26 | interface g(uint256 scalar) 27 | 28 | iff 29 | y * scalar > y 30 | z * scalar > z 31 | 32 | storage 33 | y => y * scalar 34 | z => z * scalar 35 | -------------------------------------------------------------------------------- /examples/homogeneous/homogeneous.smt2: -------------------------------------------------------------------------------- 1 | (define-fun f ((x_pre Int) (y_pre Int) (z_pre Int) (scalar Int) (x_post Int) (y_post Int) (z_post Int)) Bool 2 | (and 3 | (>= scalar 1) 4 | (= x_post (* x_pre scalar)) 5 | (= z_post (* z_pre scalar)) 6 | (= y_pre y_post) 7 | ) 8 | ) 9 | 10 | (define-fun g ((x_pre Int) (y_pre Int) (z_pre Int) (scalar Int) (x_post Int) (y_post Int) (z_post Int)) Bool 11 | (and 12 | (>= scalar 1) 13 | (= y_post (* y_pre scalar)) 14 | (= z_post (* z_pre scalar)) 15 | (= x_pre x_post) 16 | ) 17 | ) 18 | 19 | (define-fun init ((x Int) (y Int) (z Int)) Bool (and (= x 3) (= y 5) (= z 15))) 20 | 21 | (define-fun invariant ((x Int) (y Int) (z Int)) Bool (= z (* x y))) 22 | 23 | (declare-const x_pre Int) 24 | (declare-const y_pre Int) 25 | (declare-const z_pre Int) 26 | (declare-const x_post Int) 27 | (declare-const y_post Int) 28 | (declare-const z_post Int) 29 | (declare-const scalar Int) 30 | 31 | (assert (and 32 | (or 33 | (and 34 | (init x_pre y_pre z_pre) 35 | (not (invariant x_pre y_pre z_pre)) 36 | ) 37 | (and 38 | (<= 0 x_pre) 39 | (<= 0 y_pre) 40 | (<= 0 z_pre) 41 | (<= 0 scalar) 42 | (invariant x_pre y_pre z_pre) 43 | (or 44 | (f x_pre y_pre z_pre scalar x_post y_post z_post) 45 | (g x_pre y_pre z_pre scalar x_post y_post z_post) 46 | ) 47 | (not (invariant x_post y_post z_post)) 48 | ) 49 | ) 50 | )) 51 | 52 | (check-sat) 53 | -------------------------------------------------------------------------------- /examples/homogeneous/homogeneous.sol: -------------------------------------------------------------------------------- 1 | // We want to show that f and g maintain the invariant xy = z 2 | 3 | pragma solidity ^0.5.15; 4 | pragma experimental SMTChecker; 5 | 6 | contract Homogeneous { 7 | uint x; 8 | uint y; 9 | uint z; 10 | 11 | constructor () public { 12 | x = 3; 13 | y = 5; 14 | z = 15; 15 | } 16 | 17 | function f (uint scalar) public { 18 | require (x * scalar > x); 19 | require (z * scalar > z); 20 | x = x * scalar; 21 | z = z * scalar; 22 | assert (x * y == z); 23 | } 24 | 25 | function g (uint scalar) public { 26 | require (y * scalar > y); 27 | require (z * scalar > z); 28 | y = y * scalar; 29 | z = z * scalar; 30 | assert (x * y == z); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/homogeneous/statemachinequery.smt2: -------------------------------------------------------------------------------- 1 | (declare-rel state (Int)) 2 | (declare-rel error ()) 3 | 4 | (define-fun f ((x_pre Int) (x_post Int)) Bool (= x_post (ite (= x_pre 0) 1 x_pre))) 5 | (define-fun g ((x_pre Int) (x_post Int)) Bool (= x_post (ite (= x_pre 1) 2 x_pre))) 6 | (define-fun h ((x_pre Int) (x_post Int)) Bool (= x_post (ite (= x_pre 3) 7 x_pre))) 7 | (define-fun invariant ((x Int)) Bool (and (>= x 0) (<= x 2))) 8 | 9 | (declare-var x_pre Int) 10 | (declare-var x_post Int) 11 | 12 | (rule (state 0)) 13 | 14 | (rule 15 | (=> 16 | (and 17 | (state x_pre) 18 | (or 19 | (f x_pre x_post) 20 | (g x_pre x_post) 21 | (h x_pre x_post) 22 | ) 23 | ) 24 | (state x_post) 25 | )) 26 | 27 | (rule 28 | (=> 29 | (and 30 | (state x_pre) 31 | (not (invariant x_pre)) 32 | ) 33 | error 34 | )) 35 | 36 | (query error :print-certificate true) 37 | 38 | -------------------------------------------------------------------------------- /examples/multipleContracts/multi.act: -------------------------------------------------------------------------------- 1 | behaviour remote of B 2 | interface set_remote(uint _x) 3 | 4 | iff 5 | CALLVALUE == 0 6 | 7 | // we can specify how a call to a function of B 8 | // updates the storage of A by labelling the storage header 9 | storage a 10 | x => _x 11 | 12 | behaviour create_a of B 13 | interface create_a() 14 | 15 | iff 16 | CALLVALUE == 0 17 | 18 | storage 19 | // newAddr should be a builtin function 20 | a => newAddr(ADDRESS, NONCE) 21 | 22 | // storage labels can also introduce new contracts 23 | creates B at newAddr(ADDRESS, NONCE) 24 | uint x := 1 25 | -------------------------------------------------------------------------------- /examples/multipleContracts/multi.sol: -------------------------------------------------------------------------------- 1 | contract A { 2 | uint x; 3 | constructor() { 4 | x = 1; 5 | } 6 | function set(uint _x) { 7 | x = _x; 8 | } 9 | } 10 | 11 | contract B { 12 | A a; 13 | function set_remote(uint _x) { 14 | a.set(_x); 15 | } 16 | 17 | function create_a() { 18 | a = new A(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/transfer/transfer.act: -------------------------------------------------------------------------------- 1 | // Transfer function a la ERC20 2 | 3 | behaviour transfer of Token 4 | interface transfer(uint256 value, address to) 5 | 6 | iff in range uint256 7 | balanceOf[CALLER] - value 8 | balanceOf[to] + value 9 | 10 | iff 11 | CALLVALUE == 0 12 | 13 | case CALLER =/= to: 14 | storage 15 | balanceOf[CALLER] => balanceOf[CALLER] - value 16 | balanceOf[to] => balanceOf[to] + value 17 | returns 1 18 | 19 | case _: 20 | returns 1 21 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "act"; 3 | 4 | inputs = { 5 | flake-utils.url = "github:numtide/flake-utils"; 6 | nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; 7 | hevm = { 8 | url = "github:ethereum/hevm"; 9 | inputs.nixpkgs.follows = "nixpkgs"; 10 | }; 11 | }; 12 | 13 | outputs = { nixpkgs, flake-utils, hevm, ... }: 14 | flake-utils.lib.eachDefaultSystem (system: 15 | let 16 | pkgs = nixpkgs.legacyPackages.${system}; 17 | gitignore = pkgs.nix-gitignore.gitignoreSourcePure [ ./.gitignore ]; 18 | hspkgs = pkgs.haskellPackages.override { 19 | overrides = self: super: { 20 | hevm = hevm.packages.${system}.unwrapped; 21 | }; 22 | }; 23 | act = (hspkgs.callCabal2nixWithOptions "act" (gitignore ./.) "-fci" {}) 24 | .overrideAttrs (attrs : { 25 | buildInputs = attrs.buildInputs ++ [ pkgs.z3 pkgs.cvc5 pkgs.solc ]; 26 | }); 27 | in rec { 28 | packages.act = act; 29 | packages.default = packages.act; 30 | 31 | apps.act = flake-utils.lib.mkApp { drv = packages.act; }; 32 | apps.default = apps.act; 33 | 34 | devShell = let 35 | libraryPath = "${pkgs.lib.makeLibraryPath (with pkgs; [ libff secp256k1 gmp ])}"; 36 | in hspkgs.shellFor { 37 | packages = _: [ act ]; 38 | buildInputs = [ 39 | hspkgs.cabal-install 40 | hspkgs.haskell-language-server 41 | pkgs.jq 42 | pkgs.z3 43 | pkgs.cvc5 44 | pkgs.coq 45 | pkgs.coqPackages.stdlib 46 | pkgs.solc 47 | pkgs.mdbook 48 | pkgs.mdbook-mermaid 49 | pkgs.mdbook-katex 50 | pkgs.secp256k1 51 | pkgs.libff 52 | ]; 53 | withHoogle = true; 54 | shellHook = '' 55 | export PATH=$(pwd)/bin:$PATH 56 | export DYLD_LIBRARY_PATH="${libraryPath}" 57 | ''; 58 | }; 59 | } 60 | ); 61 | } -------------------------------------------------------------------------------- /funding.json: -------------------------------------------------------------------------------- 1 | { 2 | "opRetro": { 3 | "projectId": "0x2704cd27b8c60b098d4fe8c5c0fbae2f8f5fe9067c687c501a4c6dc6e9887876" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /hie.yaml: -------------------------------------------------------------------------------- 1 | cradle: 2 | cabal: 3 | - path: "src/Act" 4 | component: "lib:act" 5 | - path: "src/CLI" 6 | component: "exe:act" 7 | - path: "src/Test" 8 | component: "test:test" 9 | -------------------------------------------------------------------------------- /lib/ActLib.v: -------------------------------------------------------------------------------- 1 | (** library to be included with user coq developments *) 2 | From Stdlib Require Import ZArith. 3 | 4 | Print LoadPath. 5 | (** * type definitions *) 6 | Definition address := Z. 7 | 8 | (** * Environment record *) 9 | Record Env : Set := 10 | { Callvalue : Z; 11 | Caller : address; 12 | Blockhash : Z; 13 | Blocknumber : Z; 14 | Difficulty : Z; 15 | Timestamp : Z; 16 | Gaslimit : Z; 17 | Coinbase : address; 18 | Chainid : Z; 19 | This : address; 20 | Origin : address; 21 | Nonce : Z; 22 | Calldepth : Z }. 23 | 24 | (** * integer bounds *) 25 | Definition UINT_MIN (n : Z) := 0. 26 | Definition UINT_MAX (n : Z) := (2^n - 1)%Z. 27 | Definition INT_MIN (n : Z) := (0 - 2^(n - 1))%Z. 28 | Definition INT_MAX (n : Z) := (2^(n - 1) - 1)%Z. 29 | 30 | (** * notations *) 31 | Notation "a =?? b" := (bool_eq a b) (at level 70, no associativity). 32 | Definition range256 (n : Z) := (0 <= n <= UINT_MAX 256)%Z. 33 | 34 | (** * lemmas *) 35 | 36 | Lemma ite_true {A : Type} : forall (b : bool) (x y z : A), 37 | ((if b then x else y) = z) -> (z <> y) -> (b = true). 38 | Proof. 39 | intros b x y H Heq. 40 | induction b. 41 | - reflexivity. 42 | - intros Hneq. 43 | rewrite Heq in Hneq. 44 | apply except. 45 | apply Hneq. 46 | reflexivity. 47 | Qed. 48 | 49 | Lemma ite_false {A : Type} : forall (b : bool) (x y z : A), 50 | ((if b then x else y) = z) -> (z <> x) -> (b = false). 51 | Proof. 52 | intros b x y H Heq. 53 | induction b. 54 | - intros Hneq. 55 | rewrite Heq in Hneq. 56 | apply except. 57 | apply Hneq. 58 | reflexivity. 59 | - reflexivity. 60 | Qed. 61 | 62 | Lemma ite_dec {A : Type} : forall (b : bool) (x y : A), 63 | let ite := if b then x else y in 64 | ite = x \/ ite = y. 65 | Proof. 66 | intros. unfold ite. 67 | destruct b. 68 | - left. reflexivity. 69 | - right. reflexivity. 70 | Qed. 71 | -------------------------------------------------------------------------------- /nix/sources.json: -------------------------------------------------------------------------------- 1 | { 2 | "dapptools": { 3 | "branch": "master", 4 | "description": "Dapp, Seth, Hevm, and more", 5 | "homepage": "https://dapp.tools", 6 | "owner": "dapphub", 7 | "repo": "dapptools", 8 | "rev": "d10dbe5e58a8e1f0dbd9c8f6e959f63172c90cfb", 9 | "sha256": "0qq7nn0ck26kmp7zj6ykpszrnn3yxs046fqkirb5y63nlphyw26x", 10 | "type": "tarball", 11 | "url": "https://github.com/dapphub/dapptools/archive/d10dbe5e58a8e1f0dbd9c8f6e959f63172c90cfb.tar.gz", 12 | "url_template": "https://github.com///archive/.tar.gz" 13 | }, 14 | "nixpkgs": { 15 | "branch": "21.05", 16 | "description": "A Nix Packages collection", 17 | "homepage": "https://github.com/NixOS/nixpkgs", 18 | "owner": "nixos", 19 | "repo": "nixpkgs", 20 | "rev": "7e9b0dff974c89e070da1ad85713ff3c20b0ca97", 21 | "sha256": "1ckzhh24mgz6jd1xhfgx0i9mijk6xjqxwsshnvq789xsavrmsc36", 22 | "type": "tarball", 23 | "url": "https://github.com/nixos/nixpkgs/archive/7e9b0dff974c89e070da1ad85713ff3c20b0ca97.tar.gz", 24 | "url_template": "https://github.com///archive/.tar.gz" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Act/Consistency.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE GADTs #-} 2 | {-# LANGUAGE FlexibleContexts #-} 3 | {-# LANGUAGE MonadComprehensions #-} 4 | {-# LANGUAGE ViewPatterns #-} 5 | {-# LANGUAGE LambdaCase #-} 6 | {-# LANGUAGE DataKinds #-} 7 | {-# Language RecordWildCards #-} 8 | 9 | module Act.Consistency ( 10 | checkCases 11 | ) where 12 | 13 | 14 | import Prelude hiding (GT, LT) 15 | 16 | import Data.List 17 | import Prettyprinter 18 | import System.Exit (exitFailure) 19 | import Data.Maybe 20 | 21 | import Act.Syntax 22 | import Act.Syntax.Annotated 23 | import Act.SMT as SMT 24 | import Act.Syntax.Untyped (makeIface) 25 | import Act.Print 26 | 27 | import qualified EVM.Solvers as Solvers 28 | 29 | -- TODO this is duplicated in hevm Keccak.hs but not exported 30 | combine :: [a] -> [(a,a)] 31 | combine lst = combine' lst [] 32 | where 33 | combine' [] acc = concat acc 34 | combine' (x:xs) acc = 35 | let xcomb = [ (x, y) | y <- xs] in 36 | combine' xs (xcomb:acc) 37 | 38 | -- | Checks non-overlapping cases, 39 | -- For every pair of case conditions we assert that they are true 40 | -- simultaneously. The query must be unsat. 41 | mkNonoverlapAssertion :: [Exp ABoolean] -> Exp ABoolean 42 | mkNonoverlapAssertion caseconds = 43 | mkOr $ (\(x, y) -> And nowhere x y) <$> combine caseconds 44 | where 45 | mkOr [] = LitBool nowhere False 46 | mkOr (c:cs) = Or nowhere c (mkOr cs) 47 | 48 | -- | Checks exhaustiveness of cases. 49 | -- We assert that none of the case conditions are true at the same 50 | -- time. The query must be unsat. 51 | mkExhaustiveAssertion :: [Exp ABoolean] -> Exp ABoolean 52 | mkExhaustiveAssertion caseconds = 53 | foldl mkAnd (LitBool nowhere True) caseconds 54 | where 55 | mkAnd r c = And nowhere (Neg nowhere c) r 56 | 57 | -- | Create a query for cases 58 | mkCaseQuery :: ([Exp ABoolean] -> Exp ABoolean) -> [Behaviour] -> (Id, SMTExp, (SolverInstance -> IO Model)) 59 | mkCaseQuery props behvs@((Behaviour _ _ (Interface ifaceName decls) _ preconds _ _ _ _):_) = 60 | (ifaceName, mkSMT, getModel) 61 | where 62 | locs = nub $ concatMap locsFromExp (preconds <> caseconds) 63 | env = concatMap ethEnvFromBehaviour behvs 64 | pres = mkAssert ifaceName <$> preconds 65 | caseconds = concatMap _caseconditions behvs 66 | 67 | mkSMT = SMTExp 68 | { _storage = concatMap (declareStorage [Pre]) locs 69 | , _calldata = declareArg ifaceName <$> decls 70 | , _environment = declareEthEnv <$> env 71 | , _assertions = (mkAssert ifaceName $ props caseconds) : pres 72 | } 73 | 74 | getModel solver = do 75 | prestate <- mapM (getStorageValue solver ifaceName Pre) locs 76 | calldata <- mapM (getCalldataValue solver ifaceName) decls 77 | environment <- mapM (getEnvironmentValue solver) env 78 | pure $ Model 79 | { _mprestate = prestate 80 | , _mpoststate = [] 81 | , _mcalldata = (ifaceName, calldata) 82 | , _menvironment = environment 83 | , _minitargs = [] 84 | } 85 | mkCaseQuery _ [] = error "Internal error: behaviours cannot be empty" 86 | 87 | -- | Checks nonoverlapping and exhaustiveness of cases 88 | checkCases :: Act -> Solvers.Solver -> Maybe Integer -> Bool -> IO () 89 | checkCases (Act _ contracts) solver' smttimeout debug = do 90 | let groups = concatMap (\(Contract _ behvs) -> groupBy sameIface behvs) contracts 91 | let config = SMT.SMTConfig solver' (fromMaybe 20000 smttimeout) debug 92 | solver <- spawnSolver config 93 | let qs = mkCaseQuery mkNonoverlapAssertion <$> groups 94 | r <- flip mapM qs (\(name, q, getModel) -> do 95 | res <- checkSat solver getModel q 96 | pure (name, res)) 97 | mapM_ (checkRes "nonoverlapping") r 98 | let qs' = mkCaseQuery mkExhaustiveAssertion <$> groups 99 | r' <- flip mapM qs' (\(name, q, getModel) -> do 100 | res <- checkSat solver getModel q 101 | pure (name, res)) 102 | mapM_ (checkRes "exhaustive") r' 103 | 104 | where 105 | 106 | sameIface (Behaviour _ _ iface _ _ _ _ _ _) (Behaviour _ _ iface' _ _ _ _ _ _) = 107 | makeIface iface == makeIface iface' 108 | 109 | checkRes :: String -> (Id, SMT.SMTResult) -> IO () 110 | checkRes check (name, res) = 111 | case res of 112 | Sat model -> failMsg ("Cases are not " <> check <> " for behavior " <> name <> ".") (prettyAnsi model) 113 | Unsat -> pure () 114 | Unknown -> errorMsg $ "Solver timeour. Cannot prove that cases are " <> check <> " for behavior " <> name <> "." 115 | SMT.Error _ err -> errorMsg $ "Solver error: " <> err <> "\nCannot prove that cases are " <> check <> " for behavior " <> name <> "." 116 | 117 | failMsg str model = render (red (pretty str) <> line <> model <> line) >> exitFailure 118 | errorMsg str = render (pretty str <> line) >> exitFailure 119 | -------------------------------------------------------------------------------- /src/Act/Dev.hs: -------------------------------------------------------------------------------- 1 | module Act.Dev where 2 | 3 | 4 | import Act.CLI 5 | import Act.Coq (coq) 6 | import Act.Enrich 7 | import Act.Consistency 8 | import qualified EVM.Solvers as Solvers 9 | 10 | import qualified Data.Text as T 11 | 12 | writeCoqFile :: FilePath -> FilePath -> IO () 13 | writeCoqFile src trg = do 14 | contents <- readFile src 15 | proceed contents (enrich <$> compile contents) $ \claims -> 16 | writeFile trg . T.unpack $ coq claims 17 | 18 | 19 | checkCaseConsistency :: FilePath -> Solvers.Solver -> Maybe Integer -> Bool -> IO () 20 | checkCaseConsistency actspec solver' smttimeout' debug' = do 21 | specContents <- readFile actspec 22 | proceed specContents (enrich <$> compile specContents) $ \act -> do 23 | checkCases act solver' smttimeout' debug' 24 | -------------------------------------------------------------------------------- /src/Act/Enrich.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE GADTs #-} 2 | {-# LANGUAGE DataKinds #-} 3 | 4 | module Act.Enrich (enrich, mkStorageBounds) where 5 | 6 | import Data.Maybe 7 | import Data.List (nub) 8 | 9 | import Act.Syntax 10 | import Act.Syntax.Annotated 11 | import Act.Type (defaultStore) 12 | 13 | -- | Adds extra preconditions to non constructor behaviours based on the types of their variables 14 | enrich :: Act -> Act 15 | enrich (Act store contracts) = Act store (enrichContract <$> contracts) 16 | where 17 | enrichContract (Contract ctors behvs) = Contract (enrichConstructor ctors) (enrichBehaviour <$> behvs) 18 | 19 | -- |Adds type bounds for calldata , environment vars, and external storage vars as preconditions 20 | enrichConstructor :: Constructor -> Constructor 21 | enrichConstructor ctor@(Constructor _ (Interface _ decls) _ pre _ invs _) = 22 | ctor { _cpreconditions = pre' 23 | , _invariants = invs' } 24 | where 25 | pre' = pre 26 | <> mkCallDataBounds decls 27 | <> mkEthEnvBounds (ethEnvFromConstructor ctor) 28 | invs' = enrichInvariant ctor <$> invs 29 | 30 | -- | Adds type bounds for calldata, environment vars, and storage vars as preconditions 31 | enrichBehaviour :: Behaviour -> Behaviour 32 | enrichBehaviour behv@(Behaviour _ _ (Interface _ decls) _ pre cases _ stateUpdates _) = 33 | behv { _preconditions = pre' } 34 | where 35 | pre' = pre 36 | <> mkCallDataBounds decls 37 | <> mkStorageBounds stateUpdates 38 | <> mkStorageBoundsLoc (concatMap locsFromExp (pre <> cases)) 39 | <> mkEthEnvBounds (ethEnvFromBehaviour behv) 40 | 41 | -- | Adds type bounds for calldata, environment vars, and storage vars 42 | enrichInvariant :: Constructor -> Invariant -> Invariant 43 | enrichInvariant (Constructor _ (Interface _ decls) _ _ _ _ _) inv@(Invariant _ preconds storagebounds (predicate,_)) = 44 | inv { _ipreconditions = preconds', _istoragebounds = storagebounds' } 45 | where 46 | preconds' = preconds 47 | <> mkCallDataBounds decls 48 | <> mkEthEnvBounds (ethEnvFromExp predicate) 49 | storagebounds' = storagebounds 50 | <> mkStorageBoundsLoc (locsFromExp predicate) 51 | 52 | mkEthEnvBounds :: [EthEnv] -> [Exp ABoolean] 53 | mkEthEnvBounds vars = catMaybes $ mkBound <$> nub vars 54 | where 55 | mkBound :: EthEnv -> Maybe (Exp ABoolean) 56 | mkBound e = case lookup e defaultStore of 57 | Just AInteger -> Just $ bound (toAbiType e) (IntEnv nowhere e) 58 | _ -> Nothing 59 | 60 | toAbiType :: EthEnv -> AbiType 61 | toAbiType env = case env of 62 | Caller -> AbiAddressType 63 | Callvalue -> AbiUIntType 256 64 | Calldepth -> AbiUIntType 10 65 | Origin -> AbiAddressType 66 | Blockhash -> AbiBytesType 32 67 | Blocknumber -> AbiUIntType 256 68 | Difficulty -> AbiUIntType 256 69 | Chainid -> AbiUIntType 256 70 | Gaslimit -> AbiUIntType 256 71 | Coinbase -> AbiAddressType 72 | Timestamp -> AbiUIntType 256 73 | This -> AbiAddressType 74 | Nonce -> AbiUIntType 256 75 | 76 | -- | extracts bounds from the AbiTypes of Integer values in storage 77 | mkStorageBounds :: [StorageUpdate] -> [Exp ABoolean] 78 | mkStorageBounds refs = concatMap mkBound refs 79 | where 80 | mkBound :: StorageUpdate -> [Exp ABoolean] 81 | mkBound (Update SInteger item _) = [fromItem item] 82 | mkBound _ = [] 83 | 84 | -- TODO why only Pre items here? 85 | fromItem :: TItem AInteger Storage -> Exp ABoolean 86 | fromItem item@(Item _ (PrimitiveType vt) _) = bound vt (TEntry nowhere Pre SStorage item) 87 | fromItem (Item _ (ContractType _) _) = LitBool nowhere True 88 | 89 | mkStorageBoundsLoc :: [StorageLocation] -> [Exp ABoolean] 90 | mkStorageBoundsLoc refs = concatMap mkBound refs 91 | where 92 | mkBound :: StorageLocation -> [Exp ABoolean] 93 | mkBound (Loc SInteger item) = [fromItem item] 94 | mkBound _ = [] 95 | 96 | mkCallDataBounds :: [Decl] -> [Exp ABoolean] 97 | mkCallDataBounds = concatMap $ \(Decl typ name) -> case fromAbiType typ of 98 | AInteger -> [bound typ (_Var Pre typ name)] 99 | _ -> [] 100 | -------------------------------------------------------------------------------- /src/Act/Error.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE OverloadedLists #-} 2 | {-# LANGUAGE UndecidableInstances #-} 3 | {-# LANGUAGE FlexibleContexts #-} 4 | {-# LANGUAGE RankNTypes #-} 5 | {-# LANGUAGE ScopedTypeVariables #-} 6 | 7 | {-| 8 | Module : Error 9 | Description : An instantiation of 'Validation' with our error type. 10 | 11 | This specializes 'Data.Validation.Validation' to keep its errors in a 'NonEmpty' list 12 | and keep track of a 'Pn' for every error it logs. There is also some infrastructure 13 | around modified chaining/branching behaviours. 14 | -} 15 | 16 | module Act.Error (module Act.Error) where 17 | 18 | import Data.List (find) 19 | import Data.List.NonEmpty as NE 20 | import Data.Validation as Act.Error 21 | 22 | import Act.Syntax.Untyped (Pn) 23 | 24 | -- Reexport NonEmpty so that we can use `-XOverloadedLists` without thinking. 25 | import Data.List.NonEmpty as Act.Error (NonEmpty) 26 | 27 | type Error e = Validation (NonEmpty (Pn,e)) 28 | 29 | throw :: (Pn,e) -> Error e a 30 | throw msg = Failure [msg] 31 | 32 | assert :: (Pn, e) -> Bool -> Error e () 33 | assert err b = if b then pure () else throw err 34 | 35 | foldValidation :: (b -> a -> Error err b) -> b -> [a] -> Error err b 36 | foldValidation _ b [] = pure b 37 | foldValidation f b (a:as) = f b a `bindValidation` \b' -> foldValidation f b' as 38 | 39 | infixr 1 <==<, >==> 40 | 41 | -- Like 'Control.Monad.(>=>)' but allows us to chain error-prone 42 | -- computations even without a @Monad@ instance. 43 | (>==>) :: (a -> Error e b) -> (b -> Error e c) -> a -> Error e c 44 | f >==> g = \x -> f x `bindValidation` g 45 | 46 | (<==<) :: (b -> Error e c) -> (a -> Error e b) -> a -> Error e c 47 | (<==<) = flip (>==>) 48 | 49 | -- | Runs through a list of error-prone computations and returns the first 50 | -- successful one, with the definition of "success" expanded to include 51 | -- failures which did not generate any error at the supplied position. 52 | notAtPosn :: Pn -> [Error e a] -> Maybe (Error e a) 53 | notAtPosn p = find valid 54 | where 55 | valid (Success _) = True 56 | valid (Failure errs) = all ((p /=) . fst) errs 57 | 58 | -- | Try to find a succesfull computation in a list, and if it fails 59 | -- it returns a default computation 60 | findSuccess :: Error e a -> [Error e a] -> Error e a 61 | findSuccess d comp = case find valid comp of 62 | Just a -> a 63 | Nothing -> foldl (*>) d comp 64 | where 65 | valid (Success _) = True 66 | valid _ = False 67 | 68 | 69 | concatError :: Error e a -> [Error e a] -> Error e a 70 | concatError def = \case 71 | [] -> def 72 | x:xs -> foldl (*>) x xs 73 | -------------------------------------------------------------------------------- /src/Act/Makefile: -------------------------------------------------------------------------------- 1 | main: Lex.hs Parse.hs 2 | 3 | Parse.hs: Parse.y 4 | happy Parse.y 5 | 6 | Lex.hs: Lex.x 7 | alex Lex.x 8 | 9 | .PHONY: clean 10 | clean: 11 | rm -f *.hi *.o Lex.hs Parse.hs main 12 | -------------------------------------------------------------------------------- /src/Act/Syntax/Annotated.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE GADTs #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE PatternSynonyms #-} 4 | {-# LANGUAGE RecordWildCards #-} 5 | 6 | {-| 7 | Module : Syntax.Annotated 8 | Description : AST data types where all implicit timings have been made explicit. 9 | -} 10 | module Act.Syntax.Annotated (module Act.Syntax.Annotated) where 11 | 12 | import qualified Act.Syntax.TimeAgnostic as Agnostic 13 | import Act.Syntax.TimeAgnostic (Timing(..),setPre,setPost) 14 | 15 | -- Reexports 16 | import Act.Syntax.TimeAgnostic as Act.Syntax.Annotated hiding (Timing(..),Timable(..),Time,Neither,Act,Contract,Invariant,InvariantPred,Constructor,Behaviour,StorageUpdate,StorageLocation,TItem,Exp,TypedExp,Ref) 17 | import Act.Syntax.TimeAgnostic as Act.Syntax.Annotated (pattern Act, pattern Contract, pattern Invariant, pattern Constructor, pattern Behaviour, pattern Exp) 18 | 19 | 20 | -- We shadow all timing-agnostic AST types with explicitly timed versions. 21 | type Act = Agnostic.Act Timed 22 | type Contract = Agnostic.Contract Timed 23 | type Invariant = Agnostic.Invariant Timed 24 | type InvariantPred = Agnostic.InvariantPred Timed 25 | type Constructor = Agnostic.Constructor Timed 26 | type Behaviour = Agnostic.Behaviour Timed 27 | type StorageUpdate = Agnostic.StorageUpdate Timed 28 | type StorageLocation = Agnostic.StorageLocation Timed 29 | type Ref k = Agnostic.Ref k Timed 30 | type TItem k a = Agnostic.TItem k a Timed 31 | type Exp a = Agnostic.Exp a Timed 32 | type TypedExp = Agnostic.TypedExp Timed 33 | 34 | ------------------------------------------ 35 | -- * How to make all timings explicit * -- 36 | ------------------------------------------ 37 | 38 | instance Annotatable Agnostic.Act where 39 | annotate (Agnostic.Act store act) = Agnostic.Act store $ fmap annotate act 40 | 41 | instance Annotatable Agnostic.Contract where 42 | annotate (Agnostic.Contract ctor behv) = Agnostic.Contract (annotate ctor) (fmap annotate behv) 43 | 44 | instance Annotatable Agnostic.Invariant where 45 | annotate inv@Invariant{..} = inv 46 | { _ipreconditions = setPre <$> _ipreconditions 47 | , _istoragebounds = setPre <$> _istoragebounds 48 | , _predicate = (setPre _predicate, setPost _predicate) 49 | } 50 | 51 | instance Annotatable Agnostic.Constructor where 52 | annotate ctor@Constructor{..} = ctor 53 | { _cpreconditions = setPre <$> _cpreconditions 54 | , _initialStorage = annotate <$> _initialStorage 55 | , _invariants = annotate <$> _invariants 56 | } 57 | 58 | instance Annotatable Agnostic.Behaviour where 59 | annotate behv@Behaviour{..} = behv 60 | { _preconditions = setPre <$> _preconditions 61 | , _caseconditions = setPre <$> _caseconditions 62 | , _stateUpdates = annotate <$> _stateUpdates 63 | } 64 | 65 | instance Annotatable Agnostic.StorageUpdate where 66 | -- The timing in items only refers to the timing of mapping indices of a 67 | -- storage update. Hence, it should be Pre 68 | annotate (Update typ item expr) = Update typ (setPre item) (setPre expr) 69 | -------------------------------------------------------------------------------- /src/Act/Syntax/Timing.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE GADTs #-} 2 | {-# LANGUAGE DataKinds #-} 3 | {-# LANGUAGE StandaloneDeriving #-} 4 | 5 | {-| 6 | Module : Syntax.Timing 7 | Description : Stuff relating to explicit and implicit references to pre-/post-states. 8 | -} 9 | module Act.Syntax.Timing where 10 | 11 | import Data.Char (toLower) 12 | 13 | -- | This will never be used as-is. Its only purpose is to use with -XDataKinds, 14 | -- to distinguish between those AST terms which have fully explicit timings ('Timed') 15 | -- and those which have implicit timings. 16 | data Timing = Timed | Untimed 17 | 18 | -- | Encodes choice between explicitly referring to the pre-/post-state, or not. 19 | data Time t where 20 | Pre :: Time Timed 21 | Post :: Time Timed 22 | Neither :: Time Untimed 23 | deriving instance Eq (Time t) 24 | deriving instance Show (Time t) 25 | 26 | -- | Encodes choice between referencing the pre- or the poststate. 27 | type When = Time Timed 28 | 29 | -- | True iff the input is `Pre` or `Post`. 30 | isTimed :: Time t -> Bool 31 | isTimed Neither = False 32 | isTimed _ = True 33 | 34 | -- | If the supplied time is `Pre`, this returns `pre(input)` (and analogously for `Post`). 35 | -- Otherwise returns the untouched `String`. 36 | timeParens :: Time t -> String -> String 37 | timeParens t s | isTimed t = fmap toLower (show t) <> "(" <> s <> ")" 38 | | otherwise = s 39 | 40 | -- | Types which we can always annotate with explicit timings without needing context. 41 | class Annotatable c where 42 | -- | Defines how an 'Untimed' thing should be given explicit timings. 43 | annotate :: c Untimed -> c Timed 44 | 45 | -- | Types for which all implicit timings can freely be given any explicit timing, 46 | -- i.e. we need context to decide which time it refers to. 47 | class Timable c where 48 | -- | Takes an 'Untimed' 'Timable' thing and points it towards the prestate. 49 | setPre :: c Untimed -> c Timed 50 | setPre = setTime Pre 51 | 52 | -- | Takes an 'Untimed' 'Timeable' thing and points it towards the poststate. 53 | setPost :: c Untimed -> c Timed 54 | setPost = setTime Post 55 | 56 | -- | Takes an 'Untimed' 'Timeable' thing and points it towards the given state. 57 | setTime :: When -> c Untimed -> c Timed 58 | -------------------------------------------------------------------------------- /src/Act/Syntax/Typed.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE DataKinds #-} 2 | {-# LANGUAGE PatternSynonyms #-} 3 | 4 | {-| 5 | Module : Syntax.Typed 6 | Description : AST data types which have passed type checking but still contain implicit timings. 7 | -} 8 | module Act.Syntax.Typed (module Act.Syntax.Typed) where 9 | 10 | import qualified Act.Syntax.TimeAgnostic as Agnostic 11 | 12 | -- Reexports 13 | import Act.Syntax.TimeAgnostic as Act.Syntax.Typed hiding (Act,Contract,Invariant,InvariantPred,Constructor,Behaviour,StorageUpdate,StorageLocation) 14 | import Act.Syntax.TimeAgnostic as Act.Syntax.Typed (pattern Act, pattern Contract, pattern Invariant, pattern Constructor, pattern Behaviour) 15 | 16 | -- We shadow all timing-agnostic AST types with versions 17 | -- that need to have implicit timings where possible. 18 | type Act = Agnostic.Act Untimed 19 | type Contract = Agnostic.Contract Untimed 20 | type Invariant = Agnostic.Invariant Untimed 21 | type Constructor = Agnostic.Constructor Untimed 22 | type Behaviour = Agnostic.Behaviour Untimed 23 | type StorageUpdate = Agnostic.StorageUpdate Untimed 24 | type StorageLocation = Agnostic.StorageLocation Untimed 25 | -------------------------------------------------------------------------------- /src/Act/Syntax/Types.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE GADTs #-} 2 | {-# LANGUAGE ViewPatterns #-} 3 | {-# LANGUAGE PatternSynonyms #-} 4 | {-# LANGUAGE StandaloneDeriving #-} 5 | {-# LANGUAGE DataKinds #-} 6 | {-# LANGUAGE TypeFamilies #-} 7 | {-# LANGUAGE FlexibleInstances #-} 8 | {-# LANGUAGE LambdaCase #-} 9 | {-# LANGUAGE ScopedTypeVariables #-} 10 | {-# LANGUAGE MultiParamTypeClasses #-} 11 | 12 | -- These extensions should be removed once we remove the defs at the end of this file. 13 | {-# LANGUAGE RankNTypes, TypeApplications, StandaloneKindSignatures, PolyKinds #-} 14 | 15 | {-| 16 | Module : Syntax.Types 17 | Description : Types that represent Act types, and functions and patterns to go between them and Haskell's own types. 18 | -} 19 | 20 | module Act.Syntax.Types (module Act.Syntax.Types) where 21 | 22 | import Data.Singletons 23 | import Data.ByteString 24 | import Data.Type.Equality (TestEquality(..), (:~:)(..)) 25 | import EVM.ABI as Act.Syntax.Types (AbiType(..)) 26 | 27 | import Act.Syntax.Untyped (ValueType(..)) 28 | 29 | -- | Types of Act expressions 30 | data ActType 31 | = AInteger 32 | | ABoolean 33 | | AByteStr 34 | 35 | -- | Singleton runtime witness for Act types 36 | -- Sometimes we need to examine type tags at runime. Tagging structures 37 | -- with this type will let us do that. 38 | data SType (a :: ActType) where 39 | SInteger :: SType AInteger 40 | SBoolean :: SType ABoolean 41 | SByteStr :: SType AByteStr 42 | deriving instance Eq (SType a) 43 | 44 | instance Show (SType a) where 45 | show = \case 46 | SInteger -> "int" 47 | SBoolean -> "bool" 48 | SByteStr -> "bytestring" 49 | 50 | type instance Sing = SType 51 | 52 | instance TestEquality SType where 53 | testEquality SInteger SInteger = Just Refl 54 | testEquality SBoolean SBoolean = Just Refl 55 | testEquality SByteStr SByteStr = Just Refl 56 | testEquality _ _ = Nothing 57 | 58 | 59 | -- | Compare equality of two things parametrized by types which have singletons. 60 | eqS :: forall (a :: ActType) (b :: ActType) f t. (SingI a, SingI b, Eq (f a t)) => f a t -> f b t -> Bool 61 | eqS fa fb = maybe False (\Refl -> fa == fb) $ testEquality (sing @a) (sing @b) 62 | 63 | -- | The same but when the higher-kinded type has two type arguments 64 | eqS' :: forall (a :: ActType) (b :: ActType) f t t'. (SingI a, SingI b, Eq (f a t t')) => f a t t' -> f b t t' -> Bool 65 | eqS' fa fb = maybe False (\Refl -> fa == fb) $ testEquality (sing @a) (sing @b) 66 | 67 | -- Defines which singleton to retrieve when we only have the type, not the 68 | -- actual singleton. 69 | instance SingI 'AInteger where sing = SInteger 70 | instance SingI 'ABoolean where sing = SBoolean 71 | instance SingI 'AByteStr where sing = SByteStr 72 | 73 | -- | Reflection of an Act type into a haskell type. Useful to define 74 | -- the result type of the evaluation function. 75 | type family TypeOf a where 76 | TypeOf 'AInteger = Integer 77 | TypeOf 'ABoolean = Bool 78 | TypeOf 'AByteStr = ByteString 79 | 80 | 81 | fromAbiType :: AbiType -> ActType 82 | fromAbiType (AbiUIntType _) = AInteger 83 | fromAbiType (AbiIntType _) = AInteger 84 | fromAbiType AbiAddressType = AInteger 85 | fromAbiType AbiBoolType = ABoolean 86 | fromAbiType (AbiBytesType n) = if n <= 32 then AInteger else AByteStr 87 | fromAbiType AbiBytesDynamicType = AByteStr 88 | fromAbiType AbiStringType = AByteStr 89 | fromAbiType _ = error "Syntax.Types.actType: TODO" 90 | 91 | 92 | someType :: ActType -> SomeType 93 | someType AInteger = SomeType SInteger 94 | someType ABoolean = SomeType SBoolean 95 | someType AByteStr = SomeType SByteStr 96 | 97 | actType :: SType s -> ActType 98 | actType SInteger = AInteger 99 | actType SBoolean = ABoolean 100 | actType SByteStr = AByteStr 101 | 102 | fromValueType :: ValueType -> ActType 103 | fromValueType (PrimitiveType t) = fromAbiType t 104 | fromValueType (ContractType _) = AInteger 105 | 106 | data SomeType where 107 | SomeType :: SingI a => SType a -> SomeType 108 | 109 | -- | Pattern match on an 'SomeType' is if it were an 'SType'. 110 | pattern FromSome :: () => (SingI a) => SType a -> SomeType 111 | pattern FromSome t <- SomeType t 112 | {-# COMPLETE FromSome #-} 113 | 114 | -- | Pattern match on an 'AbiType' is if it were an 'SType'. 115 | pattern FromAbi :: () => (SingI a) => SType a -> AbiType 116 | pattern FromAbi t <- (someType . fromAbiType -> FromSome t) 117 | {-# COMPLETE FromAbi #-} 118 | 119 | -- | Pattern match on an 'ActType' is if it were an 'SType'. 120 | pattern FromAct ::() => (SingI a) => SType a -> ActType 121 | pattern FromAct t <- (someType -> FromSome t) 122 | {-# COMPLETE FromAct #-} 123 | 124 | -- | Pattern match on an 'ValueType' is if it were an 'SType'. 125 | pattern FromVType :: () => (SingI a) => SType a -> ValueType 126 | pattern FromVType t <- (someType . fromValueType -> FromSome t) 127 | {-# COMPLETE FromVType #-} 128 | 129 | 130 | -- | Helper pattern to retrieve the 'SingI' instances of the type 131 | -- represented by an 'SType'. 132 | pattern SType :: () => (SingI a) => SType a 133 | pattern SType <- Sing 134 | {-# COMPLETE SType #-} 135 | -------------------------------------------------------------------------------- /src/Act/Syntax/Untyped.hs: -------------------------------------------------------------------------------- 1 | -- data types for the parsed syntax. 2 | -- Has the correct basic structure, but doesn't necessarily type check 3 | -- It is also equipped with position information for extra debugging xp 4 | {-# LANGUAGE OverloadedStrings #-} 5 | 6 | module Act.Syntax.Untyped (module Act.Syntax.Untyped) where 7 | 8 | import Data.Aeson 9 | import Data.List (intercalate) 10 | import Data.List.NonEmpty (NonEmpty) 11 | import Data.Text as T (pack) 12 | 13 | import EVM.ABI 14 | import Act.Lex 15 | 16 | type Pn = AlexPosn 17 | 18 | type Id = String 19 | 20 | newtype Act = Main [Contract] 21 | deriving (Eq, Show) 22 | 23 | data Contract = Contract Definition [Transition] 24 | deriving (Eq, Show) 25 | 26 | data Definition = Definition Pn Id Interface [Pointer] [IffH] Creates Ensures Invariants 27 | deriving (Eq, Show) 28 | 29 | data Transition = Transition Pn Id Id Interface [Pointer] [IffH] Cases Ensures 30 | deriving (Eq, Show) 31 | 32 | type Ensures = [Expr] 33 | 34 | type Invariants = [Expr] 35 | 36 | data Pointer = PointsTo Pn Id Id 37 | deriving (Eq, Ord) 38 | 39 | instance Show Pointer where 40 | show (PointsTo _ x c) = "(" <> x <> "|->" <> c <> ")" 41 | 42 | data Interface = Interface Id [Decl] 43 | deriving (Eq, Ord) 44 | 45 | instance Show Interface where 46 | show (Interface a d) = a <> "(" <> intercalate ", " (fmap show d) <> ")" 47 | 48 | data Cases 49 | = Direct Post 50 | | Branches [Case] 51 | deriving (Eq, Show) 52 | 53 | data Case = Case Pn Expr Post 54 | deriving (Eq, Show) 55 | 56 | data Post 57 | = Post [Storage] (Maybe Expr) 58 | deriving (Eq, Show) 59 | 60 | newtype Creates = Creates [Assign] 61 | deriving (Eq, Show) 62 | 63 | data Storage 64 | = Rewrite Entry Expr 65 | deriving (Eq, Show) 66 | 67 | data Assign = AssignVal StorageVar Expr | AssignMany StorageVar [Defn] | AssignStruct StorageVar [Defn] 68 | deriving (Eq, Show) 69 | -- TODO AssignStruct is never used 70 | 71 | data IffH = Iff Pn [Expr] | IffIn Pn AbiType [Expr] 72 | deriving (Eq, Show) 73 | 74 | data Entry 75 | = EVar Pn Id 76 | | EMapping Pn Entry [Expr] 77 | | EField Pn Entry Id 78 | deriving (Eq, Show) 79 | 80 | data Defn = Defn Expr Expr 81 | deriving (Eq, Show) 82 | 83 | data Expr 84 | = EAnd Pn Expr Expr 85 | | EOr Pn Expr Expr 86 | | ENot Pn Expr 87 | | EImpl Pn Expr Expr 88 | | EEq Pn Expr Expr 89 | | ENeq Pn Expr Expr 90 | | ELEQ Pn Expr Expr 91 | | ELT Pn Expr Expr 92 | | EGEQ Pn Expr Expr 93 | | EGT Pn Expr Expr 94 | | EAdd Pn Expr Expr 95 | | ESub Pn Expr Expr 96 | | EITE Pn Expr Expr Expr 97 | | EMul Pn Expr Expr 98 | | EDiv Pn Expr Expr 99 | | EMod Pn Expr Expr 100 | | EExp Pn Expr Expr 101 | | EUTEntry Entry 102 | | EPreEntry Entry 103 | | EPostEntry Entry 104 | | ECreate Pn Id [Expr] 105 | | ListConst Expr 106 | | ECat Pn Expr Expr 107 | | ESlice Pn Expr Expr Expr 108 | | ENewaddr Pn Expr Expr 109 | | ENewaddr2 Pn Expr Expr Expr 110 | | BYHash Pn Expr 111 | | BYAbiE Pn Expr 112 | | StringLit Pn String 113 | | WildExp Pn 114 | | EnvExp Pn EthEnv 115 | | IntLit Pn Integer 116 | | BoolLit Pn Bool 117 | | EInRange Pn AbiType Expr 118 | deriving (Eq, Show) 119 | 120 | data EthEnv 121 | = Caller 122 | | Callvalue 123 | | Calldepth 124 | | Origin 125 | | Blockhash 126 | | Blocknumber 127 | | Difficulty 128 | | Chainid 129 | | Gaslimit 130 | | Coinbase 131 | | Timestamp 132 | | This 133 | | Nonce 134 | deriving (Show, Eq) 135 | 136 | 137 | data ValueType 138 | = ContractType Id 139 | | PrimitiveType AbiType 140 | deriving Eq 141 | 142 | instance Show ValueType where 143 | show (ContractType c) = c 144 | show (PrimitiveType t) = show t 145 | 146 | data SlotType 147 | = StorageMapping (NonEmpty ValueType) ValueType 148 | | StorageValue ValueType 149 | deriving (Eq) 150 | 151 | instance Show SlotType where 152 | show (StorageValue t) = show t 153 | show (StorageMapping s t) = 154 | foldr 155 | (\x y -> 156 | "mapping(" 157 | <> show x 158 | <> " => " 159 | <> y 160 | <> ")") 161 | (show t) s 162 | 163 | 164 | data StorageVar = StorageVar Pn SlotType Id 165 | deriving (Eq, Show) 166 | 167 | data Decl = Decl AbiType Id 168 | deriving (Eq, Ord) 169 | 170 | instance Show Decl where 171 | show (Decl t a) = show t <> " " <> a 172 | 173 | instance ToJSON SlotType where 174 | toJSON (StorageValue t) = object ["kind" .= String "ValueType" 175 | , "valueType" .= toJSON t] 176 | toJSON (StorageMapping ixTypes resType) = object [ "kind" .= String "MappingType" 177 | , "ixTypes" .= toJSON ixTypes 178 | , "resType" .= toJSON resType] 179 | 180 | 181 | instance ToJSON ValueType where 182 | toJSON (ContractType c) = object [ "kind" .= String "ContractType" 183 | , "name" .= show c ] 184 | toJSON (PrimitiveType abiType) = object [ "kind" .= String "AbiType" 185 | , "abiType" .= toJSON abiType ] 186 | 187 | 188 | instance ToJSON AbiType where 189 | toJSON (AbiUIntType n) = object [ "type" .= String "UInt" 190 | , "size" .= String (T.pack $ show n) ] 191 | toJSON (AbiIntType n) = object [ "type" .= String "Int" 192 | , "size" .= String (T.pack $ show n) ] 193 | toJSON AbiAddressType = object [ "type" .= String "Address" ] 194 | toJSON AbiBoolType = object [ "type" .= String "Bool" ] 195 | toJSON (AbiBytesType n) = object [ "type" .= String "Bytes" 196 | , "size" .= String (T.pack $ show n) ] 197 | toJSON AbiBytesDynamicType = object [ "type" .= String "BytesDynamic" ] 198 | toJSON AbiStringType = object [ "type" .= String "String" ] 199 | toJSON (AbiArrayDynamicType t) = object [ "type" .= String "ArrayDynamic" 200 | , "arrayType" .= toJSON t ] 201 | toJSON (AbiArrayType n t) = object [ "type" .= String "Array" 202 | , "arrayType" .= toJSON t 203 | , "size" .= String (T.pack $ show n) ] 204 | toJSON (AbiTupleType ts) = object [ "type" .= String "Tuple" 205 | , "elemTypes" .= toJSON ts ] 206 | toJSON (AbiFunctionType) = object [ "type" .= String "Function" ] 207 | 208 | 209 | 210 | -- Create the string that is used to construct the function selector 211 | makeIface :: Interface -> String 212 | makeIface (Interface a decls) = 213 | a <> "(" <> intercalate "," (fmap (\(Decl typ _) -> show typ) decls) <> ")" 214 | -------------------------------------------------------------------------------- /src/Act/Traversals.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE RankNTypes #-} 2 | {-# LANGUAGE LambdaCase #-} 3 | 4 | module Act.Traversals (TraversableTerm(..)) where 5 | 6 | import Data.Functor.Identity 7 | import Act.Syntax.TimeAgnostic 8 | import Prelude hiding (LT, GT) 9 | 10 | -- | Generic operations over AST terms 11 | class TraversableTerm a where 12 | mapTermM :: Monad m => (forall b t . Exp b t -> m (Exp b t)) -> a -> m a 13 | 14 | mapTerm :: (forall b t . Exp b t -> Exp b t) -> a -> a 15 | mapTerm f e = runIdentity (mapTermM (Identity . f) e) 16 | 17 | instance TraversableTerm (Exp a t) where 18 | mapTermM = mapExpM 19 | 20 | instance TraversableTerm (TypedExp t) where 21 | mapTermM = mapTypedExpM 22 | 23 | instance TraversableTerm (TItem k a t) where 24 | mapTermM = mapTItemM 25 | 26 | instance TraversableTerm (Ref k t) where 27 | mapTermM = mapRefM 28 | 29 | mapExpM :: Monad m => (forall a . Exp a t -> m (Exp a t)) -> Exp b t -> m (Exp b t) 30 | mapExpM f = \case 31 | --booleans 32 | And p a b -> do 33 | a' <- mapExpM f a 34 | b' <- mapExpM f b 35 | f (And p a' b') 36 | Or p a b -> do 37 | a' <- mapExpM f a 38 | b' <- mapExpM f b 39 | f (Or p a' b') 40 | Impl p a b -> do 41 | a' <- mapExpM f a 42 | b' <- mapExpM f b 43 | f (Impl p a' b') 44 | Neg p a -> do 45 | a' <- mapExpM f a 46 | f (Neg p a') 47 | LT p a b -> do 48 | a' <- mapExpM f a 49 | b' <- mapExpM f b 50 | f (LT p a' b') 51 | LEQ p a b -> do 52 | a' <- mapExpM f a 53 | b' <- mapExpM f b 54 | f (LEQ p a' b') 55 | GEQ p a b -> do 56 | a' <- mapExpM f a 57 | b' <- mapExpM f b 58 | f (GEQ p a' b') 59 | GT p a b -> do 60 | a' <- mapExpM f a 61 | b' <- mapExpM f b 62 | f (GT p a' b') 63 | LitBool p a -> f (LitBool p a) 64 | 65 | --integers 66 | 67 | Add p a b -> do 68 | a' <- mapExpM f a 69 | b' <- mapExpM f b 70 | f (Add p a' b') 71 | Sub p a b -> do 72 | a' <- mapExpM f a 73 | b' <- mapExpM f b 74 | f (Sub p a' b') 75 | Mul p a b -> do 76 | a' <- mapExpM f a 77 | b' <- mapExpM f b 78 | f (Mul p a' b') 79 | Div p a b -> do 80 | a' <- mapExpM f a 81 | b' <- mapExpM f b 82 | f (Div p a' b') 83 | Mod p a b -> do 84 | a' <- mapExpM f a 85 | b' <- mapExpM f b 86 | f (Mod p a' b') 87 | Exp p a b -> do 88 | a' <- mapExpM f a 89 | b' <- mapExpM f b 90 | f (Exp p a' b') 91 | LitInt p a -> f (LitInt p a) 92 | IntEnv p a -> f (IntEnv p a) 93 | 94 | --bounds 95 | IntMin p a -> f (IntMin p a) 96 | IntMax p a -> f (IntMax p a) 97 | UIntMin p a -> f (UIntMin p a) 98 | UIntMax p a -> f (UIntMax p a) 99 | InRange p a b -> do 100 | b' <- mapExpM f b 101 | f (InRange p a b') 102 | 103 | --bytestrings 104 | 105 | Cat p a b -> do 106 | a' <- mapExpM f a 107 | b' <- mapExpM f b 108 | f (Cat p a' b') 109 | Slice p a b c -> do 110 | a' <- mapExpM f a 111 | b' <- mapExpM f b 112 | c' <- mapExpM f c 113 | f (Slice p a' b' c') 114 | ByStr p a -> f (ByStr p a) 115 | ByLit p a -> f (ByLit p a) 116 | ByEnv p a -> f (ByEnv p a) 117 | 118 | --contracts 119 | 120 | Create p n as -> do 121 | as' <- mapM (mapTypedExpM f) as 122 | f (Create p n as') 123 | 124 | --polymorphic 125 | 126 | Eq p s a b -> do 127 | a' <- mapExpM f a 128 | b' <- mapExpM f b 129 | f (Eq p s a' b') 130 | NEq p s a b -> do 131 | a' <- mapExpM f a 132 | b' <- mapExpM f b 133 | f (NEq p s a' b') 134 | 135 | ITE p a b c -> do 136 | a' <- mapExpM f a 137 | b' <- mapExpM f b 138 | c' <- mapExpM f c 139 | f (ITE p a' b' c') 140 | TEntry p t k i -> do 141 | i' <- mapTItemM f i 142 | f (TEntry p t k i') 143 | 144 | mapTypedExpM :: Monad m => (forall a . Exp a t -> m (Exp a t)) -> TypedExp t -> m (TypedExp t) 145 | mapTypedExpM f (TExp s e) = do 146 | e' <- f e 147 | pure $ TExp s e' 148 | 149 | mapTItemM :: Monad m => (forall a . Exp a t -> m (Exp a t)) -> TItem k b t -> m (TItem k b t) 150 | mapTItemM f (Item s v r) = do 151 | r' <- mapRefM f r 152 | pure $ Item s v r' 153 | 154 | mapRefM :: Monad m => (forall a . Exp a t -> m (Exp a t)) -> Ref k t -> m (Ref k t) 155 | mapRefM f = \case 156 | SVar p a b -> pure (SVar p a b) 157 | CVar p a b -> pure (CVar p a b) 158 | SMapping p a ts b -> do 159 | a' <- mapRefM f a 160 | b' <- mapM (mapTypedExpM f) b 161 | pure $ SMapping p a' ts b' 162 | SField p r a b -> do 163 | r' <- mapRefM f r 164 | pure $ SField p r' a b 165 | -------------------------------------------------------------------------------- /src/CLI/Main.hs: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Act.CLI qualified as CLI 4 | 5 | main :: IO () 6 | main = CLI.main 7 | -------------------------------------------------------------------------------- /src/Test/Decompile.hs: -------------------------------------------------------------------------------- 1 | {-# LANGUAGE QuasiQuotes #-} 2 | {-# LANGUAGE OverloadedStrings #-} 3 | 4 | module Decompile where 5 | 6 | import Test.Tasty 7 | import Test.Tasty.ExpectedFailure 8 | import Test.Tasty.HUnit 9 | import EVM.Solidity 10 | import EVM.Solvers 11 | import Data.List.NonEmpty qualified as NE 12 | import Data.Map qualified as Map 13 | import Data.Maybe 14 | import Data.String.Here 15 | import Data.Text (Text) 16 | import Data.Text.IO qualified as T 17 | import Data.Validation 18 | 19 | import Act.Decompile 20 | import Act.Print 21 | import Act.CLI 22 | import Act.HEVM_utils 23 | 24 | import qualified EVM.Solvers as Solvers 25 | import EVM.Effects 26 | 27 | decompilerTests :: TestTree 28 | decompilerTests = testGroup "decompiler" 29 | -- TODO allow empty behaviours in source Act 30 | [ expectFail $ testCase "noop" $ checkDecompilation "C" [i| 31 | contract C { 32 | function f() public {} 33 | } 34 | |] 35 | , testCase "implicit constructor" $ checkDecompilation "C" [i| 36 | contract C { 37 | uint x; 38 | function f(uint v) public { 39 | x = v; 40 | } 41 | } 42 | |] 43 | , testCase "simple storage write" $ checkDecompilation "C" [i| 44 | contract C { 45 | uint x = 0; 46 | function f(uint v) public { 47 | x = v; 48 | } 49 | } 50 | |] 51 | , testCase "checked addition" $ checkDecompilation "C" [i| 52 | contract C { 53 | uint v = 0; 54 | function f(uint x, uint y) public returns (uint) { 55 | return x + y; 56 | } 57 | } 58 | |] 59 | , testCase "checked subtraction" $ checkDecompilation "C" [i| 60 | contract C { 61 | uint v = 0; 62 | function f(uint x, uint y) public returns (uint) { 63 | return x - y; 64 | } 65 | } 66 | |] 67 | , testCase "checked multiplication" $ checkDecompilation "C" [i| 68 | contract C { 69 | uint v = 0; 70 | function f(uint x, uint y) public returns (uint) { 71 | return x * y; 72 | } 73 | } 74 | |] 75 | , testCase "checked division" $ checkDecompilation "C" [i| 76 | contract C { 77 | uint v = 0; 78 | function f(uint x, uint y) public returns (uint) { 79 | return x / y; 80 | } 81 | } 82 | |] 83 | , testCase "writing a complex expression to storage" $ checkDecompilation "C" [i| 84 | contract C { 85 | uint v = 0; 86 | function f(uint x, uint y) public returns (uint) { 87 | uint z = (x + y) * (x - y) / (x * y); 88 | v = z; 89 | return z; 90 | } 91 | } 92 | |] 93 | , expectFail $ testCase "branching" $ checkDecompilation "C" [i| 94 | contract C { 95 | uint v = 0; 96 | function f(bool b, uint x, uint y) public returns (uint) { 97 | if (b) { 98 | return x; 99 | } else { 100 | return y; 101 | } 102 | } 103 | } 104 | |] 105 | ] 106 | 107 | checkDecompilation :: Text -> Text -> Assertion 108 | checkDecompilation contract src = do 109 | json <- solc Solidity src 110 | let (Contracts sol, _, _) = fromJust $ readStdJSON json 111 | let c = fromJust $ Map.lookup ("hevm.sol:" <> contract) sol 112 | runEnv (Env defaultActConfig) (Solvers.withSolvers CVC5 1 1 (Just 100000000) (decompile c)) >>= \case 113 | Left es -> do 114 | T.putStrLn es 115 | assertBool "decompilation should succeed" False 116 | Right s -> do 117 | case compile (prettyAct s) of 118 | Failure es -> do 119 | prettyErrs (prettyAct s) (NE.toList es) 120 | assertBool "decompiled output does not typecheck" False 121 | Success _ -> do 122 | assertBool "" True 123 | -------------------------------------------------------------------------------- /tests/coq/ERC20/.gitignore: -------------------------------------------------------------------------------- 1 | ERC20.v 2 | -------------------------------------------------------------------------------- /tests/coq/ERC20/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: verify 2 | verify: CoqMakefile ERC20.v 3 | make -f CoqMakefile 4 | 5 | ERC20.v: erc20.act 6 | act coq --file erc20.act > ERC20.v 7 | 8 | CoqMakefile: _CoqProject 9 | coq_makefile -f _CoqProject -o CoqMakefile 10 | 11 | .PHONY: clean 12 | clean: 13 | if [[ -f CoqMakefile ]]; then make -f CoqMakefile clean; fi 14 | rm -f StateMachine.v CoqMakefile CoqMakefile.conf 15 | rm -f *.glob *.vo *.vok *.vos 16 | -------------------------------------------------------------------------------- /tests/coq/ERC20/_CoqProject: -------------------------------------------------------------------------------- 1 | -Q . ERC20 2 | -Q ../../../lib ActLib 3 | 4 | ../../../lib/ActLib.v 5 | ERC20.v 6 | Theory.v -------------------------------------------------------------------------------- /tests/coq/ERC20/erc20.act: -------------------------------------------------------------------------------- 1 | constructor of Token 2 | interface constructor(uint _totalSupply) 3 | 4 | creates 5 | 6 | uint256 totalSupply := _totalSupply 7 | mapping(address => uint) balanceOf := [CALLER := _totalSupply] 8 | mapping(address=>mapping(address=>uint)) allowance := [] 9 | 10 | 11 | behaviour transfer of Token 12 | interface transfer(uint256 value, address to) 13 | 14 | iff 15 | 16 | CALLVALUE == 0 17 | value <= balanceOf[CALLER] 18 | CALLER =/= to => balanceOf[to] + value < 2^256 19 | 20 | case CALLER =/= to: 21 | 22 | storage 23 | 24 | balanceOf[CALLER] => balanceOf[CALLER] - value 25 | balanceOf[to] => balanceOf[to] + value 26 | 27 | returns 1 28 | 29 | case CALLER == to: 30 | 31 | returns 1 32 | 33 | 34 | behaviour transferFrom of Token 35 | interface transferFrom(address src, address dst, uint amount) 36 | 37 | iff 38 | 39 | amount <= balanceOf[CALLER] 40 | src =/= dst => balanceOf[dst] + amount < 2^256 41 | CALLER =/= src => 0 <= allowance[src][CALLER] - amount 42 | CALLVALUE == 0 43 | 44 | case src =/= dst and CALLER == src: 45 | 46 | storage 47 | 48 | balanceOf[src] => balanceOf[src] - amount 49 | balanceOf[dst] => balanceOf[dst] + amount 50 | 51 | returns 1 52 | 53 | case src =/= dst and CALLER =/= src and allowance[src][CALLER] == 2^256 - 1: 54 | 55 | storage 56 | 57 | balanceOf[src] => balanceOf[src] - amount 58 | balanceOf[dst] => balanceOf[dst] + amount 59 | 60 | returns 1 61 | 62 | case src =/= dst and CALLER =/= src and allowance[src][CALLER] < 2^256 - 1: 63 | 64 | storage 65 | 66 | allowance[src][CALLER] => allowance[src][CALLER] - amount 67 | balanceOf[src] => balanceOf[src] - amount 68 | balanceOf[dst] => balanceOf[dst] + amount 69 | 70 | returns 1 71 | 72 | case src == dst: 73 | 74 | returns 1 -------------------------------------------------------------------------------- /tests/coq/exponent/.gitignore: -------------------------------------------------------------------------------- 1 | Exponent.v 2 | -------------------------------------------------------------------------------- /tests/coq/exponent/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: verify 2 | verify: CoqMakefile Exponent.v 3 | make -f CoqMakefile 4 | 5 | Exponent.v: exponent.act 6 | act coq --file exponent.act > Exponent.v 7 | 8 | CoqMakefile: _CoqProject 9 | coq_makefile -f _CoqProject -o CoqMakefile 10 | 11 | .PHONY: clean 12 | clean: 13 | if [[ -f CoqMakefile ]]; then make -f CoqMakefile clean; fi 14 | rm -f Exponent.v CoqMakefile CoqMakefile.conf 15 | rm -f *.glob *.vo *.vok *.vos 16 | -------------------------------------------------------------------------------- /tests/coq/exponent/Theory.v: -------------------------------------------------------------------------------- 1 | Require Import Exponent.Exponent. 2 | Require Import ActLib.ActLib. 3 | Require Import Coq.ZArith.ZArith. 4 | Open Scope Z_scope. 5 | 6 | Import Exponent. 7 | 8 | Lemma pow_pred : forall a e, 0 < e -> a * a ^ (Z.pred e) = a ^ e. 9 | Proof. 10 | intros a e Hlt. 11 | apply eq_sym. 12 | replace (a ^ e) with (a ^ (Z.succ (Z.pred e))). 13 | - apply Z.pow_succ_r. 14 | apply Zlt_0_le_0_pred. 15 | assumption. 16 | - rewrite (Z.succ_pred e). 17 | reflexivity. 18 | Qed. 19 | 20 | Lemma invariant : forall base s, 21 | reachable base s -> (r s) * (b s) ^ ((e s) - 1) = (b base) ^ (e base). 22 | Proof. 23 | intros base s H. induction H. 24 | - simpl. 25 | rewrite Z.sub_1_r. 26 | apply pow_pred. 27 | apply Z.gt_lt. 28 | assumption. 29 | - simpl. 30 | rewrite <- IHreachable. 31 | rewrite Z.sub_1_r. 32 | rewrite <- (pow_pred (b STATE) (e STATE - 1)). 33 | + rewrite Z.mul_assoc. reflexivity. 34 | + apply Z.gt_lt in H1. 35 | apply (proj1 (Z.sub_lt_mono_r 1 (e STATE) 1)). 36 | assumption. 37 | Qed. 38 | 39 | Theorem exp_correct : forall base s, 40 | reachable base s -> e s = 1 -> r s = (b base) ^ (e base). 41 | Proof. 42 | intros base s H He. 43 | apply invariant in H. 44 | rewrite He in H. simpl in H. 45 | rewrite (Z.mul_1_r (r s)) in H. 46 | assumption. 47 | Qed. Check exp_correct. 48 | -------------------------------------------------------------------------------- /tests/coq/exponent/_CoqProject: -------------------------------------------------------------------------------- 1 | -Q . Exponent 2 | -Q ../../../lib ActLib 3 | 4 | ../../../lib/ActLib.v 5 | Exponent.v 6 | Theory.v 7 | -------------------------------------------------------------------------------- /tests/coq/exponent/exponent.act: -------------------------------------------------------------------------------- 1 | constructor of Exponent 2 | interface constructor(uint _b, uint _e) 3 | 4 | iff 5 | 6 | _e > 0 7 | 8 | creates 9 | 10 | uint b := _b 11 | uint e := _e 12 | uint r := _b 13 | 14 | 15 | behaviour exp of Exponent 16 | interface exp() 17 | 18 | iff 19 | 20 | e > 1 21 | 22 | iff in range uint 23 | 24 | r * b 25 | e - 1 26 | 27 | storage 28 | 29 | r => r * b 30 | e => e - 1 -------------------------------------------------------------------------------- /tests/coq/exponent/exponent.sol: -------------------------------------------------------------------------------- 1 | // implementation of an exponentiation process 2 | 3 | contract Exponent { 4 | 5 | uint b; 6 | uint e; 7 | uint r; 8 | 9 | constructor(uint _b, uint _e) { 10 | require(e > 0); 11 | 12 | b = _b; 13 | e = _e; 14 | r = _b; 15 | } 16 | 17 | function exp() public { 18 | require(e > 1); 19 | require(b == 0 || (r * b) / b == r); // safe mul 20 | require(e - 1 <= e); // safe sub 21 | 22 | r = r * b; 23 | e = e - 1; 24 | } 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /tests/coq/multi/.gitignore: -------------------------------------------------------------------------------- 1 | Multi.v 2 | -------------------------------------------------------------------------------- /tests/coq/multi/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: verify 2 | verify: CoqMakefile Multi.v 3 | make -f CoqMakefile 4 | 5 | Multi.v: multi.act 6 | act coq --file multi.act > Multi.v 7 | 8 | CoqMakefile: _CoqProject 9 | coq_makefile -f _CoqProject -o CoqMakefile 10 | 11 | .PHONY: clean 12 | clean: 13 | if [[ -f CoqMakefile ]]; then make -f CoqMakefile clean; fi 14 | rm -f Exponent.v CoqMakefile CoqMakefile.conf 15 | rm -f *.glob *.vo *.vok *.vos 16 | -------------------------------------------------------------------------------- /tests/coq/multi/Theory.v: -------------------------------------------------------------------------------- 1 | Require Import Multi.Multi. 2 | Require Import Coq.ZArith.ZArith. 3 | Require Import ActLib.ActLib. 4 | 5 | 6 | Import C. 7 | 8 | 9 | Theorem reachable_value_f S0 S: 10 | reachable S0 S -> 11 | forall x, A.f (B.a (b S)) x = 0 \/ A.f (B.a (b S)) x = 2. 12 | Proof. 13 | intros HR x. induction HR. 14 | - simpl; eauto. 15 | 16 | - simpl. destruct (x =? i). 17 | + eauto. 18 | + eauto. 19 | 20 | - eauto. 21 | Qed. 22 | 23 | Theorem reachable_value_x S0 S: 24 | reachable S0 S -> 25 | w S = 0 \/ w S = 1. 26 | Proof. 27 | intros HR. induction HR; simpl; eauto. 28 | Qed. 29 | -------------------------------------------------------------------------------- /tests/coq/multi/_CoqProject: -------------------------------------------------------------------------------- 1 | -Q . Multi 2 | -Q ../../../lib ActLib 3 | 4 | ../../../lib/ActLib.v 5 | Multi.v 6 | Theory.v 7 | -------------------------------------------------------------------------------- /tests/coq/multi/multi.act: -------------------------------------------------------------------------------- 1 | //Contract B 2 | constructor of B 3 | interface constructor(uint u) 4 | 5 | creates 6 | uint x := u 7 | A a := create A() 8 | 9 | behaviour setx of B 10 | interface setx(uint z) 11 | 12 | storage 13 | 14 | a.x => z 15 | 16 | behaviour setf of B 17 | interface setf(address i) 18 | 19 | storage 20 | 21 | a.f[i] => 1 22 | 23 | // Contract A 24 | constructor of A 25 | interface constructor() 26 | 27 | creates 28 | uint x := 0 29 | mapping(address => uint) f := [] 30 | 31 | //Contract C 32 | constructor of C 33 | interface constructor() 34 | 35 | creates 36 | uint w := 0 37 | B b := create B(42) 38 | 39 | behaviour setf of C 40 | interface setf(address i) 41 | 42 | storage 43 | 44 | b.a.f[i] => 2 45 | 46 | behaviour setxw of C 47 | interface setxw(address i) 48 | 49 | storage 50 | 51 | b.a.x => 1 52 | b.x => 1 53 | -------------------------------------------------------------------------------- /tests/coq/safemath/.gitignore: -------------------------------------------------------------------------------- 1 | SafeMath.v 2 | -------------------------------------------------------------------------------- /tests/coq/safemath/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: verify 2 | verify: CoqMakefile SafeMath.v 3 | make -f CoqMakefile 4 | 5 | SafeMath.v: safemath.act 6 | act coq --file safemath.act > SafeMath.v 7 | 8 | CoqMakefile: _CoqProject 9 | coq_makefile -f _CoqProject -o CoqMakefile 10 | 11 | .PHONY: clean 12 | clean: 13 | if [[ -f CoqMakefile ]]; then make -f CoqMakefile clean; fi 14 | rm -f SafeMath.v CoqMakefile CoqMakefile.conf 15 | rm -f *.glob *.vo *.vok *.vos 16 | -------------------------------------------------------------------------------- /tests/coq/safemath/Theory.v: -------------------------------------------------------------------------------- 1 | Require Import SafeMath.SafeMath. 2 | Require Import ActLib.ActLib. 3 | Require Import Coq.ZArith.ZArith. 4 | Open Scope Z_scope. 5 | 6 | Import SafeMath. 7 | 8 | (* trivial observation that there is only one possible state *) 9 | Lemma state_constant : forall s, s = state. 10 | Proof. 11 | intros. 12 | destruct s. 13 | reflexivity. 14 | Qed. 15 | 16 | Theorem mul_correct : forall e s x y, 17 | range256 x /\ range256 y /\ range256 (x * y) <-> mul0_ret e s x y (x * y). 18 | Proof. 19 | intros. 20 | split. { 21 | intros. 22 | destruct H as [Hx [Hy Hxy]]. 23 | apply mul0_ret_intro. 24 | - assumption. 25 | - assumption. 26 | - assumption. 27 | - assumption. 28 | - assumption. 29 | - eauto. 30 | } { 31 | intros. destruct H. 32 | split. assumption. 33 | split. assumption. assumption. 34 | } 35 | Qed. Check mul_correct. 36 | 37 | 38 | Theorem mul_is_mul : 39 | forall e s x y z, 40 | mul0_ret e s x y z -> 41 | z = x * y. 42 | Proof. 43 | intros. inversion H. 44 | reflexivity. 45 | Qed. 46 | -------------------------------------------------------------------------------- /tests/coq/safemath/_CoqProject: -------------------------------------------------------------------------------- 1 | -Q . SafeMath 2 | -Q ../../../lib ActLib 3 | 4 | ../../../lib/ActLib.v 5 | SafeMath.v 6 | Theory.v 7 | -------------------------------------------------------------------------------- /tests/coq/safemath/safemath.act: -------------------------------------------------------------------------------- 1 | constructor of SafeMath 2 | interface constructor() 3 | 4 | behaviour add of SafeMath 5 | interface add(uint256 x, uint256 y) 6 | 7 | iff in range uint256 8 | x + y 9 | 10 | returns x + y 11 | 12 | behaviour mul of SafeMath 13 | interface mul(uint256 x, uint256 y) 14 | 15 | iff in range uint256 16 | x * y 17 | 18 | returns x * y 19 | -------------------------------------------------------------------------------- /tests/coq/token/.gitignore: -------------------------------------------------------------------------------- 1 | Token.v 2 | -------------------------------------------------------------------------------- /tests/coq/token/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: verify 2 | verify: CoqMakefile Token.v 3 | make -f CoqMakefile 4 | 5 | Token.v: token.act 6 | act coq --file token.act > Token.v 7 | 8 | CoqMakefile: _CoqProject 9 | coq_makefile -f _CoqProject -o CoqMakefile 10 | 11 | .PHONY: clean 12 | clean: 13 | if [[ -f CoqMakefile ]]; then make -f CoqMakefile clean; fi 14 | rm -f StateMachine.v CoqMakefile CoqMakefile.conf 15 | rm -f *.glob *.vo *.vok *.vos 16 | -------------------------------------------------------------------------------- /tests/coq/token/Theory.v: -------------------------------------------------------------------------------- 1 | Require Import Coq.ZArith.ZArith. 2 | Require Import ActLib.ActLib. 3 | Require Coq.Strings.String. 4 | Require Import Lia. 5 | 6 | 7 | Require Import Token.Token. 8 | 9 | Import Token. 10 | 11 | (* Address should be Z or N? Or int20? *) 12 | 13 | Definition MAX_ADDRESS := UINT_MAX 160. 14 | 15 | 16 | Fixpoint balances_sum' (balances : address -> Z) (n : nat) (acc : Z) : Z := 17 | match n with 18 | | O => balances 0 + acc 19 | | S n => balances_sum' balances n (acc + balances (Z.of_nat (S n))) 20 | end. 21 | 22 | Definition balances_sum (STATE : State) := 23 | balances_sum' (balances STATE) (Z.to_nat MAX_ADDRESS) 0. 24 | 25 | 26 | Definition transfer_from map (from : address) (amt : Z) := 27 | fun b => if b =? from then map from - amt else map b. 28 | 29 | Definition transfer_to map (from : address) (amt : Z) := 30 | fun b => if b =? from then map from + amt else map b. 31 | 32 | Definition transfer map from to amt := transfer_to (transfer_from map from amt) to amt. 33 | 34 | Lemma balances_sum_f_eq f f' addr acc : 35 | (forall x, x <= Z.of_nat addr -> f x = f' x) -> 36 | balances_sum' f addr acc = balances_sum' f' addr acc. 37 | Proof. 38 | revert acc. induction addr; intros acc Hyp. 39 | - simpl. rewrite Hyp. reflexivity. lia. 40 | - simpl. rewrite IHaddr. rewrite Hyp. reflexivity. 41 | lia. intros. eapply Hyp. lia. 42 | Qed. 43 | 44 | Lemma balances_sum_acc f addr acc z : 45 | balances_sum' f addr (acc + z) = balances_sum' f addr acc + z. 46 | Proof. 47 | revert z acc. induction addr; intros z acc. 48 | - simpl. lia. 49 | - simpl. rewrite !IHaddr. lia. 50 | Qed. 51 | 52 | Opaque Z.sub Z.add Z.of_nat. 53 | 54 | 55 | Lemma balances_sum_thm x f f' addr acc : 56 | (forall y, x <> y -> f y = f' y) -> 57 | 0 <= x -> 58 | balances_sum' f addr acc = 59 | if (Z.to_nat x <=? addr)%nat then balances_sum' f' addr acc - f' x + f x else balances_sum' f' addr acc. 60 | Proof. 61 | revert acc. induction addr; intros acc Hyp Hleq1. 62 | - simpl. destruct (0 =? x) eqn:Heq. 63 | + eapply Z.eqb_eq in Heq. subst. 64 | simpl. lia. 65 | + eapply Z.eqb_neq in Heq. 66 | assert (Hbeq : (Z.to_nat x <=? 0)%nat = false). 67 | { eapply leb_correct_conv. lia. } 68 | rewrite Hbeq. rewrite Hyp. reflexivity. eauto. 69 | 70 | - destruct (Z.to_nat x <=? S addr)%nat eqn:Hleq. 71 | + eapply Nat.leb_le in Hleq. 72 | destruct (Z.of_nat (S addr) =? x) eqn:Heqb. 73 | * eapply Z.eqb_eq in Heqb. simpl. rewrite Heqb. 74 | erewrite balances_sum_f_eq with (f' := f'). 75 | rewrite !balances_sum_acc. lia. 76 | 77 | intros. eapply Hyp. lia. 78 | 79 | * simpl. 80 | destruct ((Z.to_nat x <=? addr)%nat) eqn:Hleq'. 81 | -- rewrite IHaddr; eauto. rewrite Hyp. reflexivity. 82 | intros Heq; subst. lia. 83 | -- eapply Z.eqb_neq in Heqb. 84 | eapply Nat.leb_gt in Hleq'. lia. 85 | 86 | + simpl. eapply leb_complete_conv in Hleq. 87 | erewrite balances_sum_f_eq with (f' := f'). 88 | rewrite Hyp. reflexivity. 89 | * intros Heq; subst. lia. 90 | * intros. rewrite Hyp. reflexivity. 91 | intros Heq; subst. lia. 92 | Qed. 93 | 94 | 95 | Lemma balances_sum_transfer_from map from amt addr acc : 96 | 0 <= from -> 97 | balances_sum' (transfer_from map from amt) addr acc = 98 | if (Z.to_nat from <=? addr)%nat then balances_sum' map addr acc - amt else balances_sum' map addr acc. 99 | Proof. 100 | intros Hleq1. 101 | erewrite balances_sum_thm with (f := transfer_from map from amt) (f' := map) (x := from); eauto. 102 | 103 | - destruct (Z.to_nat from <=? addr)%nat eqn:Hleq. 104 | 105 | unfold transfer_from. rewrite Z.eqb_refl. lia. 106 | 107 | reflexivity. 108 | 109 | - intros. unfold transfer_from. eapply Z.eqb_neq in H. 110 | rewrite Z.eqb_sym, H. reflexivity. 111 | Qed. 112 | 113 | Lemma balances_sum_transfer_to map to amt addr acc : 114 | 0 <= to -> 115 | balances_sum' (transfer_to map to amt) addr acc = 116 | if (Z.to_nat to <=? addr)%nat then balances_sum' map addr acc + amt else balances_sum' map addr acc. 117 | Proof. 118 | intros Hleq1. 119 | erewrite balances_sum_thm with (f := transfer_to map to amt) (f' := map) (x := to); eauto. 120 | 121 | - destruct (Z.to_nat to <=? addr)%nat eqn:Hleq. 122 | 123 | unfold transfer_to. rewrite Z.eqb_refl. lia. 124 | 125 | reflexivity. 126 | 127 | - intros. unfold transfer_to. eapply Z.eqb_neq in H. 128 | rewrite Z.eqb_sym, H. reflexivity. 129 | Qed. 130 | 131 | 132 | Theorem transfer_thm map from to amt addr acc: 133 | to <> from -> 134 | 0 <= from <= Z.of_nat addr -> 135 | 0 <= to <= Z.of_nat addr -> 136 | balances_sum' (transfer map from to amt) addr acc = balances_sum' map addr acc. 137 | Proof. 138 | intros Hneq Hleq1 Hleq2. 139 | unfold transfer. 140 | 141 | rewrite balances_sum_transfer_to; [ | lia ]. 142 | rewrite leb_correct; [ | lia ]. 143 | 144 | rewrite balances_sum_transfer_from; [ | lia ]. 145 | rewrite leb_correct; [ | lia ]. 146 | 147 | lia. 148 | Qed. 149 | 150 | 151 | Theorem constant_balances : forall BASE STATE, 152 | reachable BASE STATE -> 153 | balances_sum BASE = balances_sum STATE. 154 | Proof. 155 | intros BASE S Hreach. induction Hreach; intros. 156 | - reflexivity. 157 | - rewrite IHHreach. unfold transfer0. 158 | unfold balances_sum. simpl. 159 | 160 | erewrite <- transfer_thm. 161 | 162 | + unfold transfer, transfer_to, transfer_from. 163 | eapply not_eq_sym in H. eapply Z.eqb_neq in H. 164 | rewrite H. reflexivity. 165 | 166 | + eauto. 167 | + rewrite Z2Nat.id. assumption. 168 | unfold MAX_ADDRESS. unfold UINT_MAX. lia. 169 | 170 | + rewrite Z2Nat.id. assumption. 171 | unfold MAX_ADDRESS. unfold UINT_MAX. lia. 172 | 173 | - unfold transfer1. rewrite IHHreach. 174 | reflexivity. 175 | Qed. 176 | -------------------------------------------------------------------------------- /tests/coq/token/_CoqProject: -------------------------------------------------------------------------------- 1 | -Q . Token 2 | -Q ../../../lib ActLib 3 | 4 | ../../../lib/ActLib.v 5 | Token.v 6 | Theory.v -------------------------------------------------------------------------------- /tests/coq/token/token.act: -------------------------------------------------------------------------------- 1 | constructor of Token 2 | interface constructor (address a, uint256 amt) 3 | 4 | creates 5 | mapping(address => uint256) balances := [a := amt] 6 | 7 | 8 | behaviour transfer of Token 9 | interface transfer(address to, uint256 amt) 10 | 11 | 12 | iff 13 | 14 | amt <= balances[CALLER] 15 | 16 | 17 | iff in range uint256 18 | 19 | balances[to] + amt 20 | 21 | 22 | case CALLER =/= to: 23 | 24 | storage 25 | 26 | balances[CALLER] => balances[CALLER] - amt 27 | balances[to] => balances[to] + amt 28 | 29 | returns 1 30 | 31 | 32 | case CALLER == to: 33 | 34 | returns 1 -------------------------------------------------------------------------------- /tests/coq/transitions/.gitignore: -------------------------------------------------------------------------------- 1 | StateMachine.v 2 | -------------------------------------------------------------------------------- /tests/coq/transitions/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: verify 2 | verify: CoqMakefile StateMachine.v 3 | make -f CoqMakefile 4 | 5 | StateMachine.v: state_machine.act 6 | act coq --file state_machine.act > StateMachine.v 7 | 8 | CoqMakefile: _CoqProject 9 | coq_makefile -f _CoqProject -o CoqMakefile 10 | 11 | .PHONY: clean 12 | clean: 13 | if [[ -f CoqMakefile ]]; then make -f CoqMakefile clean; fi 14 | rm -f StateMachine.v CoqMakefile CoqMakefile.conf 15 | rm -f *.glob *.vo *.vok *.vos 16 | -------------------------------------------------------------------------------- /tests/coq/transitions/Theory.v: -------------------------------------------------------------------------------- 1 | (* depends on StateMachine.v *) 2 | 3 | Require Import StateMachine.StateMachine. 4 | Require Import ActLib.ActLib. 5 | Require Import Coq.ZArith.ZArith. 6 | Open Scope Z_scope. 7 | 8 | Import StateMachine. 9 | 10 | Theorem invariant : forall BASE s, reachable BASE s -> (x s) >= 0 /\ (x s) <= 2. 11 | Proof. 12 | intros. induction H. { 13 | simpl. split. 14 | - intros contra. discriminate. 15 | - intros contra. discriminate. 16 | } { 17 | simpl. split. 18 | - intros contra. discriminate. 19 | - intros contra. discriminate. 20 | } { 21 | simpl. split. 22 | - intros contra. discriminate. 23 | - intros contra. discriminate. 24 | } { 25 | simpl. split. 26 | - intros contra. discriminate. 27 | - intros contra. discriminate. 28 | } 29 | Qed. Check invariant. 30 | 31 | -------------------------------------------------------------------------------- /tests/coq/transitions/_CoqProject: -------------------------------------------------------------------------------- 1 | -Q . StateMachine 2 | -Q ../../../lib ActLib 3 | 4 | ../../../lib/ActLib.v 5 | StateMachine.v 6 | Theory.v 7 | -------------------------------------------------------------------------------- /tests/coq/transitions/state_machine.act: -------------------------------------------------------------------------------- 1 | constructor of StateMachine 2 | interface constructor() 3 | 4 | creates 5 | uint256 x := 0 6 | 7 | invariants 8 | x <= 2 9 | 10 | behaviour f of StateMachine 11 | interface f() 12 | 13 | iff x == 0 14 | 15 | storage 16 | x => 1 17 | 18 | behaviour g of StateMachine 19 | interface g() 20 | 21 | iff x == 1 22 | 23 | storage 24 | x => 2 25 | 26 | behaviour h of StateMachine 27 | interface h() 28 | 29 | iff x == 2 30 | 31 | storage 32 | x => 0 33 | -------------------------------------------------------------------------------- /tests/coq/transitions/state_machine.smt2: -------------------------------------------------------------------------------- 1 | (define-fun f ((x_pre Int) (x_post Int)) Bool 2 | (= x_post (ite (= x_pre 0) 1 x_pre)) 3 | ) 4 | (define-fun g ((x_pre Int) (x_post Int)) Bool 5 | (= x_post (ite (= x_pre 1) 2 x_pre)) 6 | ) 7 | (define-fun h ((x_pre Int) (x_post Int)) Bool 8 | (= x_post (ite (= x_pre 2) 0 x_pre)) 9 | ) 10 | 11 | (define-fun init ((x Int)) Bool (= x 0)) 12 | (define-fun inv ((x Int)) Bool (<= x 2)) 13 | 14 | (declare-const x_pre Int) 15 | (declare-const x_post Int) 16 | 17 | (assert (and 18 | (or 19 | (and 20 | (init x_pre) 21 | (not (inv x_pre)) 22 | ) 23 | (and 24 | (<= 0 x_pre) 25 | (inv x_pre) 26 | (or 27 | (f x_pre x_post) 28 | (g x_pre x_post) 29 | (h x_pre x_post) 30 | ) 31 | (not (inv x_post)) 32 | ) 33 | ) 34 | )) 35 | 36 | (check-sat) 37 | -------------------------------------------------------------------------------- /tests/coq/transitions/state_machine.sol: -------------------------------------------------------------------------------- 1 | contract StateMachine { 2 | uint x; 3 | 4 | function f() public { 5 | require(x == 0); 6 | x = 1; 7 | } 8 | 9 | function g() public { 10 | require(x == 1); 11 | x = 2; 12 | } 13 | 14 | function h() public { 15 | require(x == 2); 16 | x = 0; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/frontend/env/all_env_vars.act: -------------------------------------------------------------------------------- 1 | constructor of AllEnvVars 2 | interface constructor() 3 | 4 | creates 5 | address caller := CALLER 6 | uint value := CALLVALUE 7 | uint calldepth := CALLDEPTH 8 | address origin := ORIGIN 9 | bytes32 blockhash := BLOCKHASH 10 | uint blocknumber := BLOCKNUMBER 11 | uint difficulty := DIFFICULTY 12 | uint chainid := CHAINID 13 | uint gaslimit := GASLIMIT 14 | address coinbase := COINBASE 15 | uint timestamp := TIMESTAMP 16 | address this := THIS 17 | uint nonce := NONCE 18 | -------------------------------------------------------------------------------- /tests/frontend/fail/case-fail1.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor() 3 | 4 | creates 5 | uint x := 0 6 | 7 | behaviour bar of C 8 | interface bar(uint z) 9 | 10 | iff 11 | CALLVALUE == 0 12 | 13 | case z == 0: 14 | 15 | storage 16 | x => z 17 | 18 | case z >= 2: 19 | 20 | storage 21 | x => z + 1 -------------------------------------------------------------------------------- /tests/frontend/fail/case-fail2.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor() 3 | 4 | creates 5 | uint x := 0 6 | 7 | behaviour bar of C 8 | interface bar(uint z) 9 | 10 | iff 11 | CALLVALUE == 0 12 | 13 | case z == 0: 14 | 15 | storage 16 | x => z 17 | 18 | case z >= 1: 19 | 20 | storage 21 | x => z + 1 -------------------------------------------------------------------------------- /tests/frontend/fail/cast.act: -------------------------------------------------------------------------------- 1 | constructor of A 2 | interface constructor(uint w1) 3 | 4 | iff 5 | CALLVALUE == 0 6 | 7 | creates 8 | uint w := w1 9 | 10 | constructor of B 11 | interface constructor(uint z1) 12 | 13 | iff 14 | CALLVALUE == 0 15 | 16 | creates 17 | uint z := z1 18 | 19 | constructor of C 20 | interface constructor(address a1, address a2) 21 | 22 | pointers 23 | a1 |-> A 24 | a2 |-> B 25 | 26 | iff 27 | CALLVALUE == 0 28 | 29 | creates 30 | A a := a1 31 | B b := a2 32 | 33 | constructor of D 34 | interface constructor(address a1, address a2) 35 | 36 | pointers 37 | a1 |-> A 38 | 39 | iff 40 | CALLVALUE == 0 41 | 42 | creates 43 | C c1 := create C(a1, a2) 44 | -------------------------------------------------------------------------------- /tests/frontend/fail/cycle.act: -------------------------------------------------------------------------------- 1 | constructor of A 2 | interface constructor() 3 | 4 | creates 5 | uint x := 0 6 | B b := create B() 7 | 8 | constructor of B 9 | interface constructor() 10 | 11 | creates 12 | uint y := 0 13 | A a := create A() 14 | 15 | behaviour remote of B 16 | interface set_remote(uint z) 17 | 18 | iff 19 | CALLVALUE == 0 20 | 21 | storage 22 | a.x => z 23 | 24 | behaviour multi of B 25 | interface set_remote2(uint z) 26 | 27 | iff 28 | CALLVALUE == 0 29 | 30 | storage 31 | y => 1 32 | a.x => z 33 | -------------------------------------------------------------------------------- /tests/frontend/fail/cycle2.act: -------------------------------------------------------------------------------- 1 | // Call graph: 2 | // 3 | // C 4 | // / \ 5 | // A B 6 | // \ 7 | // D 8 | // \ 9 | // C 10 | 11 | constructor of A 12 | interface constructor() 13 | 14 | creates 15 | uint x := 0 16 | 17 | constructor of B 18 | interface constructor(uint x) 19 | 20 | creates 21 | uint y := x 22 | D d := create D() 23 | 24 | behaviour remote of B 25 | interface set_remote(uint z) 26 | 27 | iff 28 | CALLVALUE == 0 29 | 30 | storage 31 | d.x => z 32 | 33 | constructor of C 34 | interface constructor(uint u) 35 | 36 | creates 37 | B b := create B(u) 38 | A a := create A() 39 | 40 | constructor of D 41 | interface constructor() 42 | 43 | creates 44 | C c := create C(0) 45 | uint x := 0 46 | -------------------------------------------------------------------------------- /tests/frontend/fail/emptystorage/circular.act: -------------------------------------------------------------------------------- 1 | constructor of LValue 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint x := y 7 | uint y := x 8 | uint z := x 9 | -------------------------------------------------------------------------------- /tests/frontend/fail/emptystorage/mapkey.act: -------------------------------------------------------------------------------- 1 | constructor of ReadMappingIndex 2 | interface constructor(uint _x, uint _y) 3 | 4 | creates 5 | uint x := _x 6 | uint y := _y 7 | mapping(uint=>uint) m := [x := _y] 8 | -------------------------------------------------------------------------------- /tests/frontend/fail/emptystorage/mapkeyexp.act: -------------------------------------------------------------------------------- 1 | constructor of ReadMappingIndex 2 | interface constructor(uint _x, uint _y) 3 | 4 | creates 5 | uint x := _x 6 | uint y := _y 7 | mapping(uint=>uint) m := [x + 5 := _y] 8 | -------------------------------------------------------------------------------- /tests/frontend/fail/emptystorage/mapval.act: -------------------------------------------------------------------------------- 1 | constructor of ReadMapping 2 | interface constructor(uint _x, uint _y) 3 | 4 | creates 5 | uint x := _x 6 | uint y := _y 7 | mapping(uint=>uint) m := [_x := y] 8 | -------------------------------------------------------------------------------- /tests/frontend/fail/emptystorage/mapvalexp.act: -------------------------------------------------------------------------------- 1 | constructor of ReadMapping 2 | interface constructor(uint _x, uint _y) 3 | 4 | creates 5 | uint x := _x 6 | uint y := _y 7 | mapping(uint=>uint) m := [_x := y + 5] 8 | -------------------------------------------------------------------------------- /tests/frontend/fail/emptystorage/var.act: -------------------------------------------------------------------------------- 1 | constructor of LValue 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint x := x 7 | 8 | invariants 9 | 10 | x == 0 11 | -------------------------------------------------------------------------------- /tests/frontend/fail/emptystorage/varexp.act: -------------------------------------------------------------------------------- 1 | constructor of LValue 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint x := true and x 7 | 8 | invariants 9 | 10 | x == 0 11 | -------------------------------------------------------------------------------- /tests/frontend/fail/missingdef0.act: -------------------------------------------------------------------------------- 1 | constructor of Fail 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint x := 0 7 | uint y := 2 8 | 9 | // should fail to typecheck because y has not been declared in the storage block below... 10 | behaviour f of Fail 11 | interface f() 12 | 13 | storage 14 | 15 | x => y 16 | -------------------------------------------------------------------------------- /tests/frontend/fail/missingdef1.act: -------------------------------------------------------------------------------- 1 | constructor of Fail 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint x := 0 7 | uint y := 2 8 | 9 | // should fail to typecheck because x & y have not been declared in a storage block 10 | behaviour g of Fail 11 | interface g() 12 | 13 | returns x + y 14 | -------------------------------------------------------------------------------- /tests/frontend/fail/missingdef2.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint x := 1 7 | uint y := 1 8 | 9 | 10 | constructor of B 11 | interface constructor() 12 | 13 | creates 14 | 15 | uint a := 1 16 | uint b := 1 17 | 18 | behaviour f of B 19 | interface f() 20 | 21 | storage C 22 | 23 | x => y 24 | -------------------------------------------------------------------------------- /tests/frontend/fail/multipledef/multipledef.act: -------------------------------------------------------------------------------- 1 | constructor of LValue 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint x := 2 7 | bool x := false 8 | -------------------------------------------------------------------------------- /tests/frontend/fail/multipledef/threedefs.act: -------------------------------------------------------------------------------- 1 | constructor of LValue 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint x := 2 7 | bool x := false 8 | uint x := 3 9 | -------------------------------------------------------------------------------- /tests/frontend/fail/three_behaviours.act: -------------------------------------------------------------------------------- 1 | // This is wrong because we have two behviours with the same name 2 | constructor of C 3 | interface constructor(uint24 _x) 4 | 5 | creates 6 | 7 | uint24 x := _x 8 | 9 | behaviour increase of C 10 | interface increase(uint24 new_x) 11 | 12 | storage 13 | x 14 | 15 | returns pre(x) 16 | 17 | ensures 18 | 19 | post(x) >= pre(x) 20 | 21 | behaviour increase of C 22 | interface increase2(uint24 new_x) 23 | 24 | storage 25 | x 26 | 27 | returns new_x 28 | 29 | ensures 30 | 31 | post(x) >= pre(x) 32 | 33 | behaviour increase of C 34 | interface increase2(uint24 new_x) 35 | 36 | storage 37 | x 38 | 39 | returns new_x 40 | 41 | ensures 42 | 43 | post(x) >= pre(x) 44 | 45 | -------------------------------------------------------------------------------- /tests/frontend/fail/timed_calldata.act: -------------------------------------------------------------------------------- 1 | constructor of Fail 2 | interface constructor(uint y) 3 | 4 | creates 5 | 6 | uint x := y 7 | 8 | ensures 9 | 10 | post(x) >= pre(y) 11 | -------------------------------------------------------------------------------- /tests/frontend/fail/two_behaviours.act: -------------------------------------------------------------------------------- 1 | // This is wrong because we have two behviours with the same name 2 | constructor of C 3 | interface constructor(uint24 _x) 4 | 5 | creates 6 | 7 | uint24 x := _x 8 | 9 | behaviour increase of C 10 | interface increase(uint24 new_x) 11 | 12 | storage 13 | x 14 | 15 | returns pre(x) 16 | 17 | ensures 18 | 19 | post(x) >= pre(x) 20 | 21 | behaviour increase of C 22 | interface increase2(uint24 new_x) 23 | 24 | storage 25 | x 26 | 27 | returns new_x 28 | 29 | ensures 30 | 31 | post(x) >= pre(x) 32 | 33 | -------------------------------------------------------------------------------- /tests/frontend/fail/two_interfaces.act: -------------------------------------------------------------------------------- 1 | // This is wrong because we have two behviours for the same interface 2 | constructor of C 3 | interface constructor(uint24 _x) 4 | 5 | creates 6 | 7 | uint24 x := _x 8 | 9 | behaviour increase of C 10 | interface increase(uint24 new_x) 11 | 12 | storage 13 | x 14 | 15 | returns pre(x) 16 | 17 | ensures 18 | 19 | post(x) >= pre(x) 20 | 21 | behaviour increase2 of C 22 | interface increase(uint24 new_x) 23 | 24 | storage 25 | x 26 | 27 | returns new_x 28 | 29 | ensures 30 | 31 | post(x) >= pre(x) 32 | 33 | -------------------------------------------------------------------------------- /tests/frontend/fail/typecheck/integer_postcond.act: -------------------------------------------------------------------------------- 1 | constructor of Fail 2 | interface constructor(uint y) 3 | 4 | creates 5 | 6 | uint x := y 7 | 8 | ensures 9 | 10 | post(x) + y 11 | -------------------------------------------------------------------------------- /tests/frontend/fail/typecheck/mismatched_equality.act: -------------------------------------------------------------------------------- 1 | constructor of Fail 2 | interface constructor(string y) 3 | 4 | creates 5 | 6 | uint x := 0 7 | 8 | ensures 9 | 10 | post(x) == y 11 | -------------------------------------------------------------------------------- /tests/frontend/fail/typecheck/silly_types.act: -------------------------------------------------------------------------------- 1 | constructor of Pass 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint x := 0 7 | uint y := 2 8 | bool p := true 9 | bool q := false 10 | 11 | behaviour g of Pass 12 | interface g() 13 | 14 | iff in range uint256 15 | 16 | x + y 17 | 18 | storage 19 | 20 | // TODO: remove this hack once bug #81 is fixed... 21 | x => x 22 | y => (x + p) == (q > y) 23 | p => (x + p) and (q > y) 24 | q 25 | -------------------------------------------------------------------------------- /tests/frontend/fail/untimed_entry.act: -------------------------------------------------------------------------------- 1 | constructor of Fail 2 | interface constructor(uint y) 3 | 4 | creates 5 | 6 | uint x := y 7 | 8 | ensures 9 | 10 | x >= y 11 | -------------------------------------------------------------------------------- /tests/frontend/pass/array/array.act: -------------------------------------------------------------------------------- 1 | behaviour f of A 2 | interface f(address[2] xs) 3 | 4 | returns xs[1] -------------------------------------------------------------------------------- /tests/frontend/pass/array/array.act.parsed.hs: -------------------------------------------------------------------------------- 1 | Main [Contract (Definition (AlexPn 0 0 0) "A" constructor() [] [] (Creates []) [] []) [Transition (AlexPn 0 1 1) "f" "A" f(address[2] xs) [] [] (Direct (Post [] (Just (EUTEntry (EMapping (AlexPn 55 4 11) (EVar (AlexPn 53 4 9) "xs") [IntLit (AlexPn 56 4 12) 1]))))) []]] 2 | -------------------------------------------------------------------------------- /tests/frontend/pass/case/case.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor() 3 | 4 | creates 5 | uint x := 0 6 | 7 | behaviour bar of C 8 | interface bar(uint z) 9 | 10 | iff 11 | CALLVALUE == 0 12 | 13 | case z == 0: 14 | 15 | storage 16 | x => z 17 | 18 | case z == 1: 19 | 20 | storage 21 | x => z + 1 22 | 23 | case z > 1: 24 | 25 | storage 26 | x => z + 2 -------------------------------------------------------------------------------- /tests/frontend/pass/case/case.act.parsed.hs: -------------------------------------------------------------------------------- 1 | Main [Contract (Definition (AlexPn 15 1 16) "C" constructor() [] [] (Creates [AssignVal (StorageVar (AlexPn 58 5 9) uint256 "x") (IntLit (AlexPn 63 5 14) 0)]) [] []) [Transition (AlexPn 67 7 1) "bar" "C" bar(uint256 z) [] [Iff (AlexPn 109 10 1) [EEq (AlexPn 126 11 14) (EnvExp (AlexPn 116 11 4) Callvalue) (IntLit (AlexPn 129 11 17) 0)]] (Branches [Case (AlexPn 132 13 1) (EEq (AlexPn 139 13 8) (EUTEntry (EVar (AlexPn 137 13 6) "z")) (IntLit (AlexPn 142 13 11) 0)) (Post [Rewrite (EVar (AlexPn 156 16 3) "x") (EUTEntry (EVar (AlexPn 161 16 8) "z"))] Nothing),Case (AlexPn 164 18 1) (EEq (AlexPn 171 18 8) (EUTEntry (EVar (AlexPn 169 18 6) "z")) (IntLit (AlexPn 174 18 11) 1)) (Post [Rewrite (EVar (AlexPn 188 21 3) "x") (EAdd (AlexPn 195 21 10) (EUTEntry (EVar (AlexPn 193 21 8) "z")) (IntLit (AlexPn 197 21 12) 1))] Nothing),Case (AlexPn 200 23 1) (EGT (AlexPn 207 23 8) (EUTEntry (EVar (AlexPn 205 23 6) "z")) (IntLit (AlexPn 209 23 10) 1)) (Post [Rewrite (EVar (AlexPn 223 26 3) "x") (EAdd (AlexPn 230 26 10) (EUTEntry (EVar (AlexPn 228 26 8) "z")) (IntLit (AlexPn 232 26 12) 2))] Nothing)]) []]] 2 | -------------------------------------------------------------------------------- /tests/frontend/pass/creation/create.act: -------------------------------------------------------------------------------- 1 | // A modest example specifying the creation of a new contract, 2 | // in this case the contract subject at question. 3 | // Specifying the creation of a contract serves multiple purposes: 4 | // from a formal semantics perspective, it defines the base case 5 | // for the inductive system that is a smart contract. From a syntactic 6 | // perspective, it defines the storage layout of the contract, by explicit 7 | // typing of storage variables. 8 | 9 | constructor of Modest 10 | interface constructor() 11 | 12 | creates 13 | // storage variables must be given an explicit initial value 14 | // if they are not assigned a value in the constructor code, 15 | // this is 0. TODO (is this annoying?) 16 | uint x := 1 17 | address y := CALLER 18 | -------------------------------------------------------------------------------- /tests/frontend/pass/creation/create.act.parsed.hs: -------------------------------------------------------------------------------- 1 | Main [Contract (Definition (AlexPn 439 9 16) "Modest" constructor() [] [] (Creates [AssignVal (StorageVar (AlexPn 661 16 10) uint256 "x") (IntLit (AlexPn 666 16 15) 1),AssignVal (StorageVar (AlexPn 680 17 13) address "y") (EnvExp (AlexPn 685 17 18) Caller)]) [] []) []] 2 | -------------------------------------------------------------------------------- /tests/frontend/pass/creation/create.act.typed.json: -------------------------------------------------------------------------------- 1 | { 2 | "contracts": [ 3 | { 4 | "behaviours": [], 5 | "constructor": { 6 | "contract": "Modest", 7 | "initialStorage": [ 8 | { 9 | "location": { 10 | "item": { 11 | "contract": "Modest", 12 | "kind": "SVar", 13 | "svar": "x" 14 | }, 15 | "type": "int" 16 | }, 17 | "value": { 18 | "literal": "1", 19 | "type": "int" 20 | } 21 | }, 22 | { 23 | "location": { 24 | "item": { 25 | "contract": "Modest", 26 | "kind": "SVar", 27 | "svar": "y" 28 | }, 29 | "type": "int" 30 | }, 31 | "value": { 32 | "ethEnv": "Caller", 33 | "type": "int" 34 | } 35 | } 36 | ], 37 | "interface": { 38 | "args": [], 39 | "id": "\"Modest\"", 40 | "kind": "Interface" 41 | }, 42 | "invariants": [], 43 | "kind": "Constructor", 44 | "pointers": [], 45 | "postConditions": [], 46 | "preConditions": [ 47 | { 48 | "args": [ 49 | { 50 | "args": [ 51 | { 52 | "literal": "0", 53 | "type": "int" 54 | }, 55 | { 56 | "ethEnv": "Caller", 57 | "type": "int" 58 | } 59 | ], 60 | "arity": 2, 61 | "symbol": "<=" 62 | }, 63 | { 64 | "args": [ 65 | { 66 | "ethEnv": "Caller", 67 | "type": "int" 68 | }, 69 | { 70 | "literal": "1461501637330902918203684832716283019655932542975", 71 | "type": "int" 72 | } 73 | ], 74 | "arity": 2, 75 | "symbol": "<=" 76 | } 77 | ], 78 | "arity": 2, 79 | "symbol": "and" 80 | } 81 | ] 82 | }, 83 | "kind": "Contract" 84 | } 85 | ], 86 | "kind": "Act", 87 | "store": { 88 | "kind": "Storages", 89 | "storages": { 90 | "Modest": { 91 | "x": [ 92 | { 93 | "kind": "ValueType", 94 | "valueType": { 95 | "abiType": { 96 | "size": "256", 97 | "type": "UInt" 98 | }, 99 | "kind": "AbiType" 100 | } 101 | }, 102 | 0 103 | ], 104 | "y": [ 105 | { 106 | "kind": "ValueType", 107 | "valueType": { 108 | "abiType": { 109 | "type": "Address" 110 | }, 111 | "kind": "AbiType" 112 | } 113 | }, 114 | 1 115 | ] 116 | } 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /tests/frontend/pass/dss/vat.act: -------------------------------------------------------------------------------- 1 | // contract Vat 2 | 3 | // ilk : bytes32 -> {Art : uint256, 4 | // rate : uint256, 5 | // spot : uint256, 6 | // line : uint256, 7 | // dust : uint256} 8 | // urn : bytes32 -> {ink : uint256, 9 | // art : uint256} 10 | // Line : uint256 11 | // live : uint256 12 | // can : address -> address -> uint256 13 | // gem : bytes32 -> address -> uint256 14 | // debt : uint256 15 | // dai : address -> uint256 16 | 17 | behaviour frob of Vat 18 | interface frob(bytes32 i, address u, address v, address w, int dink, int dart) 19 | 20 | iff in range uint256 21 | 22 | urns[i][u].ink + dink 23 | urns[i][u].art + dart 24 | ilks[i].Art + dart 25 | (ilks[i].Art + dart) * ilks[i].rate 26 | dai[w] + (ilks[i].rate * dart) 27 | debt + (ilks[i].rate * dart) 28 | 29 | iff in range int256 30 | 31 | ilks[i].rate 32 | ilks[i].rate * dart 33 | 34 | iff 35 | CALLVALUE == 0 36 | live == 1 37 | ilks[i].rate =/= 0 38 | dart <= 0 or ((ilks[i].art + dart) * ilks[i].rate <= ilks[i].line and debt + ilks[i].rate * dart <= line) 39 | (dart <= 0) or (((ilks[i].Art + dart) * ilks[i].rate <= ilks[i].line) and ((debt + ilks[i].rate * dart) <= line)) 40 | (dart <= 0 and dink >= 0) or (((urns[i][u].art + dart) * ilks[i].rate) <= ((urns[i][u].ink + dink) * ilks[i].spot)) 41 | (dart <= 0 and dink >= 0) or (u == CALLER or can[u][CALLER] == 1) 42 | 43 | (dink <= 0) or (v == CALLER or Can[v][CALLER] == 1) 44 | (dart >= 0) or (w == CALLER or Can[w][CALLER] == 1) 45 | 46 | storage 47 | 48 | urns[i][u].ink => urns[i][u].ink + dink 49 | urns[i][u].art => urns[i][u].art + dart 50 | ilks[i].Art => ilks[i].Art + dart 51 | gem[i][v] => gem[i][v] - dink 52 | dai[w] => dai[w] + ilks[i].rate * dart 53 | debt => debt + ilks[i].rate * dart 54 | -------------------------------------------------------------------------------- /tests/frontend/pass/multi/multi.act: -------------------------------------------------------------------------------- 1 | constructor of A 2 | interface constructor() 3 | 4 | creates 5 | uint x := 0 6 | 7 | constructor of B 8 | interface constructor() 9 | 10 | creates 11 | uint y := 0 12 | A a := create A() 13 | 14 | behaviour remote of B 15 | interface remote(uint z) 16 | 17 | iff 18 | CALLVALUE == 0 19 | 20 | storage 21 | a.x => z 22 | 23 | behaviour multi of B 24 | interface multi(uint z) 25 | 26 | iff 27 | CALLVALUE == 0 28 | 29 | storage 30 | y => 1 31 | a.x => z 32 | -------------------------------------------------------------------------------- /tests/frontend/pass/multi/multi.act.parsed.hs: -------------------------------------------------------------------------------- 1 | Main [Contract (Definition (AlexPn 15 1 16) "A" constructor() [] [] (Creates [AssignVal (StorageVar (AlexPn 58 5 9) uint256 "x") (IntLit (AlexPn 63 5 14) 0)]) [] []) [],Contract (Definition (AlexPn 81 7 16) "B" constructor() [] [] (Creates [AssignVal (StorageVar (AlexPn 124 11 9) uint256 "y") (IntLit (AlexPn 129 11 14) 0),AssignVal (StorageVar (AlexPn 136 12 6) A "a") (ECreate (AlexPn 148 12 18) "A" [])]) [] []) [Transition (AlexPn 153 14 1) "remote" "B" remote(uint256 z) [] [Iff (AlexPn 201 17 1) [EEq (AlexPn 218 18 14) (EnvExp (AlexPn 208 18 4) Callvalue) (IntLit (AlexPn 221 18 17) 0)]] (Direct (Post [Rewrite (EField (AlexPn 236 21 5) (EVar (AlexPn 235 21 4) "a") "x") (EUTEntry (EVar (AlexPn 242 21 11) "z"))] Nothing)) [],Transition (AlexPn 245 23 1) "multi" "B" multi(uint256 z) [] [Iff (AlexPn 291 26 1) [EEq (AlexPn 308 27 14) (EnvExp (AlexPn 298 27 4) Callvalue) (IntLit (AlexPn 311 27 17) 0)]] (Direct (Post [Rewrite (EVar (AlexPn 325 30 4) "y") (IntLit (AlexPn 330 30 9) 1),Rewrite (EField (AlexPn 336 31 5) (EVar (AlexPn 335 31 4) "a") "x") (EUTEntry (EVar (AlexPn 342 31 11) "z"))] Nothing)) []]] 2 | -------------------------------------------------------------------------------- /tests/frontend/pass/negative-literals.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor() 3 | 4 | creates 5 | 6 | int totalSupply := -1 7 | -------------------------------------------------------------------------------- /tests/frontend/pass/safemath/safemathraw.act: -------------------------------------------------------------------------------- 1 | behaviour add of SafeAdd 2 | interface add(uint256 x, uint256 y) 3 | 4 | iff in range uint256 5 | 6 | x + y 7 | 8 | iff 9 | 10 | CALLVALUE == 0 11 | 12 | returns x + y 13 | -------------------------------------------------------------------------------- /tests/frontend/pass/safemath/safemathraw.act.ir.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "add", 3 | "preConditions": [ 4 | { 5 | "arity": 2, 6 | "args": [ 7 | { 8 | "arity": 2, 9 | "args": [ 10 | 0, 11 | { 12 | "arity": 2, 13 | "args": [ 14 | "x", 15 | "y" 16 | ], 17 | "symbol": "+" 18 | } 19 | ], 20 | "symbol": "<=" 21 | }, 22 | { 23 | "arity": 2, 24 | "args": [ 25 | { 26 | "arity": 2, 27 | "args": [ 28 | "x", 29 | "y" 30 | ], 31 | "symbol": "+" 32 | }, 33 | 1.157920892373162e+77 34 | ], 35 | "symbol": "<=" 36 | } 37 | ], 38 | "symbol": "and" 39 | }, 40 | { 41 | "arity": 2, 42 | "args": [ 43 | "CALLVALUE", 44 | 0 45 | ], 46 | "symbol": "=" 47 | } 48 | ], 49 | "contract": "SafeAdd", 50 | "interface": "\"add\"[uint256 \"x\",uint256 \"y\"]", 51 | "returns": { 52 | "expression": { 53 | "arity": 2, 54 | "args": [ 55 | "x", 56 | "y" 57 | ], 58 | "symbol": "+" 59 | }, 60 | "sort": "int" 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/frontend/pass/safemath/safemathraw.act.parsed.hs: -------------------------------------------------------------------------------- 1 | Main [Contract (Definition (AlexPn 0 0 0) "SafeAdd" constructor() [] [] (Creates []) [] []) [Transition (AlexPn 0 1 1) "add" "SafeAdd" add(uint256 x, uint256 y) [] [IffIn (AlexPn 62 4 1) uint256 [EAdd (AlexPn 90 6 7) (EUTEntry (EVar (AlexPn 88 6 5) "x")) (EUTEntry (EVar (AlexPn 92 6 9) "y"))],Iff (AlexPn 95 8 1) [EEq (AlexPn 114 10 15) (EnvExp (AlexPn 104 10 5) Callvalue) (IntLit (AlexPn 117 10 18) 0)]] (Direct (Post [] (Just (EAdd (AlexPn 130 12 11) (EUTEntry (EVar (AlexPn 128 12 9) "x")) (EUTEntry (EVar (AlexPn 132 12 13) "y")))))) []]] 2 | -------------------------------------------------------------------------------- /tests/frontend/pass/safemath/safemathraw.act.parsed.hs.custom: -------------------------------------------------------------------------------- 1 | [Transition "add" "SafeAdd" (Interface "add" 2 | [Dec (T_uint 256) "x" 3 | ,Dec (T_uint 256) "y"]) 4 | [IffIn (P 0 0 0) (T_uint 256) [EAdd (P 0 0 0) (Var (P 0 0 0) "x") 5 | (Var (P 0 0 0) "y") 6 | ] 7 | ,Iff (P 0 0 0) [EEq (P 0 0 0) (EnvExpr (P 0 0 0) CALLVALUE) (IntLit (P 0 0 0) 0)]] 8 | 9 | (TDirect (Post Nothing [] (Just (EAdd (P 0 0 0) 10 | (Var (P 0 0 0) "x") 11 | (Var (P 0 0 0) "y") 12 | ) 13 | ) 14 | ) 15 | ) Nothing 16 | ] 17 | -------------------------------------------------------------------------------- /tests/frontend/pass/smoke/smoke.act: -------------------------------------------------------------------------------- 1 | behaviour f of A 2 | interface f(uint x) 3 | 4 | returns 1 -------------------------------------------------------------------------------- /tests/frontend/pass/smoke/smoke.act.parsed.hs: -------------------------------------------------------------------------------- 1 | Main [Contract (Definition (AlexPn 0 0 0) "A" constructor() [] [] (Creates []) [] []) [Transition (AlexPn 0 1 1) "f" "A" f(uint256 x) [] [] (Direct (Post [] (Just (IntLit (AlexPn 46 4 9) 1)))) []]] 2 | -------------------------------------------------------------------------------- /tests/frontend/pass/smoke/smoke.act.parsed.hs.custom: -------------------------------------------------------------------------------- 1 | [Transition (P 0 0 0, "add") (P 0 0 0, "SafeAdd") 2 | (P 0 0 0, "add") [] [] 3 | (TDirect (Post Nothing [] Nothing))] -------------------------------------------------------------------------------- /tests/frontend/pass/smoke/smoke.act.typed.json: -------------------------------------------------------------------------------- 1 | { 2 | "contracts": [ 3 | { 4 | "behaviours": [ 5 | { 6 | "case": [ 7 | { 8 | "literal": "True", 9 | "type": "bool" 10 | } 11 | ], 12 | "contract": "A", 13 | "interface": { 14 | "args": [ 15 | { 16 | "abitype": { 17 | "size": "256", 18 | "type": "UInt" 19 | }, 20 | "id": "\"x\"", 21 | "kind": "Declaration" 22 | } 23 | ], 24 | "id": "\"f\"", 25 | "kind": "Interface" 26 | }, 27 | "kind": "Behaviour", 28 | "name": "f", 29 | "pointers": [], 30 | "postConditions": [], 31 | "preConditions": [ 32 | { 33 | "args": [ 34 | { 35 | "args": [ 36 | { 37 | "literal": "0", 38 | "type": "int" 39 | }, 40 | { 41 | "entry": { 42 | "item": { 43 | "abitype": { 44 | "size": "256", 45 | "type": "UInt" 46 | }, 47 | "kind": "Var", 48 | "var": "x" 49 | }, 50 | "type": "int" 51 | }, 52 | "timing": "Pre" 53 | } 54 | ], 55 | "arity": 2, 56 | "symbol": "<=" 57 | }, 58 | { 59 | "args": [ 60 | { 61 | "entry": { 62 | "item": { 63 | "abitype": { 64 | "size": "256", 65 | "type": "UInt" 66 | }, 67 | "kind": "Var", 68 | "var": "x" 69 | }, 70 | "type": "int" 71 | }, 72 | "timing": "Pre" 73 | }, 74 | { 75 | "literal": "115792089237316195423570985008687907853269984665640564039457584007913129639935", 76 | "type": "int" 77 | } 78 | ], 79 | "arity": 2, 80 | "symbol": "<=" 81 | } 82 | ], 83 | "arity": 2, 84 | "symbol": "and" 85 | } 86 | ], 87 | "returns": { 88 | "expression": { 89 | "literal": "1", 90 | "type": "int" 91 | }, 92 | "kind": "TypedExpr", 93 | "type": "int" 94 | }, 95 | "stateUpdates": [] 96 | } 97 | ], 98 | "constructor": { 99 | "contract": "A", 100 | "initialStorage": [], 101 | "interface": { 102 | "args": [], 103 | "id": "\"A\"", 104 | "kind": "Interface" 105 | }, 106 | "invariants": [], 107 | "kind": "Constructor", 108 | "pointers": [], 109 | "postConditions": [], 110 | "preConditions": [] 111 | }, 112 | "kind": "Contract" 113 | } 114 | ], 115 | "kind": "Act", 116 | "store": { 117 | "kind": "Storages", 118 | "storages": { 119 | "A": {} 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /tests/frontend/pass/staticstore/staticstore.act: -------------------------------------------------------------------------------- 1 | behaviour f of C 2 | interface f() 3 | 4 | iff in range uint256 5 | 6 | x + y 7 | 8 | returns x + y 9 | -------------------------------------------------------------------------------- /tests/frontend/pass/staticstore/staticstore.act.parsed.hs: -------------------------------------------------------------------------------- 1 | Main [Contract (Definition (AlexPn 0 0 0) "C" constructor() [] [] (Creates []) [] []) [Transition (AlexPn 0 1 1) "f" "C" f() [] [IffIn (AlexPn 32 4 1) uint256 [EAdd (AlexPn 60 6 7) (EUTEntry (EVar (AlexPn 58 6 5) "x")) (EUTEntry (EVar (AlexPn 62 6 9) "y"))]] (Direct (Post [] (Just (EAdd (AlexPn 75 8 11) (EUTEntry (EVar (AlexPn 73 8 9) "x")) (EUTEntry (EVar (AlexPn 77 8 13) "y")))))) []]] 2 | -------------------------------------------------------------------------------- /tests/frontend/pass/storageread0.act: -------------------------------------------------------------------------------- 1 | constructor of Pass 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint x := 0 7 | uint y := 2 8 | 9 | behaviour f of Pass 10 | interface f() 11 | 12 | storage 13 | 14 | x => y 15 | y 16 | -------------------------------------------------------------------------------- /tests/frontend/pass/storageread1.act: -------------------------------------------------------------------------------- 1 | constructor of Pass 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint x := 0 7 | uint y := 2 8 | 9 | behaviour g of Pass 10 | interface g() 11 | 12 | iff in range uint256 13 | 14 | x + y 15 | 16 | storage 17 | 18 | // TODO: remove this hack once bug #81 is fixed... 19 | x => x 20 | y 21 | 22 | returns x + y 23 | -------------------------------------------------------------------------------- /tests/frontend/pass/storageread2.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint x := 1 7 | uint y := 1 8 | 9 | 10 | constructor of B 11 | interface constructor() 12 | 13 | creates 14 | 15 | uint a := 1 16 | uint b := 1 17 | 18 | behaviour f of B 19 | interface f() 20 | 21 | storage C 22 | 23 | x => y 24 | y 25 | -------------------------------------------------------------------------------- /tests/frontend/pass/token/transfer.act: -------------------------------------------------------------------------------- 1 | constructor of Token 2 | interface constructor(string _symbol, string _name, string _version, uint _totalSupply) 3 | 4 | creates 5 | 6 | string name := _name 7 | string symbol := _symbol 8 | uint256 totalSupply := _totalSupply 9 | mapping(address => uint) balanceOf := [CALLER := _totalSupply] 10 | mapping(address=>mapping(address=>uint)) allowance := [] 11 | 12 | invariants 13 | 14 | totalSupply == _totalSupply 15 | name == _name 16 | symbol == _symbol 17 | 18 | 19 | behaviour transfer of Token 20 | interface transfer(uint256 value, address to) 21 | 22 | iff 23 | 24 | CALLVALUE == 0 25 | value <= balanceOf[CALLER] 26 | CALLER =/= to => balanceOf[to] + value < 2^256 27 | 28 | case CALLER =/= to: 29 | 30 | storage 31 | 32 | balanceOf[CALLER] => balanceOf[CALLER] - value 33 | balanceOf[to] => balanceOf[to] + value 34 | 35 | returns 1 36 | 37 | case CALLER == to: 38 | 39 | returns 1 40 | 41 | 42 | behaviour transferFrom of Token 43 | interface transferFrom(address src, address dst, uint amount) 44 | 45 | iff 46 | 47 | amount <= balanceOf[CALLER] 48 | src =/= dst => balanceOf[dst] + amount < 2^256 49 | CALLER =/= src => 0 <= allowance[src][CALLER] - amount 50 | CALLVALUE == 0 51 | 52 | case src =/= dst and CALLER == src: 53 | 54 | storage 55 | 56 | balanceOf[src] => balanceOf[src] - amount 57 | balanceOf[dst] => balanceOf[dst] + amount 58 | 59 | returns 1 60 | 61 | case src =/= dst and CALLER =/= src and allowance[src][CALLER] == 2^256 - 1: 62 | 63 | storage 64 | 65 | balanceOf[src] => balanceOf[src] - amount 66 | balanceOf[dst] => balanceOf[dst] + amount 67 | 68 | returns 1 69 | 70 | case src =/= dst and CALLER =/= src and allowance[src][CALLER] < 2^256 - 1: 71 | 72 | storage 73 | 74 | allowance[src][CALLER] => allowance[src][CALLER] - amount 75 | balanceOf[src] => balanceOf[src] - amount 76 | balanceOf[dst] => balanceOf[dst] + amount 77 | 78 | returns 1 79 | 80 | case src == dst: 81 | 82 | 83 | returns 1 84 | -------------------------------------------------------------------------------- /tests/frontend/pass/two_behaviours_on_two_contracts.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor(uint24 _x) 3 | 4 | creates 5 | 6 | uint24 x := _x 7 | 8 | behaviour increase of C 9 | interface increase(uint24 new_x) 10 | 11 | storage 12 | x 13 | 14 | returns pre(x) 15 | 16 | ensures 17 | 18 | post(x) >= pre(x) 19 | 20 | // Second contract, with same behaviour name 21 | constructor of D 22 | interface constructor(uint24 _x) 23 | 24 | creates 25 | 26 | uint24 x := _x 27 | 28 | behaviour increase of D 29 | interface increase2(uint24 new_x) 30 | 31 | storage 32 | x 33 | 34 | returns new_x 35 | 36 | ensures 37 | 38 | post(x) >= pre(x) 39 | 40 | -------------------------------------------------------------------------------- /tests/hevm/fail/multi/multi.act: -------------------------------------------------------------------------------- 1 | constructor of A 2 | interface constructor(uint z) 3 | 4 | iff 5 | CALLVALUE == 0 6 | inRange(uint256, z + 42) 7 | 8 | creates 9 | uint x := z + 42 10 | 11 | behaviour set_x of A 12 | interface set_x(uint z) 13 | 14 | iff 15 | CALLVALUE == 0 16 | 17 | storage 18 | x => z 19 | 20 | 21 | constructor of B 22 | interface constructor(uint v) 23 | 24 | iff 25 | CALLVALUE == 0 26 | // This condition must be present for equivalence check to pass 27 | // inRange(uint256, v + 42) 28 | 29 | creates 30 | uint y := 0 31 | A a := create A(v) 32 | -------------------------------------------------------------------------------- /tests/hevm/fail/multi/multi.sol: -------------------------------------------------------------------------------- 1 | contract A { 2 | uint x; 3 | 4 | constructor (uint z) { 5 | x = z + 42; 6 | } 7 | 8 | function set_x(uint z) public { 9 | x = z; 10 | } 11 | } 12 | 13 | contract B { 14 | 15 | uint y; 16 | A a; 17 | 18 | constructor(uint v) { 19 | y = 0; 20 | a = new A(v); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tests/hevm/fail/redundant/redundant.act: -------------------------------------------------------------------------------- 1 | constructor of A 2 | interface constructor() 3 | 4 | iff 5 | 6 | CALLVALUE == 0 7 | 8 | creates 9 | 10 | uint x := 0 11 | 12 | behaviour f of A 13 | interface f() 14 | 15 | iff 16 | 17 | CALLVALUE == 0 18 | x == 1 19 | 20 | storage 21 | 22 | x => 2 -------------------------------------------------------------------------------- /tests/hevm/fail/redundant/redundant.sol: -------------------------------------------------------------------------------- 1 | contract A { 2 | uint x; 3 | 4 | constructor () { 5 | x = 0; 6 | } 7 | 8 | function f() external { 9 | x = 2; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/hevm/fail/shape-2/shape-2.act: -------------------------------------------------------------------------------- 1 | // contract A 2 | constructor of A 3 | interface constructor(uint z) 4 | 5 | iff 6 | CALLVALUE == 0 7 | 8 | creates 9 | uint x := z 10 | 11 | 12 | behaviour x of A 13 | interface x() 14 | 15 | iff 16 | CALLVALUE == 0 17 | 18 | returns 19 | pre(x) 20 | 21 | // contract C 22 | constructor of C 23 | interface constructor() 24 | iff 25 | CALLVALUE == 0 26 | 27 | creates 28 | A a1 := create A(42) 29 | A a2 := create A(17) 30 | 31 | behaviour change of C 32 | interface change() 33 | 34 | iff 35 | 36 | CALLVALUE == 0 37 | 38 | storage 39 | a1 => a2 -------------------------------------------------------------------------------- /tests/hevm/fail/shape-2/shape-2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.0; 2 | 3 | contract A { 4 | uint public x; 5 | 6 | constructor (uint z) { 7 | x = z; 8 | } 9 | } 10 | 11 | 12 | contract C { 13 | A a1; 14 | A a2; 15 | 16 | constructor () { 17 | a1 = new A(42); 18 | a2 = new A(17); 19 | } 20 | 21 | function change() public { 22 | a1 = a2; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/hevm/fail/shape/shape.act: -------------------------------------------------------------------------------- 1 | // contract A 2 | constructor of A 3 | interface constructor(uint z) 4 | 5 | iff 6 | CALLVALUE == 0 7 | 8 | creates 9 | uint x := z 10 | 11 | 12 | behaviour x of A 13 | interface x() 14 | 15 | iff 16 | CALLVALUE == 0 17 | 18 | returns 19 | pre(x) 20 | 21 | behaviour set_x of A 22 | interface set_x(uint z) 23 | 24 | iff 25 | CALLVALUE == 0 26 | 27 | storage 28 | x => z 29 | 30 | // contract B 31 | constructor of B 32 | interface constructor(uint z) 33 | iff 34 | CALLVALUE == 0 35 | 36 | creates 37 | uint y := z 38 | A a := create A(0) 39 | 40 | behaviour y of B 41 | interface y() 42 | 43 | iff 44 | CALLVALUE == 0 45 | 46 | returns 47 | pre(y) 48 | 49 | behaviour a of B 50 | interface a() 51 | 52 | iff 53 | CALLVALUE == 0 54 | 55 | returns 56 | pre(a) 57 | 58 | 59 | // contract C 60 | constructor of C 61 | interface constructor(address y) 62 | pointers 63 | y |-> B 64 | 65 | iff 66 | CALLVALUE == 0 67 | 68 | creates 69 | A a := y.a 70 | B b := y 71 | 72 | behaviour change of C 73 | interface change() 74 | 75 | iff 76 | 77 | CALLVALUE == 0 78 | 79 | storage 80 | a => create A(42) -------------------------------------------------------------------------------- /tests/hevm/fail/shape/shape.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.0; 2 | 3 | contract A { 4 | uint public x; 5 | 6 | constructor (uint z) { 7 | x = z; 8 | } 9 | 10 | function set_x(uint z) public{ 11 | x = z; 12 | } 13 | } 14 | 15 | contract B { 16 | uint public y; 17 | A public a; 18 | 19 | constructor (uint z) { 20 | y = z; 21 | a = new A(0); 22 | } 23 | } 24 | 25 | contract C { 26 | A a; 27 | B b; 28 | 29 | constructor (address y) { 30 | a = B(y).a(); 31 | b = B(y); 32 | } 33 | 34 | function change() public { 35 | a = new A(42); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/hevm/fail/simple/simple.act: -------------------------------------------------------------------------------- 1 | constructor of A 2 | interface constructor() 3 | 4 | iff 5 | 6 | CALLVALUE == 0 7 | 8 | creates 9 | 10 | mapping (uint=>uint) x := [] 11 | 12 | behaviour f of A 13 | interface f() 14 | 15 | iff 16 | 17 | CALLVALUE == 0 18 | 19 | storage 20 | 21 | x[0] => 1 22 | 23 | returns 24 | 25 | 2 -------------------------------------------------------------------------------- /tests/hevm/fail/simple/simple.sol: -------------------------------------------------------------------------------- 1 | contract A { 2 | mapping(uint=>uint) x; 3 | 4 | function f() external payable returns (uint) { 5 | x[0] = 1; 6 | return 1; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/hevm/fail/unspecced_storage/unspecced_storage.act: -------------------------------------------------------------------------------- 1 | constructor of A 2 | interface constructor() 3 | 4 | iff 5 | 6 | CALLVALUE == 0 7 | 8 | creates 9 | 10 | mapping (uint=>uint) x := [] 11 | 12 | behaviour f of A 13 | interface f() 14 | 15 | // fails to mention the state update 16 | 17 | returns 18 | 19 | 1 20 | -------------------------------------------------------------------------------- /tests/hevm/fail/unspecced_storage/unspecced_storage.sol: -------------------------------------------------------------------------------- 1 | contract A { 2 | mapping(uint=>uint) x; 3 | 4 | function f() external payable returns (uint) { 5 | x[0] = 1; 6 | return 1; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/hevm/pass/amm/amm.act: -------------------------------------------------------------------------------- 1 | constructor of Token 2 | interface constructor(uint _supply) 3 | 4 | iff 5 | CALLVALUE == 0 6 | 7 | creates 8 | mapping(address => uint) balanceOf := [CALLER := _supply] 9 | 10 | behaviour balanceOf of Token 11 | interface balanceOf(address a) 12 | 13 | iff 14 | 15 | CALLVALUE == 0 16 | 17 | returns pre(balanceOf[a]) 18 | 19 | 20 | behaviour transferFrom of Token 21 | interface transferFrom(uint256 value, address from, address to) 22 | 23 | iff 24 | 25 | CALLVALUE == 0 26 | from =/= to => inRange(uint256, balanceOf[to] + value) 27 | inRange(uint256,balanceOf[from] - value) 28 | 29 | case from =/= to: 30 | 31 | storage 32 | 33 | balanceOf[from] => balanceOf[from] - value 34 | balanceOf[to] => balanceOf[to] + value 35 | 36 | returns 1 37 | 38 | case from == to: 39 | 40 | returns 1 41 | 42 | 43 | constructor of Amm 44 | interface constructor(address t0, address t1) 45 | 46 | pointers 47 | t0 |-> Token 48 | t1 |-> Token 49 | 50 | iff 51 | 52 | CALLVALUE == 0 53 | t0 =/= t1 54 | 55 | creates 56 | 57 | Token token0 := t0 58 | Token token1 := t1 59 | 60 | behaviour swap0 of Amm 61 | interface swap0(uint256 amt) 62 | 63 | iff 64 | CALLVALUE == 0 65 | inRange(uint256,token0.balanceOf[CALLER] - amt) 66 | CALLER =/= THIS => inRange(uint256,token0.balanceOf[THIS] + amt) 67 | 68 | inRange(uint256,token1.balanceOf[THIS] - ((token1.balanceOf[THIS] * amt) / (token0.balanceOf[THIS] + amt))) 69 | CALLER =/= THIS => inRange(uint256,token1.balanceOf[CALLER] + ((token1.balanceOf[THIS] * amt) / (token0.balanceOf[THIS] + amt))) 70 | 71 | token0.balanceOf[THIS] + amt =/= 0 72 | 73 | case CALLER =/= THIS: 74 | 75 | storage 76 | 77 | token0.balanceOf[CALLER] => token0.balanceOf[CALLER] - amt 78 | token0.balanceOf[THIS] => token0.balanceOf[THIS] + amt 79 | 80 | token1.balanceOf[THIS] => token1.balanceOf[THIS] - ((token1.balanceOf[THIS] * amt) / (token0.balanceOf[THIS] + amt)) 81 | token1.balanceOf[CALLER] => token1.balanceOf[CALLER] + ((token1.balanceOf[THIS] * amt) / (token0.balanceOf[THIS] + amt)) 82 | 83 | returns 1 84 | 85 | case CALLER == THIS: 86 | 87 | returns 1 88 | 89 | ensures 90 | 91 | pre(token0.balanceOf[THIS]) * pre(token1.balanceOf[THIS]) <= post(token0.balanceOf[THIS]) * post(token1.balanceOf[THIS]) 92 | post(token0.balanceOf[THIS]) * post(token1.balanceOf[THIS]) <= pre(token0.balanceOf[THIS]) * pre(token1.balanceOf[THIS]) + pre(token0.balanceOf[THIS]) + amt 93 | 94 | 95 | behaviour swap1 of Amm 96 | interface swap1(uint256 amt) 97 | 98 | iff 99 | CALLVALUE == 0 100 | inRange(uint256,token1.balanceOf[CALLER] - amt) 101 | CALLER =/= THIS => inRange(uint256,token1.balanceOf[THIS] + amt) 102 | 103 | inRange(uint256,token0.balanceOf[THIS] - ((token0.balanceOf[THIS] * amt) / (token1.balanceOf[THIS] + amt))) 104 | CALLER =/= THIS => inRange(uint256,token0.balanceOf[CALLER] + ((token0.balanceOf[THIS] * amt) / (token1.balanceOf[THIS] + amt))) 105 | 106 | token1.balanceOf[THIS] + amt =/= 0 107 | 108 | case CALLER =/= THIS: 109 | 110 | storage 111 | 112 | token1.balanceOf[CALLER] => token1.balanceOf[CALLER] - amt 113 | token1.balanceOf[THIS] => token1.balanceOf[THIS] + amt 114 | 115 | token0.balanceOf[THIS] => token0.balanceOf[THIS] - ((token0.balanceOf[THIS] * amt) / (token1.balanceOf[THIS] + amt)) 116 | token0.balanceOf[CALLER] => token0.balanceOf[CALLER] + ((token0.balanceOf[THIS] * amt) / (token1.balanceOf[THIS] + amt)) 117 | 118 | returns 1 119 | 120 | case CALLER == THIS: 121 | 122 | returns 1 123 | 124 | ensures 125 | 126 | pre(token0.balanceOf[THIS]) * pre(token1.balanceOf[THIS]) <= post(token0.balanceOf[THIS]) * post(token1.balanceOf[THIS]) 127 | post(token0.balanceOf[THIS]) * post(token1.balanceOf[THIS]) <= pre(token0.balanceOf[THIS]) * pre(token1.balanceOf[THIS]) + pre(token1.balanceOf[THIS]) + amt 128 | -------------------------------------------------------------------------------- /tests/hevm/pass/amm/amm.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.0; 2 | 3 | contract Token { 4 | mapping (address => uint) public balanceOf; 5 | 6 | constructor(uint _totalSupply) { 7 | balanceOf[msg.sender] = _totalSupply; 8 | } 9 | 10 | 11 | function transferFrom(uint256 value, address from, address to) public returns (uint) { 12 | balanceOf[from] = balanceOf[from] - value; 13 | balanceOf[to] = balanceOf[to] + value; 14 | return 1; 15 | } 16 | 17 | } 18 | 19 | 20 | contract Amm { 21 | 22 | Token token0; 23 | Token token1; 24 | 25 | constructor(address t0, address t1) { 26 | require (t0 != t1); 27 | token0 = Token(t0); 28 | token1 = Token(t1); 29 | } 30 | 31 | function swap0(uint256 amt) public returns (uint) { 32 | uint256 reserve0 = token0.balanceOf(address(this)); 33 | uint256 reserve1 = token1.balanceOf(address(this)); 34 | 35 | token0.transferFrom(amt, msg.sender, address(this)); 36 | token1.transferFrom((reserve1*amt)/(reserve0+amt), address(this), msg.sender); 37 | 38 | return 1; 39 | } 40 | 41 | function swap1(uint256 amt) public returns (uint) { 42 | uint256 reserve0 = token0.balanceOf(address(this)); 43 | uint256 reserve1 = token1.balanceOf(address(this)); 44 | 45 | token1.transferFrom(amt, msg.sender, address(this)); 46 | token0.transferFrom((reserve0*amt)/(reserve1+amt), address(this), msg.sender); 47 | 48 | return 1; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /tests/hevm/pass/cast-3/cast-3.act: -------------------------------------------------------------------------------- 1 | constructor of Token 2 | interface constructor(uint _supply) 3 | 4 | iff 5 | CALLVALUE == 0 6 | 7 | creates 8 | mapping(address => uint) balanceOf := [CALLER := _supply] 9 | 10 | behaviour balanceOf of Token 11 | interface balanceOf(address a) 12 | 13 | iff 14 | 15 | CALLVALUE == 0 16 | 17 | returns pre(balanceOf[a]) 18 | 19 | 20 | behaviour transfer of Token 21 | interface transfer(address to, uint256 value) 22 | 23 | iff 24 | 25 | CALLVALUE == 0 26 | CALLER =/= to => inRange(uint256, balanceOf[to] + value) 27 | inRange(uint256,balanceOf[CALLER] - value) 28 | 29 | case CALLER =/= to: 30 | 31 | storage 32 | 33 | balanceOf[CALLER] => balanceOf[CALLER] - value 34 | balanceOf[to] => balanceOf[to] + value 35 | 36 | returns 1 37 | 38 | case CALLER == to: 39 | 40 | returns 1 41 | 42 | 43 | constructor of TransferOneToken 44 | interface constructor(address _tokenAddress) 45 | 46 | pointers 47 | _tokenAddress |-> Token 48 | 49 | iff 50 | 51 | CALLVALUE == 0 52 | _tokenAddress =/= 0 53 | 54 | creates 55 | 56 | Token token := _tokenAddress 57 | 58 | behaviour transfer of TransferOneToken 59 | interface transfer() 60 | 61 | iff 62 | CALLVALUE == 0 63 | THIS =/= CALLER => inRange(uint256, token.balanceOf[CALLER] + 1) 64 | inRange(uint256,token.balanceOf[THIS] - 1) 65 | 66 | case CALLER =/= THIS: 67 | 68 | storage 69 | 70 | token.balanceOf[CALLER] => token.balanceOf[CALLER] + 1 71 | token.balanceOf[THIS] => token.balanceOf[THIS] - 1 72 | 73 | returns 1 74 | 75 | case CALLER == THIS: 76 | 77 | returns 1 -------------------------------------------------------------------------------- /tests/hevm/pass/cast-3/cast-3.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.0; 3 | 4 | contract Token { 5 | mapping (address => uint) public balanceOf; 6 | 7 | constructor(uint _totalSupply) { 8 | balanceOf[msg.sender] = _totalSupply; 9 | } 10 | 11 | 12 | function transfer(address to, uint256 value) public returns (uint) { 13 | balanceOf[msg.sender] = balanceOf[msg.sender] - value; 14 | balanceOf[to] = balanceOf[to] + value; 15 | return 1; 16 | } 17 | 18 | } 19 | 20 | 21 | 22 | contract TransferOneToken { 23 | Token token; 24 | 25 | constructor(address _tokenAddress) { 26 | require(_tokenAddress != address(0), "Invalid token address"); 27 | token = Token(_tokenAddress); 28 | } 29 | 30 | function transfer() public returns (uint) { 31 | // Transfer 1 token from the contract to the sender 32 | uint256 transferAmt = 1; 33 | token.transfer(msg.sender, transferAmt); 34 | return 1; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/hevm/pass/cast-4/cast-4.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor(uint z1) 3 | 4 | iff 5 | CALLVALUE == 0 6 | 7 | creates 8 | uint z := z1 9 | 10 | constructor of A 11 | interface constructor(uint x1) 12 | 13 | iff 14 | CALLVALUE == 0 15 | 16 | creates 17 | uint x := x1 18 | C c := create C(42) 19 | 20 | 21 | constructor of B 22 | interface constructor(address x, address y) 23 | 24 | pointers 25 | x |-> A 26 | y |-> A 27 | 28 | iff 29 | CALLVALUE == 0 30 | x =/= y 31 | 32 | creates 33 | A a1 := x 34 | A a2 := y 35 | 36 | behaviour upd of B 37 | interface upd() 38 | 39 | iff 40 | CALLVALUE == 0 41 | 42 | storage 43 | 44 | a1 => create A(42) 45 | a2 => create A(43) -------------------------------------------------------------------------------- /tests/hevm/pass/cast-4/cast-4.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.0; 2 | 3 | contract C { 4 | uint z; 5 | 6 | constructor(uint z1) { 7 | z = z1; 8 | } 9 | 10 | } 11 | 12 | 13 | contract A { 14 | uint x; 15 | C c; 16 | 17 | constructor(uint x1) { 18 | x = x1; 19 | c = new C(42); 20 | } 21 | } 22 | 23 | 24 | contract B { 25 | A a1; 26 | A a2; 27 | 28 | constructor(address x, address y) { 29 | require (x != y); 30 | a1 = A(x); 31 | a2 = A(y); 32 | } 33 | 34 | function upd() public { 35 | a1 = new A(42); 36 | a2 = new A(43); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/hevm/pass/cast-5/cast-5.act: -------------------------------------------------------------------------------- 1 | constructor of A 2 | interface constructor(uint w1) 3 | 4 | iff 5 | CALLVALUE == 0 6 | 7 | creates 8 | uint w := w1 9 | 10 | constructor of B 11 | interface constructor(uint z1) 12 | 13 | iff 14 | CALLVALUE == 0 15 | 16 | creates 17 | uint z := z1 18 | 19 | constructor of C 20 | interface constructor(address a1, address a2) 21 | 22 | pointers 23 | a1 |-> A 24 | a2 |-> B 25 | 26 | iff 27 | CALLVALUE == 0 28 | 29 | creates 30 | A a := a1 31 | B b := a2 32 | 33 | constructor of D 34 | interface constructor(address a1, address a2) 35 | 36 | pointers 37 | a1 |-> A 38 | a2 |-> B 39 | 40 | iff 41 | CALLVALUE == 0 42 | 43 | creates 44 | C c1 := create C(a1, a2) 45 | -------------------------------------------------------------------------------- /tests/hevm/pass/cast-5/cast-5.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.0; 2 | 3 | contract A { 4 | uint z; 5 | 6 | constructor(uint z1) { 7 | z = z1; 8 | } 9 | 10 | } 11 | 12 | contract B { 13 | uint t; 14 | 15 | constructor(uint t1) { 16 | t = t1; 17 | } 18 | } 19 | 20 | contract C { 21 | A a; 22 | B b; 23 | 24 | constructor(address x, address y) { 25 | a = A(x); 26 | b = B(y); 27 | } 28 | } 29 | 30 | contract D { 31 | C c2; 32 | 33 | constructor(address x, address y) { 34 | c2 = new C(x,y); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests/hevm/pass/cast-6/cast-6.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor(uint z1) 3 | 4 | iff 5 | CALLVALUE == 0 6 | 7 | creates 8 | uint z := z1 9 | 10 | constructor of A 11 | interface constructor(uint x1) 12 | 13 | iff 14 | CALLVALUE == 0 15 | 16 | creates 17 | uint x := x1 18 | C c := create C(42) 19 | 20 | 21 | behaviour c of A 22 | interface c() 23 | 24 | iff 25 | CALLVALUE == 0 26 | 27 | returns (pre(c)) 28 | 29 | constructor of B 30 | interface constructor(address x) 31 | 32 | pointers 33 | x |-> A 34 | 35 | iff 36 | CALLVALUE == 0 37 | 38 | creates 39 | A a := x 40 | C c := x.c 41 | -------------------------------------------------------------------------------- /tests/hevm/pass/cast-6/cast-6.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.0; 2 | 3 | contract C { 4 | uint z; 5 | 6 | constructor(uint z1) { 7 | z = z1; 8 | } 9 | 10 | } 11 | 12 | 13 | contract A { 14 | uint x; 15 | C public c; 16 | 17 | constructor(uint x1) { 18 | x = x1; 19 | c = new C(42); 20 | } 21 | } 22 | 23 | 24 | contract B { 25 | A a; 26 | C c; 27 | 28 | constructor(address x) { 29 | a = A(x); 30 | c = a.c(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/hevm/pass/cast/cast.act: -------------------------------------------------------------------------------- 1 | constructor of A 2 | interface constructor(uint x0) 3 | 4 | iff 5 | CALLVALUE == 0 6 | 7 | creates 8 | uint x := x0 9 | 10 | 11 | behaviour getx of A 12 | interface getx() 13 | 14 | iff 15 | 16 | CALLVALUE == 0 17 | 18 | returns pre(x) 19 | 20 | 21 | behaviour setx of A 22 | interface setx(uint x1) 23 | 24 | iff 25 | 26 | CALLVALUE == 0 27 | 28 | storage 29 | x => x1 30 | 31 | 32 | constructor of B 33 | interface constructor(address x, address y) 34 | 35 | pointers 36 | x |-> A 37 | y |-> A 38 | 39 | iff 40 | CALLVALUE == 0 41 | x =/= y 42 | 43 | creates 44 | A a1 := x 45 | A a2 := y 46 | 47 | 48 | behaviour upd of B 49 | interface upd() 50 | 51 | iff 52 | 53 | CALLVALUE == 0 54 | 55 | storage 56 | a1.x => 42 57 | a2.x => 11 -------------------------------------------------------------------------------- /tests/hevm/pass/cast/cast.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.0; 2 | 3 | contract A { 4 | uint x; 5 | 6 | constructor(uint _x) { 7 | x = _x; 8 | } 9 | 10 | 11 | function getx() public returns (uint) { 12 | return x; 13 | } 14 | 15 | function setx(uint _x1) public { 16 | x = _x1; 17 | } 18 | 19 | } 20 | 21 | 22 | contract B { 23 | A a1; 24 | A a2; 25 | 26 | constructor(address x, address y) { 27 | require(x!=y); 28 | a1 = A(x); 29 | a2 = A(y); 30 | } 31 | 32 | 33 | function upd() public { 34 | a1.setx(42); 35 | a2.setx(11); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /tests/hevm/pass/inputs/inputs.act: -------------------------------------------------------------------------------- 1 | behaviour test of Inputs 2 | interface test(uint128 x) 3 | 4 | iff 5 | 6 | CALLVALUE == 0 7 | x > 0 8 | 9 | iff in range uint128 10 | (x+1) 11 | 12 | returns (x+1) -------------------------------------------------------------------------------- /tests/hevm/pass/inputs/inputs.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.0; 2 | 3 | contract Inputs { 4 | 5 | constructor() payable {} 6 | 7 | function test(uint128 x) public returns (uint128) { 8 | require(x>0); 9 | return (x+1); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/hevm/pass/layout1/layout1.act: -------------------------------------------------------------------------------- 1 | constructor of A 2 | interface constructor() 3 | 4 | iff 5 | 6 | CALLVALUE == 0 7 | 8 | creates 9 | 10 | uint128 x := 11 11 | uint128 y := 42 12 | 13 | behaviour swap of A 14 | interface swap() 15 | 16 | iff 17 | 18 | CALLVALUE == 0 19 | 20 | storage 21 | 22 | x => y 23 | y => x 24 | 25 | returns 1 -------------------------------------------------------------------------------- /tests/hevm/pass/layout1/layout1.sol: -------------------------------------------------------------------------------- 1 | contract A { 2 | uint128 x; 3 | uint128 y; 4 | 5 | constructor() { 6 | x = 11; 7 | y = 42; 8 | } 9 | 10 | function swap() external returns (uint) { 11 | 12 | uint128 tmp = x; 13 | 14 | x = y; 15 | y = tmp; 16 | 17 | return 1; 18 | } 19 | } -------------------------------------------------------------------------------- /tests/hevm/pass/layout2/layout2.act: -------------------------------------------------------------------------------- 1 | constructor of A 2 | interface constructor() 3 | 4 | iff 5 | 6 | CALLVALUE == 0 7 | 8 | creates 9 | 10 | uint128 x := 11 11 | bool flag := true 12 | uint32 u := 17 13 | uint8 z := 3 14 | uint128 y := 42 15 | uint256 i := 128 16 | 17 | behaviour foo of A 18 | interface foo() 19 | 20 | iff 21 | 22 | CALLVALUE == 0 23 | 24 | storage 25 | 26 | x => y 27 | y => x 28 | z => 11 29 | flag => not flag 30 | 31 | returns 1 32 | 33 | behaviour set_flag of A 34 | interface set_flag(bool b) 35 | 36 | iff 37 | 38 | CALLVALUE == 0 39 | 40 | storage 41 | 42 | flag => b 43 | 44 | 45 | behaviour get_flag of A 46 | interface get_flag() 47 | 48 | iff 49 | 50 | CALLVALUE == 0 51 | 52 | returns 53 | 54 | pre(flag) 55 | 56 | 57 | behaviour set_y_if_flag of A 58 | interface set_y_if_flag(uint128 v) 59 | 60 | iff 61 | 62 | CALLVALUE == 0 63 | 64 | case flag: 65 | 66 | storage 67 | y => v 68 | 69 | returns 1 70 | 71 | case (not flag): 72 | returns 1 73 | 74 | -------------------------------------------------------------------------------- /tests/hevm/pass/layout2/layout2.sol: -------------------------------------------------------------------------------- 1 | contract A { 2 | uint128 x; 3 | bool flag; 4 | uint32 u; 5 | uint8 z; 6 | uint128 y; 7 | uint256 i; 8 | 9 | constructor() { 10 | x = 11; 11 | flag = true; 12 | u = 17; 13 | z = 3; 14 | y = 42; 15 | i = 128; 16 | } 17 | 18 | function foo() external returns (uint) { 19 | 20 | uint128 tmp = x; 21 | 22 | x = y; 23 | y = tmp; 24 | z = 11; 25 | flag = !flag; 26 | 27 | return 1; 28 | } 29 | 30 | function set_flag(bool b) external { 31 | flag = b; 32 | } 33 | 34 | function get_flag() external returns (bool) { 35 | return flag; 36 | } 37 | 38 | function set_y_if_flag(uint128 v) external returns (uint) { 39 | if (flag) { 40 | y = v; 41 | } 42 | 43 | return 1; 44 | } 45 | } -------------------------------------------------------------------------------- /tests/hevm/pass/layout3/layout3.act: -------------------------------------------------------------------------------- 1 | constructor of Map 2 | interface constructor() 3 | 4 | iff 5 | 6 | CALLVALUE == 0 7 | 8 | creates 9 | uint128 val := 11 10 | mapping(uint => uint128) f := [11 := 42] 11 | 12 | behaviour val of Map 13 | interface val() 14 | 15 | iff 16 | 17 | CALLVALUE == 0 18 | 19 | returns pre(val) 20 | 21 | behaviour f of Map 22 | interface f(uint x) 23 | 24 | iff 25 | 26 | CALLVALUE == 0 27 | 28 | returns pre(f[x]) 29 | 30 | 31 | behaviour set of Map 32 | interface set(uint128 value, uint key) 33 | 34 | iff 35 | 36 | CALLVALUE == 0 37 | 38 | storage 39 | 40 | f[key] => value 41 | 42 | returns 1 -------------------------------------------------------------------------------- /tests/hevm/pass/layout3/layout3.sol: -------------------------------------------------------------------------------- 1 | contract Map { 2 | uint128 public val; 3 | mapping (uint => uint128) public f; 4 | 5 | constructor() { 6 | val = 11; 7 | f[11] = 42; 8 | } 9 | 10 | function set(uint128 value, uint key) external returns (uint) { 11 | f[key] = value; 12 | return 1; 13 | } 14 | } -------------------------------------------------------------------------------- /tests/hevm/pass/layout4/layout4.act: -------------------------------------------------------------------------------- 1 | constructor of Map 2 | interface constructor() 3 | 4 | iff 5 | 6 | CALLVALUE == 0 7 | 8 | creates 9 | uint128 val := 11 10 | mapping(uint => uint128) f := [11 := 42, 42 := 1] 11 | mapping(uint128 => bool) g := [22 := true, 1 := false] 12 | 13 | 14 | behaviour val of Map 15 | interface val() 16 | 17 | iff 18 | 19 | CALLVALUE == 0 20 | 21 | returns pre(val) 22 | 23 | behaviour f of Map 24 | interface f(uint x) 25 | 26 | iff 27 | 28 | CALLVALUE == 0 29 | 30 | returns pre(f[x]) 31 | 32 | 33 | behaviour g of Map 34 | interface g(uint128 x) 35 | 36 | iff 37 | 38 | CALLVALUE == 0 39 | 40 | returns pre(g[x]) 41 | 42 | 43 | 44 | behaviour setf of Map 45 | interface setf(uint128 value, uint key) 46 | 47 | iff 48 | 49 | CALLVALUE == 0 50 | 51 | storage 52 | 53 | f[key] => value 54 | 55 | returns 1 56 | 57 | 58 | behaviour setg of Map 59 | interface setg(bool value, uint128 key) 60 | 61 | iff 62 | 63 | CALLVALUE == 0 64 | 65 | storage 66 | 67 | g[key] => value 68 | 69 | returns 1 -------------------------------------------------------------------------------- /tests/hevm/pass/layout4/layout4.sol: -------------------------------------------------------------------------------- 1 | contract Map { 2 | uint128 public val; 3 | mapping (uint => uint128) public f; 4 | mapping (uint128 => bool) public g; 5 | 6 | constructor() { 7 | val = 11; 8 | f[11] = 42; 9 | g[22] = true; 10 | g[1] = false; 11 | f[42] = 1; 12 | } 13 | 14 | function setf(uint128 value, uint key) external returns (uint) { 15 | f[key] = value; 16 | return 1; 17 | } 18 | 19 | function setg(bool value, uint128 key) external returns (uint) { 20 | g[key] = value; 21 | return 1; 22 | } 23 | } -------------------------------------------------------------------------------- /tests/hevm/pass/maps/maps.act: -------------------------------------------------------------------------------- 1 | constructor of Map 2 | interface constructor() 3 | 4 | iff 5 | 6 | CALLVALUE == 0 7 | 8 | creates 9 | uint256 val := 0 10 | mapping(uint => uint) f := [0 := 42] 11 | 12 | behaviour val of Map 13 | interface val() 14 | 15 | iff 16 | 17 | CALLVALUE == 0 18 | 19 | returns pre(val) 20 | 21 | behaviour f of Map 22 | interface f(uint x) 23 | 24 | iff 25 | 26 | CALLVALUE == 0 27 | 28 | returns pre(f[x]) 29 | 30 | 31 | behaviour set of Map 32 | interface set(uint value, uint key) 33 | 34 | iff 35 | 36 | CALLVALUE == 0 37 | 38 | storage 39 | 40 | f[key] => value 41 | 42 | returns 1 -------------------------------------------------------------------------------- /tests/hevm/pass/maps/maps.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.24; 2 | 3 | contract Map { 4 | uint256 public val; 5 | mapping (uint => uint) public f; 6 | 7 | constructor () { 8 | val = 0; 9 | f[0] = 42; 10 | } 11 | 12 | 13 | function set(uint value, uint key) public returns (uint) { 14 | f[key] = value; 15 | return 1; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/hevm/pass/multi/multi.act: -------------------------------------------------------------------------------- 1 | constructor of A 2 | interface constructor() 3 | 4 | iff 5 | CALLVALUE == 0 6 | 7 | creates 8 | uint x := 0 9 | 10 | behaviour set_x of A 11 | interface set_x(uint z) 12 | 13 | iff 14 | CALLVALUE == 0 15 | 16 | storage 17 | x => z 18 | 19 | 20 | constructor of B 21 | interface constructor() 22 | 23 | iff 24 | CALLVALUE == 0 25 | 26 | creates 27 | uint y := 0 28 | A a := create A() 29 | 30 | behaviour remote of B 31 | interface remote(uint z) 32 | 33 | iff 34 | CALLVALUE == 0 35 | 36 | storage 37 | a.x => z 38 | 39 | returns 0 40 | 41 | 42 | behaviour multi of B 43 | interface multi(uint z) 44 | 45 | iff 46 | CALLVALUE == 0 47 | storage 48 | y => z 49 | a.x => 42 -------------------------------------------------------------------------------- /tests/hevm/pass/multi/multi.sol: -------------------------------------------------------------------------------- 1 | contract A { 2 | uint x; 3 | 4 | constructor () { 5 | x = 0; 6 | } 7 | 8 | function set_x(uint z) public { 9 | x = z; 10 | } 11 | } 12 | 13 | contract B { 14 | 15 | uint y; 16 | A a; 17 | 18 | constructor() { 19 | y = 0; 20 | a = new A(); 21 | } 22 | 23 | function remote(uint z) public returns (uint){ 24 | a.set_x(z); 25 | return 0; 26 | } 27 | 28 | function multi(uint z) public { 29 | y = z; 30 | a.set_x(42); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/hevm/pass/multi2/multi2.act: -------------------------------------------------------------------------------- 1 | constructor of A 2 | interface constructor(uint z) 3 | 4 | iff 5 | CALLVALUE == 0 6 | inRange(uint256, z + 1) 7 | 8 | creates 9 | uint x := z + 1 10 | 11 | 12 | behaviour x of A 13 | interface x() 14 | 15 | iff 16 | CALLVALUE == 0 17 | 18 | returns 19 | pre(x) 20 | 21 | behaviour set_x of A 22 | interface set_x(uint z) 23 | 24 | iff 25 | CALLVALUE == 0 26 | 27 | storage 28 | x => z 29 | 30 | constructor of B 31 | interface constructor(uint u) 32 | 33 | iff 34 | CALLVALUE == 0 35 | inRange(uint256, u + 1) 36 | 37 | creates 38 | uint y := 0 39 | A a := create A(u) -------------------------------------------------------------------------------- /tests/hevm/pass/multi2/multi2.sol: -------------------------------------------------------------------------------- 1 | contract A { 2 | uint public x; 3 | 4 | constructor (uint z) { 5 | x = z + 1; 6 | } 7 | 8 | function set_x(uint z) public { 9 | x = z; 10 | } 11 | } 12 | 13 | contract B { 14 | 15 | uint y; 16 | A a; 17 | 18 | constructor(uint u) { 19 | y = 0; 20 | a = new A(u); 21 | } 22 | 23 | /* TODO */ 24 | /* function remote(uint z) public { */ 25 | /* a.set_x(z); */ 26 | /* } */ 27 | 28 | /* function multi(uint z) public { */ 29 | /* y = 1; */ 30 | /* a.set_x(z); */ 31 | /* } */ 32 | } 33 | -------------------------------------------------------------------------------- /tests/hevm/pass/multi3/multi3.act: -------------------------------------------------------------------------------- 1 | // contract A 2 | 3 | constructor of A 4 | interface constructor(uint z) 5 | 6 | iff 7 | CALLVALUE == 0 8 | inRange(uint256, z + 1) 9 | 10 | creates 11 | uint x := z + 1 12 | 13 | 14 | behaviour x of A 15 | interface x() 16 | 17 | iff 18 | CALLVALUE == 0 19 | 20 | returns 21 | pre(x) 22 | 23 | behaviour add_x of A 24 | interface add_x(uint y) 25 | 26 | iff 27 | CALLVALUE == 0 28 | inRange(uint256, x + y) 29 | 30 | storage 31 | 32 | x => x + y 33 | 34 | 35 | // contract B 36 | constructor of B 37 | interface constructor(uint i) 38 | iff 39 | CALLVALUE == 0 40 | inRange(uint256, i + 42) 41 | 42 | creates 43 | uint z := i + 42 44 | A a := create A(i) 45 | 46 | behaviour incr_z of B 47 | interface incr_z() 48 | 49 | iff 50 | CALLVALUE == 0 51 | inRange(uint256, z + 1) 52 | 53 | storage 54 | 55 | z => z + 1 56 | 57 | behaviour a_add_x of B 58 | interface a_add_x(uint y) 59 | 60 | iff 61 | CALLVALUE == 0 62 | inRange(uint256, a.x + y) 63 | 64 | storage 65 | 66 | a.x => a.x + y 67 | 68 | 69 | // contract C 70 | constructor of C 71 | interface constructor(uint u) 72 | 73 | iff 74 | CALLVALUE == 0 75 | inRange(uint256, u + 1) 76 | inRange(uint256, u + 42) 77 | 78 | creates 79 | uint y := 0 80 | A a := create A(u) 81 | B b := create B(u) 82 | 83 | 84 | behaviour remote of C 85 | interface remote(uint z) 86 | 87 | iff 88 | CALLVALUE == 0 89 | inRange(uint256, b.z + 1) 90 | inRange(uint256, b.a.x + z) 91 | 92 | storage 93 | 94 | b.z => b.z + 1 95 | b.a.x => b.a.x + z 96 | 97 | returns z -------------------------------------------------------------------------------- /tests/hevm/pass/multi3/multi3.sol: -------------------------------------------------------------------------------- 1 | contract A { 2 | uint public x; 3 | 4 | constructor (uint z) { 5 | x = z + 1; 6 | } 7 | 8 | function add_x(uint y) public { 9 | x = x + y; 10 | } 11 | } 12 | 13 | contract B { 14 | uint z; 15 | A a; 16 | 17 | constructor (uint i) { 18 | z = i + 42; 19 | a = new A(i); 20 | } 21 | 22 | function incr_z() public { 23 | z = z + 1; 24 | } 25 | 26 | function a_add_x(uint y) public { 27 | a.add_x(y); 28 | } 29 | } 30 | 31 | contract C { 32 | 33 | uint y; 34 | A a; 35 | B b; 36 | 37 | constructor(uint u) { 38 | y = 0; 39 | a = new A(u); 40 | b = new B(u); 41 | } 42 | 43 | function remote(uint z) public returns (uint) { 44 | b.incr_z(); 45 | b.a_add_x(z); 46 | return z; 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /tests/hevm/pass/multi4/multi4.act: -------------------------------------------------------------------------------- 1 | // contract A 2 | 3 | constructor of A 4 | interface constructor(uint z) 5 | 6 | iff 7 | CALLVALUE == 0 8 | inRange(uint256, z + 1) 9 | 10 | creates 11 | uint x := z + 1 12 | 13 | 14 | behaviour x of A 15 | interface x() 16 | 17 | iff 18 | CALLVALUE == 0 19 | 20 | returns 21 | pre(x) 22 | 23 | behaviour add_x of A 24 | interface add_x(uint y) 25 | 26 | iff 27 | CALLVALUE == 0 28 | inRange(uint256, x + y) 29 | 30 | storage 31 | 32 | x => x + y 33 | 34 | 35 | // contract B 36 | constructor of B 37 | interface constructor(uint i) 38 | iff 39 | CALLVALUE == 0 40 | inRange(uint256, i + 42) 41 | 42 | creates 43 | uint z := i + 42 44 | A a := create A(i) 45 | 46 | behaviour foo of B 47 | interface foo() 48 | 49 | iff 50 | CALLVALUE == 0 51 | 52 | storage 53 | a => create A(42) 54 | -------------------------------------------------------------------------------- /tests/hevm/pass/multi4/multi4.sol: -------------------------------------------------------------------------------- 1 | contract A { 2 | uint public x; 3 | 4 | constructor (uint z) { 5 | x = z + 1; 6 | } 7 | 8 | function add_x(uint y) public { 9 | x = x + y; 10 | } 11 | } 12 | 13 | contract B { 14 | uint z; 15 | A a; 16 | 17 | constructor (uint i) { 18 | z = i + 42; 19 | a = new A(i); 20 | } 21 | 22 | function foo() public { 23 | a = new A(42); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/hevm/pass/safemath/.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereum/act/287d6c99e970dc7b037330bc998ab734eb6796e7/tests/hevm/pass/safemath/.json -------------------------------------------------------------------------------- /tests/hevm/pass/safemath/safemath.act: -------------------------------------------------------------------------------- 1 | behaviour add of SafeAdd 2 | interface add(uint256 x, uint256 y) 3 | 4 | iff in range uint256 5 | 6 | x + y 7 | 8 | iff 9 | CALLVALUE == 0 10 | 11 | returns x + y 12 | 13 | behaviour mul of SafeAdd 14 | interface mul(uint256 x, uint256 y) 15 | 16 | iff in range uint256 17 | 18 | x * y 19 | 20 | iff 21 | 22 | CALLVALUE == 0 23 | 24 | returns x * y 25 | 26 | behaviour double of SafeAdd 27 | interface double(uint256 x) 28 | 29 | iff in range uint256 30 | 31 | 2 * x 32 | 33 | iff 34 | 35 | CALLVALUE == 0 36 | 37 | returns 2 * x -------------------------------------------------------------------------------- /tests/hevm/pass/safemath/safemath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21; 2 | 3 | contract SafeAdd { 4 | 5 | constructor () payable { } 6 | 7 | function add(uint x, uint y) public pure returns (uint) { 8 | return (x + y); 9 | } 10 | 11 | function mul(uint x, uint y) public pure returns (uint) { 12 | return (x * y); 13 | } 14 | 15 | function double(uint x) public pure returns (uint) { 16 | return (2*x); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /tests/hevm/pass/shape/shape.act: -------------------------------------------------------------------------------- 1 | // contract A 2 | constructor of A 3 | interface constructor(uint z) 4 | 5 | iff 6 | CALLVALUE == 0 7 | 8 | creates 9 | uint x := z 10 | 11 | 12 | behaviour x of A 13 | interface x() 14 | 15 | iff 16 | CALLVALUE == 0 17 | 18 | returns 19 | pre(x) 20 | 21 | behaviour set_x of A 22 | interface set_x(uint z) 23 | 24 | iff 25 | CALLVALUE == 0 26 | 27 | storage 28 | x => z 29 | 30 | // contract B 31 | constructor of B 32 | interface constructor(uint z) 33 | iff 34 | CALLVALUE == 0 35 | 36 | creates 37 | uint y := z 38 | A a := create A(0) 39 | 40 | behaviour y of B 41 | interface y() 42 | 43 | iff 44 | CALLVALUE == 0 45 | 46 | returns 47 | pre(y) 48 | 49 | behaviour a of B 50 | interface a() 51 | 52 | iff 53 | CALLVALUE == 0 54 | 55 | returns 56 | pre(a) 57 | 58 | 59 | // contract C 60 | constructor of C 61 | interface constructor(address y) 62 | pointers 63 | y |-> B 64 | 65 | iff 66 | CALLVALUE == 0 67 | 68 | creates 69 | A a := y.a 70 | B b := y 71 | 72 | behaviour change of C 73 | interface change() 74 | 75 | iff 76 | 77 | CALLVALUE == 0 78 | 79 | storage 80 | b.a.x => 17 -------------------------------------------------------------------------------- /tests/hevm/pass/shape/shape.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.0; 2 | 3 | contract A { 4 | uint public x; 5 | 6 | constructor (uint z) { 7 | x = z; 8 | } 9 | 10 | function set_x(uint z) public{ 11 | x = z; 12 | } 13 | } 14 | 15 | contract B { 16 | uint public y; 17 | A public a; 18 | 19 | constructor (uint z) { 20 | y = z; 21 | a = new A(0); 22 | } 23 | } 24 | 25 | contract C { 26 | A a; 27 | B b; 28 | 29 | constructor (address y) { 30 | a = B(y).a(); 31 | b = B(y); 32 | } 33 | 34 | function change() public { 35 | a.set_x(17); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/hevm/pass/simple/simple.act: -------------------------------------------------------------------------------- 1 | constructor of A 2 | interface constructor() 3 | 4 | iff 5 | 6 | CALLVALUE == 0 7 | 8 | creates 9 | 10 | mapping (uint=>uint) x := [] 11 | 12 | behaviour f of A 13 | interface f() 14 | 15 | iff 16 | 17 | CALLVALUE == 0 18 | 19 | storage 20 | 21 | x[0] => 1 22 | 23 | returns 1 -------------------------------------------------------------------------------- /tests/hevm/pass/simple/simple.sol: -------------------------------------------------------------------------------- 1 | contract A { 2 | mapping(uint=>uint) x; 3 | 4 | function f() external returns (uint) { 5 | x[0] = 1; 6 | return 1; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/hevm/pass/state_machine/state_machine.act: -------------------------------------------------------------------------------- 1 | constructor of StateMachine 2 | interface constructor() 3 | 4 | iff 5 | 6 | CALLVALUE == 0 7 | 8 | creates 9 | uint256 x := 0 10 | 11 | invariants 12 | x <= 2 13 | 14 | behaviour f of StateMachine 15 | interface f() 16 | 17 | iff 18 | 19 | x == 0 20 | CALLVALUE == 0 21 | 22 | storage 23 | x => 1 24 | 25 | behaviour g of StateMachine 26 | interface g() 27 | 28 | iff 29 | 30 | CALLVALUE == 0 31 | x == 1 32 | 33 | storage 34 | x => 2 35 | 36 | behaviour h of StateMachine 37 | interface h() 38 | 39 | iff 40 | CALLVALUE == 0 41 | x == 2 42 | 43 | storage 44 | x => 0 45 | -------------------------------------------------------------------------------- /tests/hevm/pass/state_machine/state_machine.sol: -------------------------------------------------------------------------------- 1 | contract StateMachine { 2 | uint x; 3 | 4 | constructor () { 5 | x = 0; 6 | } 7 | 8 | 9 | function f() public { 10 | require(x == 0); 11 | x = 1; 12 | } 13 | 14 | function g() public { 15 | require(x == 1); 16 | x = 2; 17 | } 18 | 19 | function h() public { 20 | require(x == 2); 21 | x = 0; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/hevm/pass/transfer-simple/transfer-simple.act: -------------------------------------------------------------------------------- 1 | constructor of Token 2 | interface constructor(uint _totalSupply) 3 | 4 | iff CALLVALUE == 0 5 | 6 | creates 7 | mapping(address => uint) balanceOf := [CALLER := _totalSupply] 8 | 9 | behaviour balanceOf of Token 10 | interface balanceOf(address x) 11 | 12 | iff CALLVALUE == 0 13 | 14 | returns 15 | pre(balanceOf[x]) 16 | 17 | behaviour transfer of Token 18 | interface transfer(uint256 value, address to) 19 | 20 | iff 21 | 22 | CALLVALUE == 0 23 | CALLER =/= to => inRange(uint256, balanceOf[to] + value) 24 | inRange(uint256,balanceOf[CALLER] - value) 25 | 26 | case CALLER =/= to: 27 | 28 | storage 29 | 30 | balanceOf[CALLER] => balanceOf[CALLER] - value 31 | balanceOf[to] => balanceOf[to] + value 32 | 33 | returns 1 34 | 35 | case CALLER == to: 36 | 37 | returns 1 -------------------------------------------------------------------------------- /tests/hevm/pass/transfer-simple/transfer-simple.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.8.0; 2 | 3 | contract Token { 4 | mapping (address => uint) public balanceOf; 5 | 6 | constructor(uint _totalSupply) { 7 | balanceOf[msg.sender] = _totalSupply; 8 | } 9 | 10 | 11 | function transfer(uint256 value, address to) public returns (uint) { 12 | balanceOf[msg.sender] = balanceOf[msg.sender] - value; 13 | balanceOf[to] = balanceOf[to] + value; 14 | return 1; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /tests/hevm/pass/transfer/transfer.act: -------------------------------------------------------------------------------- 1 | constructor of Token 2 | interface constructor(string _symbol, string _name, string _version, uint _totalSupply) 3 | 4 | creates 5 | 6 | string name := _name 7 | string symbol := _symbol 8 | uint256 totalSupply := _totalSupply 9 | mapping(address => uint) balanceOf := [CALLER := _totalSupply] 10 | mapping(address=>mapping(address=>uint)) allowance := [] 11 | 12 | invariants 13 | 14 | totalSupply == _totalSupply 15 | 16 | 17 | behaviour transfer of Token 18 | interface transfer(uint256 value, address to) 19 | 20 | iff 21 | 22 | CALLVALUE == 0 23 | value <= balanceOf[CALLER] 24 | CALLER =/= to => balanceOf[to] + value < 2^256 25 | 26 | case CALLER =/= to: 27 | 28 | storage 29 | 30 | balanceOf[CALLER] => balanceOf[CALLER] - value 31 | balanceOf[to] => balanceOf[to] + value 32 | 33 | returns 1 34 | 35 | case CALLER == to: 36 | 37 | storage 38 | 39 | balanceOf[CALLER] 40 | balanceOf[to] 41 | 42 | returns 1 43 | 44 | 45 | behaviour transferFrom of Token 46 | interface transferFrom(address src, address dst, uint amount) 47 | 48 | iff 49 | 50 | amount <= balanceOf[CALLER] 51 | src =/= dst => balanceOf[dst] + amount < 2^256 52 | CALLER =/= src => 0 <= allowance[src][CALLER] - amount 53 | CALLVALUE == 0 54 | 55 | case src =/= dst and CALLER == src: 56 | 57 | storage 58 | 59 | balanceOf[CALLER] 60 | allowance[src][CALLER] 61 | balanceOf[src] => balanceOf[src] - amount 62 | balanceOf[dst] => balanceOf[dst] + amount 63 | 64 | returns 1 65 | 66 | case src =/= dst and CALLER =/= src and allowance[src][CALLER] == 2^256 - 1: 67 | 68 | storage 69 | 70 | balanceOf[CALLER] 71 | allowance[src][CALLER] 72 | balanceOf[src] => balanceOf[src] - amount 73 | balanceOf[dst] => balanceOf[dst] + amount 74 | 75 | returns 1 76 | 77 | case src =/= dst and CALLER =/= src and allowance[src][CALLER] < 2^256 - 1: 78 | 79 | storage 80 | 81 | balanceOf[CALLER] 82 | allowance[src][CALLER] => allowance[src][CALLER] - amount 83 | balanceOf[src] => balanceOf[src] - amount 84 | balanceOf[dst] => balanceOf[dst] + amount 85 | 86 | returns 1 87 | 88 | case src == dst: 89 | 90 | storage 91 | 92 | balanceOf[CALLER] 93 | allowance[src][CALLER] 94 | balanceOf[src] 95 | balanceOf[dst] 96 | 97 | returns 1 98 | -------------------------------------------------------------------------------- /tests/hevm/pass/transfer/transfer.sol: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2020 Martin Lundfall 2 | 3 | // This program is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU Affero General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // This program is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU Affero General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU Affero General Public License 14 | // along with this program. If not, see . 15 | 16 | pragma solidity >=0.4.24; 17 | 18 | contract Token { 19 | // --- ERC20 Data --- 20 | uint8 constant public decimals = 18; 21 | string public name; 22 | string public symbol; 23 | uint256 public totalSupply; 24 | 25 | mapping (address => uint) public balanceOf; 26 | mapping (address => mapping (address => uint)) public allowance; 27 | 28 | event Approval(address indexed holder, address indexed spender, uint value); 29 | event Transfer(address indexed from, address indexed to, uint value); 30 | 31 | function add(uint x, uint y) internal pure returns (uint z) { 32 | require((z = x + y) >= x, "math-add-overflow"); 33 | } 34 | function sub(uint x, uint y) internal pure returns (uint z) { 35 | require((z = x - y) <= x, "math-sub-underflow"); 36 | } 37 | 38 | constructor(string memory symbol_, string memory name_, string memory version_, uint256 chainId_, uint _totalSupply) public { 39 | symbol = symbol_; 40 | name = name_; 41 | totalSupply = _totalSupply; 42 | balanceOf[msg.sender] = _totalSupply; 43 | } 44 | 45 | // --- Token --- 46 | function transfer(address to, uint value) public returns (bool) { 47 | transferFrom(msg.sender, to, value); 48 | return true; 49 | } 50 | function transferFrom(address from, address to, uint value) 51 | public returns (bool) 52 | { 53 | if (from != msg.sender && allowance[from][msg.sender] != uint(-1)) { 54 | allowance[from][msg.sender] = sub(allowance[from][msg.sender], value); 55 | } 56 | balanceOf[from] = sub(balanceOf[from], value); 57 | balanceOf[to] = add(balanceOf[to], value); 58 | emit Transfer(from, to, value); 59 | return true; 60 | } 61 | function approve(address spender, uint value) public returns (bool) { 62 | allowance[msg.sender][spender] = value; 63 | emit Approval(msg.sender, spender, value); 64 | return true; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/invariants/fail/all_types.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor(uint _a, uint _b, bool _c, bool _d, string _e, string _f) 3 | 4 | creates 5 | 6 | uint a := _a 7 | bool b := _c 8 | string c := _e 9 | 10 | invariants 11 | 12 | a == _b 13 | b == _d 14 | c == _f 15 | -------------------------------------------------------------------------------- /tests/invariants/fail/concretebase.act: -------------------------------------------------------------------------------- 1 | constructor of Exponentiation 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint256 thirtytwo := (10 - 2 * 4) ^ (100 - 5 * 19) 7 | 8 | invariants 9 | 10 | thirtytwo == 33 11 | -------------------------------------------------------------------------------- /tests/invariants/fail/concreteexponent.act: -------------------------------------------------------------------------------- 1 | constructor of Exponentiation 2 | interface constructor(uint256 x) 3 | 4 | creates 5 | 6 | uint256 xpow5 := x ^ (100 - 5 * 19) 7 | 8 | invariants 9 | 10 | xpow5 == x ^ 4 11 | -------------------------------------------------------------------------------- /tests/invariants/fail/constructor-type-bounds.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor() 3 | 4 | creates 5 | 6 | int totalSupply := -1 7 | 8 | invariants 9 | 10 | totalSupply > 0 11 | -------------------------------------------------------------------------------- /tests/invariants/fail/constructor_indirect_assignment.act: -------------------------------------------------------------------------------- 1 | constructor of LValue 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint y := x 7 | uint x := 2 8 | 9 | invariants 10 | 11 | y == 2 12 | -------------------------------------------------------------------------------- /tests/invariants/fail/constructor_self_reference.act: -------------------------------------------------------------------------------- 1 | constructor of LValue 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint x := x 7 | 8 | invariants 9 | 10 | x == 0 11 | -------------------------------------------------------------------------------- /tests/invariants/fail/duplicated_argument_name.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor(uint _x) 3 | 4 | creates 5 | 6 | uint x := _x 7 | 8 | invariants 9 | 10 | x == _x 11 | 12 | behaviour f of C 13 | interface f(uint _x) 14 | 15 | storage 16 | 17 | x => _x 18 | -------------------------------------------------------------------------------- /tests/invariants/fail/ethEnv.act: -------------------------------------------------------------------------------- 1 | constructor of This 2 | interface constructor() 3 | 4 | creates 5 | 6 | address this := THIS 7 | uint value := CALLVALUE 8 | uint depth := CALLDEPTH 9 | address origin := ORIGIN 10 | bytes32 hash := BLOCKHASH 11 | uint number := BLOCKNUMBER 12 | uint difficulty := DIFFICULTY 13 | uint chainid := CHAINID 14 | uint gaslimit := GASLIMIT 15 | address coinbase := COINBASE 16 | uint timestamp := TIMESTAMP 17 | uint nonce := NONCE 18 | 19 | invariants 20 | 21 | this == THIS 22 | value == CALLVALUE 23 | depth == CALLDEPTH 24 | hash == BLOCKHASH 25 | number == BLOCKNUMBER 26 | difficulty == DIFFICULTY 27 | chainid == CHAINID 28 | gaslimit == GASLIMIT 29 | coinbase == COINBASE 30 | timestamp == TIMESTAMP 31 | nonce == NONCE 32 | 33 | behaviour break of This 34 | interface break() 35 | 36 | storage 37 | 38 | this => 1 39 | value => 1 40 | depth => 1 41 | number => 1 42 | difficulty => 1 43 | chainid => 1 44 | gaslimit => 1 45 | coinbase => 1 46 | timestamp => 1 47 | nonce => 1 48 | hash => 1 49 | -------------------------------------------------------------------------------- /tests/invariants/fail/non-inductive.act: -------------------------------------------------------------------------------- 1 | // The invariant x < 9 is true, but cannot be proved as it is not inductive. 2 | 3 | // If we assume that the invariant holds at the start of the call to j, then 4 | // there is an execution path (x == 7) where the invariant does not hold over 5 | // the post state. 6 | 7 | // A stronger invariant (e.g. x < 3) can succesfully be proven 8 | 9 | constructor of C 10 | interface constructor() 11 | 12 | creates 13 | 14 | uint x := 0 15 | 16 | invariants 17 | 18 | x < 9 19 | 20 | behaviour f of C 21 | interface f() 22 | 23 | case x == 0: 24 | 25 | storage 26 | 27 | x => 1 28 | 29 | behaviour g of C 30 | interface g() 31 | 32 | case x == 1: 33 | 34 | storage 35 | 36 | x => 2 37 | 38 | behaviour j of C 39 | interface j() 40 | 41 | case x == 7: 42 | 43 | storage 44 | 45 | x => 100 46 | -------------------------------------------------------------------------------- /tests/invariants/fail/symbolicexponent.act: -------------------------------------------------------------------------------- 1 | constructor of Exponentiation 2 | interface constructor(uint x) 3 | 4 | creates 5 | 6 | uint256 exponential := (10 - 2 * 4) ^ (100 - x * 19) 7 | 8 | invariants 9 | 10 | exponential == 32 11 | -------------------------------------------------------------------------------- /tests/invariants/pass/all_types.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor(uint _a, bool _b, string _c) 3 | 4 | creates 5 | 6 | uint a := _a 7 | bool b := _b 8 | string c := _c 9 | 10 | invariants 11 | 12 | a == _a 13 | b == _b 14 | c == _c 15 | -------------------------------------------------------------------------------- /tests/invariants/pass/amm.act: -------------------------------------------------------------------------------- 1 | constructor of Amm 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint256 reserve0 := 1000 7 | uint256 reserve1 := 1000 8 | 9 | invariants 10 | 11 | 1000 * 1000 <= reserve0 * reserve1 12 | 13 | behaviour swap0 of Amm 14 | interface swap0(uint256 amt) 15 | 16 | iff in range uint256 17 | 18 | reserve0 + amt 19 | 20 | storage 21 | 22 | reserve0 => reserve0 + amt 23 | reserve1 => (reserve0 * reserve1) / (reserve0 + amt) + 1 24 | 25 | behaviour swap1 of Amm 26 | interface swap1(uint256 amt) 27 | 28 | iff in range uint256 29 | 30 | reserve1 + amt 31 | 32 | storage 33 | 34 | reserve0 => (reserve0 * reserve1) / (reserve1 + amt) + 1 35 | reserve1 => reserve1 + amt 36 | -------------------------------------------------------------------------------- /tests/invariants/pass/case.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor() 3 | 4 | creates 5 | uint x := 0 6 | 7 | invariants 8 | x == 0 9 | 10 | behaviour bar of C 11 | interface bar(uint z) 12 | 13 | iff 14 | CALLVALUE == 0 15 | 16 | case z == 0: 17 | 18 | storage 19 | x => z 20 | 21 | case 0 < z: 22 | 23 | storage 24 | x => 0 -------------------------------------------------------------------------------- /tests/invariants/pass/concretebase.act: -------------------------------------------------------------------------------- 1 | constructor of Exponentiation 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint256 thirtytwo := (10 - 2 * 4) ^ (100 - 5 * 19) 7 | 8 | invariants 9 | 10 | thirtytwo == 32 11 | -------------------------------------------------------------------------------- /tests/invariants/pass/concreteexponent.act: -------------------------------------------------------------------------------- 1 | constructor of Exponentiation 2 | interface constructor(uint256 x) 3 | 4 | creates 5 | 6 | uint256 xpow5 := x ^ (100 - 5 * 19) 7 | 8 | invariants 9 | 10 | xpow5 == x ^ 5 11 | -------------------------------------------------------------------------------- /tests/invariants/pass/constructor-type-bounds.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor(uint24 cd) 3 | 4 | creates 5 | 6 | int totalSupply := -1 7 | uint x := cd 8 | uint val := CALLVALUE 9 | 10 | invariants 11 | 12 | totalSupply == -1 13 | val >= CALLVALUE 14 | x == cd 15 | -------------------------------------------------------------------------------- /tests/invariants/pass/ethEnv.act: -------------------------------------------------------------------------------- 1 | constructor of This 2 | interface constructor() 3 | 4 | creates 5 | 6 | address this := THIS 7 | uint value := CALLVALUE 8 | uint depth := CALLDEPTH 9 | address origin := ORIGIN 10 | bytes32 hash := BLOCKHASH 11 | uint number := BLOCKNUMBER 12 | uint difficulty := DIFFICULTY 13 | uint chainid := CHAINID 14 | uint gaslimit := GASLIMIT 15 | address coinbase := COINBASE 16 | uint timestamp := TIMESTAMP 17 | uint nonce := NONCE 18 | 19 | invariants 20 | 21 | this == THIS 22 | value == CALLVALUE 23 | depth == CALLDEPTH 24 | hash == BLOCKHASH 25 | number == BLOCKNUMBER 26 | difficulty == DIFFICULTY 27 | chainid == CHAINID 28 | gaslimit == GASLIMIT 29 | coinbase == COINBASE 30 | timestamp == TIMESTAMP 31 | nonce == NONCE 32 | -------------------------------------------------------------------------------- /tests/invariants/pass/homogeneous.act: -------------------------------------------------------------------------------- 1 | constructor of Homogeneous 2 | interface constructor() 3 | 4 | creates 5 | uint256 x := 3 6 | uint256 y := 5 7 | uint256 z := 15 8 | 9 | invariants 10 | x * y == z 11 | 12 | 13 | behaviour f of Homogeneous 14 | interface f(uint256 scalar) 15 | 16 | iff 17 | x * scalar > x 18 | z * scalar > z 19 | 20 | storage 21 | x => x * scalar 22 | z => z * scalar 23 | 24 | 25 | behaviour g of Homogeneous 26 | interface g(uint256 scalar) 27 | 28 | iff 29 | y * scalar > y 30 | z * scalar > z 31 | 32 | storage 33 | y => y * scalar 34 | z => z * scalar 35 | -------------------------------------------------------------------------------- /tests/invariants/pass/ite.act: -------------------------------------------------------------------------------- 1 | constructor of ITE 2 | interface constructor(uint256 y) 3 | 4 | creates 5 | 6 | uint256 x := if y == 2 then 1 else 4 7 | 8 | invariants 9 | 10 | x < 5 11 | 12 | behaviour f of ITE 13 | interface f(int56 z) 14 | 15 | case z < 10: 16 | 17 | storage 18 | 19 | x => if z == 5 then 4 else 1 20 | 21 | case 10 <= z and z < 100: 22 | 23 | storage 24 | 25 | x => if z == 50 then 3 else 2 26 | 27 | 28 | 29 | case z >= 100: 30 | 31 | storage 32 | 33 | x => if z == 50 then 3 else 2 34 | -------------------------------------------------------------------------------- /tests/invariants/pass/log.act: -------------------------------------------------------------------------------- 1 | constructor of Log 2 | interface constructor(uint256 _n) 3 | 4 | iff 5 | _n > 1 6 | 7 | creates 8 | uint256 n := _n 9 | uint256 count := 0 10 | 11 | invariants 12 | n > 1 or count > 0 13 | 14 | behaviour f of Log 15 | interface f() 16 | 17 | iff 18 | n > 1 19 | 20 | storage 21 | n => n / 2 22 | count => count + 1 23 | -------------------------------------------------------------------------------- /tests/invariants/pass/mapping-assignments.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint a := 0 7 | mapping(uint=>uint) b := [0 := 1] 8 | 9 | invariants 10 | 11 | b[a] == 1 12 | -------------------------------------------------------------------------------- /tests/invariants/pass/mutex.act: -------------------------------------------------------------------------------- 1 | constructor of Mutex 2 | interface constructor() 3 | 4 | creates 5 | 6 | bool lock := false 7 | uint256 x := 0 8 | 9 | invariants 10 | 11 | not lock 12 | 13 | behaviour f of Mutex 14 | interface setX(uint256 _x) 15 | 16 | iff 17 | not lock 18 | 19 | storage 20 | x => _x 21 | -------------------------------------------------------------------------------- /tests/invariants/pass/state_machine.act: -------------------------------------------------------------------------------- 1 | constructor of StateMachine 2 | interface constructor() 3 | 4 | creates 5 | 6 | uint256 x := 0 7 | 8 | invariants 9 | 10 | x <= 2 11 | 12 | behaviour f of StateMachine 13 | interface f() 14 | 15 | iff x == 0 16 | storage 17 | x => 1 18 | 19 | behaviour g of StateMachine 20 | interface g() 21 | 22 | iff x == 1 23 | storage 24 | x => 2 25 | 26 | behaviour h of StateMachine 27 | interface h() 28 | 29 | iff x == 2 30 | storage 31 | x => 0 32 | -------------------------------------------------------------------------------- /tests/invariants/pass/type_constraints.act: -------------------------------------------------------------------------------- 1 | constructor of AllEnvVars 2 | interface constructor() 3 | 4 | creates 5 | uint value := 5 6 | 7 | invariants 8 | value >= 0 9 | -------------------------------------------------------------------------------- /tests/invariants/pass/type_constraints_env.act: -------------------------------------------------------------------------------- 1 | constructor of AllEnvVars 2 | interface constructor() 3 | 4 | creates 5 | uint value := CALLVALUE 6 | 7 | invariants 8 | value >= 0 9 | -------------------------------------------------------------------------------- /tests/postconditions/fail/bad_assignment.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor(uint24 _x) 3 | 4 | creates 5 | 6 | uint24 x := _x 7 | 8 | behaviour f of C 9 | interface f() 10 | 11 | storage 12 | 13 | x => 2 14 | 15 | ensures 16 | 17 | post(x) == 3 18 | -------------------------------------------------------------------------------- /tests/postconditions/fail/bad_assignment_prestate.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor(uint24 _x) 3 | 4 | creates 5 | 6 | uint24 x := _x 7 | 8 | behaviour f of C 9 | interface f(uint24 y) 10 | 11 | storage 12 | 13 | x => x + y 14 | 15 | ensures 16 | 17 | pre(x) == post(x) + y 18 | -------------------------------------------------------------------------------- /tests/postconditions/pass/amm.act: -------------------------------------------------------------------------------- 1 | constructor of Token 2 | interface constructor(uint _supply) 3 | 4 | iff 5 | CALLVALUE == 0 6 | 7 | creates 8 | mapping(address => uint) balanceOf := [CALLER := _supply] 9 | 10 | behaviour balanceOf of Token 11 | interface balanceOf(address a) 12 | 13 | iff 14 | 15 | CALLVALUE == 0 16 | 17 | returns pre(balanceOf[a]) 18 | 19 | 20 | behaviour transferFrom of Token 21 | interface transferFrom(uint256 value, address from, address to) 22 | 23 | iff 24 | 25 | CALLVALUE == 0 26 | from =/= to => inRange(uint256, balanceOf[to] + value) 27 | inRange(uint256,balanceOf[from] - value) 28 | 29 | case from =/= to: 30 | 31 | storage 32 | 33 | balanceOf[from] => balanceOf[from] - value 34 | balanceOf[to] => balanceOf[to] + value 35 | 36 | returns 1 37 | 38 | case from == to: 39 | 40 | returns 1 41 | 42 | 43 | constructor of Amm 44 | interface constructor(uint256 amt1, uint256 amt2) 45 | 46 | iff 47 | 48 | CALLVALUE == 0 49 | 50 | 51 | creates 52 | 53 | Token token0 := create Token(amt1) 54 | Token token1 := create Token(amt2) 55 | 56 | behaviour swap0 of Amm 57 | interface swap0(uint256 amt) 58 | 59 | iff 60 | CALLVALUE == 0 61 | inRange(uint256,token0.balanceOf[CALLER] - amt) 62 | CALLER =/= THIS => inRange(uint256,token0.balanceOf[THIS] + amt) 63 | 64 | inRange(uint256,token1.balanceOf[THIS] - ((token1.balanceOf[THIS] * amt) / (token0.balanceOf[THIS] + amt))) 65 | CALLER =/= THIS => inRange(uint256,token1.balanceOf[CALLER] + ((token1.balanceOf[THIS] * amt) / (token0.balanceOf[THIS] + amt))) 66 | 67 | token0.balanceOf[THIS] + amt =/= 0 68 | 69 | case CALLER =/= THIS: 70 | 71 | storage 72 | 73 | token0.balanceOf[CALLER] => token0.balanceOf[CALLER] - amt 74 | token0.balanceOf[THIS] => token0.balanceOf[THIS] + amt 75 | 76 | token1.balanceOf[THIS] => token1.balanceOf[THIS] - ((token1.balanceOf[THIS] * amt) / (token0.balanceOf[THIS] + amt)) 77 | token1.balanceOf[CALLER] => token1.balanceOf[CALLER] + ((token1.balanceOf[THIS] * amt) / (token0.balanceOf[THIS] + amt)) 78 | 79 | returns 1 80 | 81 | case CALLER == THIS: 82 | 83 | returns 1 84 | 85 | ensures 86 | 87 | pre(token0.balanceOf[THIS]) * pre(token1.balanceOf[THIS]) <= post(token0.balanceOf[THIS]) * post(token1.balanceOf[THIS]) 88 | post(token0.balanceOf[THIS]) * post(token1.balanceOf[THIS]) <= pre(token0.balanceOf[THIS]) * pre(token1.balanceOf[THIS]) + pre(token0.balanceOf[THIS]) + amt 89 | 90 | 91 | 92 | behaviour swap1 of Amm 93 | interface swap1(uint256 amt) 94 | 95 | iff 96 | CALLVALUE == 0 97 | inRange(uint256,token1.balanceOf[CALLER] - amt) 98 | CALLER =/= THIS => inRange(uint256,token1.balanceOf[THIS] + amt) 99 | 100 | inRange(uint256,token0.balanceOf[THIS] - ((token0.balanceOf[THIS] * amt) / (token1.balanceOf[THIS] + amt))) 101 | CALLER =/= THIS => inRange(uint256,token0.balanceOf[CALLER] + ((token0.balanceOf[THIS] * amt) / (token1.balanceOf[THIS] + amt))) 102 | 103 | token1.balanceOf[THIS] + amt =/= 0 104 | 105 | case CALLER =/= THIS: 106 | 107 | storage 108 | 109 | token1.balanceOf[CALLER] => token1.balanceOf[CALLER] - amt 110 | token1.balanceOf[THIS] => token1.balanceOf[THIS] + amt 111 | 112 | token0.balanceOf[THIS] => token0.balanceOf[THIS] - ((token0.balanceOf[THIS] * amt) / (token1.balanceOf[THIS] + amt)) 113 | token0.balanceOf[CALLER] => token0.balanceOf[CALLER] + ((token0.balanceOf[THIS] * amt) / (token1.balanceOf[THIS] + amt)) 114 | 115 | returns 1 116 | 117 | case CALLER == THIS: 118 | 119 | returns 1 120 | 121 | ensures 122 | 123 | pre(token0.balanceOf[THIS]) * pre(token1.balanceOf[THIS]) <= post(token0.balanceOf[THIS]) * post(token1.balanceOf[THIS]) 124 | post(token0.balanceOf[THIS]) * post(token1.balanceOf[THIS]) <= pre(token0.balanceOf[THIS]) * pre(token1.balanceOf[THIS]) + pre(token1.balanceOf[THIS]) + amt 125 | -------------------------------------------------------------------------------- /tests/postconditions/pass/assignment.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor(uint24 _x) 3 | 4 | creates 5 | 6 | uint24 x := _x 7 | 8 | behaviour f of C 9 | interface f() 10 | 11 | storage 12 | 13 | x => 2 14 | 15 | ensures 16 | 17 | post(x) == 2 18 | -------------------------------------------------------------------------------- /tests/postconditions/pass/assignment_prestate.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor(uint24 _x) 3 | 4 | creates 5 | 6 | uint24 x := _x 7 | 8 | behaviour f of C 9 | interface f(uint24 y) 10 | 11 | storage 12 | 13 | x => x + y 14 | 15 | ensures 16 | 17 | post(x) == pre(x) + y 18 | -------------------------------------------------------------------------------- /tests/postconditions/pass/assignment_return.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor(uint24 _x) 3 | 4 | creates 5 | 6 | uint24 x := _x 7 | 8 | behaviour f of C 9 | interface f() 10 | 11 | storage 12 | 13 | x => 2 14 | 15 | returns 16 | 17 | post(x) 18 | -------------------------------------------------------------------------------- /tests/postconditions/pass/increase.act: -------------------------------------------------------------------------------- 1 | constructor of C 2 | interface constructor(uint24 _x) 3 | 4 | creates 5 | 6 | uint24 x := _x 7 | 8 | behaviour increase of C 9 | interface increase(uint24 new_x) 10 | 11 | case new_x > x: 12 | storage 13 | x => new_x 14 | 15 | returns post(x) - pre(x) 16 | 17 | case new_x <= x: 18 | storage 19 | x => x + new_x 20 | 21 | returns post(x) - pre(x) 22 | 23 | ensures 24 | 25 | post(x) >= pre(x) 26 | -------------------------------------------------------------------------------- /tools/act.py: -------------------------------------------------------------------------------- 1 | class Act: 2 | """This class is a text representation of an Act specification for a smart contract.""" 3 | 4 | def __init__(self, contract, contractName, path): 5 | """Builds the specification text from a JSON object. 6 | 7 | Parameters 8 | ---------- 9 | contract : JSON object 10 | The contract object resulting from Solidity's standard-json compilation. 11 | contractName : str 12 | The name of the contract. 13 | path : str 14 | The path/source unit that contains the contract. 15 | """ 16 | self._name = path + "." + contractName 17 | self._stateVariables = [] 18 | self._properties = [] 19 | self._behaviours = [] 20 | 21 | if "storageLayout" in contract: 22 | for var in contract["storageLayout"]["storage"]: 23 | self.buildStateVariable(var, contract["storageLayout"]["types"]) 24 | 25 | if "abi" in contract: 26 | for function in contract["abi"]: 27 | self.buildFunction(function) 28 | 29 | def buildStateVariable(self, var, types): 30 | varDecl = var["label"] + " : " + types[var["type"]]["label"] 31 | self._stateVariables.append(varDecl) 32 | 33 | def buildFunction(self, function): 34 | """Builds the specification of a function, including property and behaviour." 35 | 36 | Parameters 37 | ---------- 38 | function : JSON object 39 | The function object from Solidity's ABI output. 40 | """ 41 | name = function["name"] 42 | fProperty = "property " + name + ".post of " + name + "\n" 43 | fBehaviour = "behaviour " + name + ".behaviour of " + self._name + "\n" 44 | fInterface = "interface " + name + "(" 45 | fInterface += ",".join([inParam["type"] + " " + inParam["name"] for inParam in function["inputs"]]) 46 | fInterface += ")" 47 | fBehaviour += fInterface + "\n" 48 | if len(function["outputs"]) > 0: 49 | fBehaviour + "returns\n\n" 50 | 51 | self._properties.append(fProperty) 52 | self._behaviours.append(fBehaviour) 53 | 54 | def spec(self): 55 | """Builds the specification string""" 56 | return "contract " + self._name + "\n\n" + "\n".join(self._stateVariables) + "\n\ninvariants\n\n" + "\n".join(self._properties) + "\n\n" + "\n".join(self._behaviours) + "\nend\n" 57 | -------------------------------------------------------------------------------- /tools/respec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | for f in `find tests/ -name "*.typed.json"`; do 4 | ff="${f%.*.*}" 5 | act type --file $ff | jq . > "$ff.typed.json" 6 | echo "wrote $ff.typed.json" 7 | done 8 | -------------------------------------------------------------------------------- /tools/solidity_to_act.py: -------------------------------------------------------------------------------- 1 | from act import Act 2 | 3 | import json 4 | import sys 5 | 6 | if __name__ == '__main__': 7 | if len(sys.argv) != 2: 8 | print("Usage: " + sys.argv[0] + " solOutput.json") 9 | sys.exit(1) 10 | 11 | solOutput = json.loads(open(sys.argv[1]).read()) 12 | for contractPath in solOutput['contracts']: 13 | for contract in solOutput['contracts'][contractPath]: 14 | print(Act(solOutput['contracts'][contractPath][contract], contract, contractPath).spec()) 15 | --------------------------------------------------------------------------------