├── .dockerignore ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── code.md ├── misc_utils ├── generate-graphs.py ├── get_source.py ├── stats-collect.py ├── transaction_scrape 2.py ├── transaction_scrape.py └── tx-stats.py ├── oyente ├── __init__.py ├── analysis.py ├── ast_helper.py ├── ast_walker.py ├── basicblock.py ├── batch_run.py ├── ethereum_data.py ├── ethereum_data1.py ├── global_params.py ├── input_helper.py ├── opcodes.py ├── oyente.py ├── run_tests.py ├── source_map.py ├── state.json ├── symExec.py ├── test_evm │ ├── __init__.py │ ├── evm_unit_test.py │ ├── global_test_params.py │ └── test_data │ │ ├── msize0.json │ │ ├── msize1.json │ │ ├── msize2.json │ │ ├── msize3.json │ │ ├── mstore0.json │ │ ├── mstore1.json │ │ ├── mstore8MemExp.json │ │ ├── mstore8WordToBigError.json │ │ ├── mstore8_0.json │ │ ├── mstore8_1.json │ │ ├── mstoreMemExp.json │ │ ├── mstoreWordToBigError.json │ │ ├── mstore_mload0.json │ │ ├── vmArithmeticTest.json │ │ ├── vmBitwiseLogicOperationTest.json │ │ └── vmPushDupSwapTest.json ├── utils.py ├── vargenerator.py └── vulnerability.py ├── pylintrc ├── setup.py └── web ├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── fonts │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.svg │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 │ ├── images │ │ ├── .keep │ │ └── remix_logo_512x512.svg │ ├── javascripts │ │ ├── application.js │ │ ├── browser-solidity.js │ │ ├── cable.js │ │ └── channels │ │ │ └── .keep │ └── stylesheets │ │ ├── application.css │ │ ├── font-awesome.min.css.scss │ │ └── pygment_trac.css ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── controllers │ ├── application_controller.rb │ ├── concerns │ │ └── .keep │ └── home_controller.rb ├── helpers │ ├── application_helper.rb │ ├── byte_code_helper.rb │ └── source_code_helper.rb ├── jobs │ └── application_job.rb ├── mailers │ ├── application_mailer.rb │ └── user_mailer.rb ├── models │ ├── application_record.rb │ └── concerns │ │ └── .keep └── views │ ├── home │ ├── analyze.json.jbuilder │ ├── analyze_bytecode.json.jbuilder │ └── index.html.erb │ ├── layouts │ ├── application.html.erb │ ├── mailer.html.erb │ └── mailer.text.erb │ └── user_mailer │ ├── analyzer_result_notification.html.erb │ ├── analyzer_result_notification.text.erb │ └── bytecode_analysis_result.html.erb ├── bin ├── bundle ├── rails ├── rake ├── setup ├── spring ├── update └── yarn ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── application_controller_renderer.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ └── wrap_parameters.rb ├── locales │ └── en.yml ├── puma.rb ├── routes.rb ├── secrets.yml └── spring.rb ├── db └── seeds.rb ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── log └── .keep ├── package.json ├── public ├── 404.html ├── 422.html ├── 500.html ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── favicon.ico └── robots.txt ├── test ├── application_system_test_case.rb ├── controllers │ └── .keep ├── fixtures │ ├── .keep │ └── files │ │ └── .keep ├── helpers │ └── .keep ├── integration │ └── .keep ├── mailers │ ├── .keep │ ├── previews │ │ └── user_mailer_preview.rb │ └── user_mailer_test.rb ├── models │ └── .keep ├── system │ └── .keep └── test_helper.rb ├── vendor └── .keep └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | .idea/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.disasm 2 | *.evm 3 | *.log 4 | *.pyc 5 | *.report 6 | *.sol 7 | 8 | .DS_Store 9 | 10 | .idea/dist/ 11 | 12 | contract_data 13 | dist/ 14 | tmp/ 15 | 16 | current_test.pickle 17 | 18 | bytecode 19 | bytecode.1 20 | 21 | !oyente/tests/contracts/*/* 22 | web/vendor/ 23 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "web/browser-solidity"] 2 | path = web/browser-solidity 3 | url = git@github.com:luongnt95/browser-solidity.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.5" 4 | - "3.6" 5 | install: 6 | - pip install -U z3 web3 requests flake8 yapf pytest pylint 7 | script: 8 | - flake8 9 | git: 10 | submodules: false 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Find a bug, a way to improve the documentation or have a feature request? Open an [issue](https://github.com/melonproject/oyente/issues/new). 2 | 3 | Or even better, send us a PR :) 4 | 5 | # Before you send PRs 6 | - Follow the [instructions](https://github.com/melonproject/oyente#full-install) to get a locally working version of oyente 7 | - Make your awesome change 8 | - Make sure the code adheres to the coding style. We use pylint with the [following configuration](https://github.com/melonproject/oyente/blob/master/pylintrc) 9 | - Make sure that the tests pass 10 | - Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 11 | 12 | # Running the tests 13 | 14 | There are two options to run the tests, which are concrete execution and symbolic execution. 15 | Before running the test you have to choose an option. There is a global variable named `UNIT_TEST` in `global_test_params.py` to set the type of the tests. 16 | 17 | ``` 18 | # Testing concrete execution 19 | UNIT_TEST = 2 20 | # Testing symbolic execution 21 | UNIT_TEST = 3 22 | ``` 23 | 24 | ``` 25 | # Start testing 26 | $ python oyente/run_tests.py 27 | ``` 28 | 29 | After running the testing program, the result would display a status for each testcase: 30 | - ```PASS```: the testcase passes 31 | - ```FAIL```: an opcode implements incorrectly 32 | - ```TIME_OUT```: it takes too long to run the testcase (set ```GLOBAL_TIMEOUT_TEST``` in ```global_params.py```) 33 | - ```UNKNOWN_INSTRUCTION```: there is an unknown opcode in the testcase 34 | - ```EXCEPTION```: the program encounters an exception while running the testcase 35 | - ```EMPTY_RESULT``` the result is invalid 36 | - ```INCORRECT_GAS``` the gas is calculated incorrectly 37 | 38 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG ETHEREUM_VERSION=alltools-v1.7.3 2 | ARG SOLC_VERSION=0.4.19 3 | 4 | FROM ethereum/client-go:${ETHEREUM_VERSION} as geth 5 | FROM ethereum/solc:${SOLC_VERSION} as solc 6 | 7 | FROM ubuntu:bionic as CLI 8 | 9 | ARG NODEREPO=node_8.x 10 | 11 | LABEL maintainer "Xiao Liang , Luong Nguyen " 12 | 13 | SHELL ["/bin/bash", "-c", "-l"] 14 | RUN apt-get update && apt-get -y upgrade 15 | RUN apt-get install -y wget unzip python-virtualenv git build-essential software-properties-common curl 16 | RUN curl -s 'https://deb.nodesource.com/gpgkey/nodesource.gpg.key' | apt-key add - 17 | RUN apt-add-repository "deb https://deb.nodesource.com/${NODEREPO} $(lsb_release -c -s) main" 18 | RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - 19 | RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list 20 | RUN apt-get update 21 | RUN apt-get install -y musl-dev golang-go python3 python3-pip python-pip \ 22 | bison zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev \ 23 | zlib1g-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 \ 24 | libxml2-dev libxslt1-dev libcurl4-openssl-dev libffi-dev nodejs yarn && \ 25 | apt-get clean 26 | 27 | RUN update-alternatives --install /usr/bin/python python /usr/bin/python2.7 1 28 | RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.6 2 29 | RUN update-alternatives --install /usr/bin/pip pip /usr/bin/pip2 1 30 | RUN update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 2 31 | RUN pip install requests web3 32 | RUN npm install npm@latest -g && npm install n --global && n stable 33 | 34 | RUN mkdir -p /deps/z3/ && wget https://github.com/Z3Prover/z3/archive/z3-4.5.0.zip -O /deps/z3/z3.zip && \ 35 | cd /deps/z3/ && unzip /deps/z3/z3.zip && \ 36 | ls /deps/z3 && mv /deps/z3/z3-z3-4.5.0/* /deps/z3/ && rm /deps/z3/z3.zip && \ 37 | python scripts/mk_make.py --python && cd build && make && make install 38 | 39 | # Instsall geth from official geth image 40 | COPY --from=geth /usr/local/bin/evm /usr/local/bin/evm 41 | 42 | # Install solc from official solc image 43 | COPY --from=solc /usr/bin/solc /usr/bin/solc 44 | 45 | COPY . /oyente/ 46 | 47 | WORKDIR /oyente/ 48 | ENTRYPOINT ["python3", "/oyente/oyente/oyente.py"] 49 | 50 | FROM CLI as WEB 51 | 52 | RUN wget -O ruby-install-0.6.1.tar.gz https://github.com/postmodern/ruby-install/archive/v0.6.1.tar.gz 53 | RUN tar -xzvf ruby-install-0.6.1.tar.gz 54 | RUN cd ruby-install-0.6.1/ && make install 55 | RUN ruby-install --system ruby 2.4.4 56 | WORKDIR /oyente/web 57 | RUN ./bin/yarn install && gem install bundler && bundle install --with development 58 | 59 | EXPOSE 3000 60 | CMD ["./bin/rails", "server"] 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Oyente 2 | ====== 3 | 4 | An Analysis Tool for Smart Contracts 5 | 6 | [![Gitter][gitter-badge]][gitter-url] 7 | [![License: GPL v3][license-badge]][license-badge-url] 8 | [![Build Status](https://travis-ci.org/melonproject/oyente.svg?branch=master)](https://travis-ci.org/melonproject/oyente) 9 | 10 | *This repository is currently maintained by Xiao Liang Yu ([@yxliang01](https://github.com/yxliang01)). If you encounter any bugs or usage issues, please feel free to create an issue on [our issue tracker](https://github.com/melonproject/oyente/issues).* 11 | 12 | ## Quick Start 13 | 14 | A container with required dependencies configured can be found [here](https://hub.docker.com/r/luongnguyen/oyente/). The image is however outdated. We are working on pushing the latest image to dockerhub for your convenience. If you experience any issue with this image, please try to build a new docker image by pulling this codebase before open an issue. 15 | 16 | To open the container, install docker and run: 17 | 18 | ``` 19 | docker pull luongnguyen/oyente && docker run -i -t luongnguyen/oyente 20 | ``` 21 | 22 | To evaluate the greeter contract inside the container, run: 23 | 24 | ``` 25 | cd /oyente/oyente && python oyente.py -s greeter.sol 26 | ``` 27 | 28 | and you are done! 29 | 30 | Note - If need the [version of Oyente](https://github.com/melonproject/oyente/tree/290f1ae1bbb295b8e61cbf0eed93dbde6f287e69) referred to in the paper, run the container from [here](https://hub.docker.com/r/hrishioa/oyente/) 31 | 32 | To run the web interface, execute 33 | `docker run -w /oyente/web -p 3000:3000 oyente:latest ./bin/rails server` 34 | 35 | ## Custom Docker image build 36 | 37 | ``` 38 | docker build -t oyente . 39 | docker run -it -p 3000:3000 -e "OYENTE=/oyente/oyente" oyente:latest 40 | ``` 41 | 42 | Open a web browser to `http://localhost:3000` for the graphical interface. 43 | 44 | ## Installation 45 | 46 | Execute a python virtualenv 47 | 48 | ``` 49 | python -m virtualenv env 50 | source env/bin/activate 51 | ``` 52 | 53 | Install Oyente via pip: 54 | 55 | ``` 56 | $ pip2 install oyente 57 | ``` 58 | Dependencies: 59 | 60 | The following require a Linux system to fufill. macOS instructions forthcoming. 61 | 62 | [solc](https://github.com/melonproject/oyente#solc) 63 | [evm](https://github.com/melonproject/oyente#evm-from-go-ethereum) 64 | 65 | ## Full installation 66 | 67 | ### Install the following dependencies 68 | #### solc 69 | ``` 70 | $ sudo add-apt-repository ppa:ethereum/ethereum 71 | $ sudo apt-get update 72 | $ sudo apt-get install solc 73 | ``` 74 | 75 | #### evm from [go-ethereum](https://github.com/ethereum/go-ethereum) 76 | 77 | 1. https://geth.ethereum.org/downloads/ or 78 | 2. By from PPA if your using Ubuntu 79 | ``` 80 | $ sudo apt-get install software-properties-common 81 | $ sudo add-apt-repository -y ppa:ethereum/ethereum 82 | $ sudo apt-get update 83 | $ sudo apt-get install ethereum 84 | ``` 85 | 86 | #### [z3](https://github.com/Z3Prover/z3/releases) Theorem Prover version 4.5.0. 87 | 88 | Download the [source code of version z3-4.5.0](https://github.com/Z3Prover/z3/releases/tag/z3-4.5.0) 89 | 90 | Install z3 using Python bindings 91 | 92 | ``` 93 | $ python scripts/mk_make.py --python 94 | $ cd build 95 | $ make 96 | $ sudo make install 97 | ``` 98 | 99 | #### [Requests](https://github.com/kennethreitz/requests/) library 100 | 101 | ``` 102 | pip install requests 103 | ``` 104 | 105 | #### [web3](https://github.com/pipermerriam/web3.py) library 106 | 107 | ``` 108 | pip install web3 109 | ``` 110 | 111 | ### Evaluating Ethereum Contracts 112 | 113 | ``` 114 | #evaluate a local solidity contract 115 | python oyente.py -s 116 | 117 | #evaluate a local solidity with option -a to verify assertions in the contract 118 | python oyente.py -a -s 119 | 120 | #evaluate a local evm contract 121 | python oyente.py -s -b 122 | 123 | #evaluate a remote contract 124 | python oyente.py -ru https://gist.githubusercontent.com/loiluu/d0eb34d473e421df12b38c12a7423a61/raw/2415b3fb782f5d286777e0bcebc57812ce3786da/puzzle.sol 125 | 126 | ``` 127 | 128 | And that's it! Run ```python oyente.py --help``` for a list of options. 129 | 130 | ## Paper 131 | 132 | The accompanying paper explaining the bugs detected by the tool can be found [here](https://www.comp.nus.edu.sg/~prateeks/papers/Oyente.pdf). 133 | 134 | ## Miscellaneous Utilities 135 | 136 | A collection of the utilities that were developed for the paper are in `misc_utils`. Use them at your own risk - they have mostly been disposable. 137 | 138 | 1. `generate-graphs.py` - Contains a number of functions to get statistics from contracts. 139 | 2. `get_source.py` - The *get_contract_code* function can be used to retrieve contract source from [EtherScan](https://etherscan.io) 140 | 3. `transaction_scrape.py` - Contains functions to retrieve up-to-date transaction information for a particular contract. 141 | 142 | ## Benchmarks 143 | 144 | Note: This is an improved version of the tool used for the paper. Benchmarks are not for direct comparison. 145 | 146 | To run the benchmarks, it is best to use the docker container as it includes the blockchain snapshot necessary. 147 | In the container, run `batch_run.py` after activating the virtualenv. Results are in `results.json` once the benchmark completes. 148 | 149 | The benchmarks take a long time and a *lot* of RAM in any but the largest of clusters, beware. 150 | 151 | Some analytics regarding the number of contracts tested, number of contracts analysed etc. is collected when running this benchmark. 152 | 153 | ## Contributing 154 | 155 | Checkout out our [contribution guide](https://github.com/melonproject/oyente/blob/master/CONTRIBUTING.md) and the code structure [here](https://github.com/melonproject/oyente/blob/master/code.md). 156 | 157 | 158 | [gitter-badge]: https://img.shields.io/gitter/room/melonproject/oyente.js.svg?style=flat-square 159 | [gitter-url]: https://gitter.im/melonproject/oyente?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge 160 | [license-badge]: https://img.shields.io/badge/License-GPL%20v3-blue.svg?style=flat-square 161 | [license-badge-url]: ./LICENSE 162 | -------------------------------------------------------------------------------- /code.md: -------------------------------------------------------------------------------- 1 | # Code Structure 2 | 3 | ### *oyente.py* 4 | 5 | This is the main entry point to the program. Oyente is able to analyze smart contracts via the following inputs 6 | - solidity program 7 | - evm bytecode 8 | - remote contracts 9 | 10 | Other configuration options include getting the input state, setting timeouts for z3, etc. (Check ```python oyente.py --help``` or ```global_params.py``` for the full list of configuration options available). 11 | These options are collated and set in the *global_params* module which will be used during the rest of the execution. 12 | 13 | The contracts are then disassembled into opcodes using the ```evm disasm``` command. 14 | 15 | After this, the symexec module is called with the disassembled file which carries out the analyses of the contracts for various vulnerabilities (TOD, timestamp-dependence, mishandled exceptions). 16 | 17 | ### *symExec.py* 18 | 19 | The analysis starts off with the ```build_cfg_and_analyze``` function. We break up the disasm file created by oyente.py into tokens using the native tokenize python module. 20 | 21 | The *collect_vertices* and *construct_bb* functions identify the basic blocks in the program and we store them as vertices. Basic blocks are identified by using opcodes like ```JUMPDEST```, ```STOP```, ```RETURN```, ```SUICIDE```, ```JUMP``` and ```JUMPI``` as separators. Each basic block is backed by an instance of BasicBlock class defined in basicblock.py 22 | 23 | After the basic blocks are created, we start to symbolically execute each basic block with the full_sym_exec function. We get the instructions stored in each basic block and execute each of them symbolically via the sym_exec_ins function. In this function, we model each opcode as closely as possible to the behaviour described in the ethereum yellow paper. Some interesting details regarding each class of opcodes is discussed below. 24 | 25 | #### Model 26 | The stack is modelled using a simple python list. 27 | The memory is modelled as a growing list. The maximum index of used by the memory list is stored as ```current_miu_i``` variable. 28 | The storage is stored as a python object as key-value pairs. 29 | 30 | #### 0s: Stop and Arithmetic Operations, 10s: Comparison & Bitwise Logic Operations 31 | These group of opcodes is the most straightforward to implement. If one of the operands is symbolic, both of them are converted into a 256-bit symbolic variable. The arithmetic operation is carried out (symbolically, if the operands are symbolic) and the result is pushed on to the stack. 32 | 33 | #### 20s: SHA3 34 | A generic symbolic variable is created to mimic the behaviour of the SHA3 opcode 35 | 36 | #### 30s: Environmental Information, 40s: Block Information 37 | For most of these opcodes, a unique symbolic variable is generated to represent it (similar to SHA3). In some cases, to speed up the symbolic execution, concrete values for these opcodes are taken from the state.json file. This behaviour is enabled via the --state flag. We haven't found ways to robustly simulate ```CODECOPY``` and ```EXTCODESIZE``` symbolically yet. 38 | 39 | #### 40s: 50s: Stack, Memory, Storage and Flow Operations 40 | New edges which are found during analysing the ```JUMP``` and ```JUMPI``` instructions are added to the call graph on the fly. 41 | 42 | #### f0s: System operations 43 | To handle the ```CALL``` and ```CALLCODE``` opcodes, we construct symbolic expressions to ensure there are enough funds in the sender's account and the sender's address is different from the receiver's address. If these conditions hold true, we update the corresponding global state. 44 | 45 | 46 | After this, add this basic block to the list of already visited blocks and follow it to the next basic block. We also maintain the necessary path conditions required to get to the block in the ```path_conditions_and_vars``` variable. In case of instructions like JUMP, there is only one basic block to follow the program execution to. In other cases like ```JUMPI```, we first check if the branch expression is provably True or False using z3. If not, we explore both the branches by adding the branch expression and the negated branch expression to the ```path_conditions_and_vars``` variable. 47 | 48 | - Callstack attack 49 | 50 | Checking for the callstack attack is done by the *check_callstack_attack* function. If a ```CALL``` or a ```CALLCODE``` instruction is found without the ```SWAP4, POP, POP, POP, POP, ISZERO``` (or SWAP3 followed by 3 POP, etc.) following it, we flag it as being vulnerable to the callstack attack. This opcode sequence is the one generated by solc corresponding to the following recommended code pattern to prevent against the attack. 51 | 52 | ``` 53 | if (owner.send(amount)) {..} 54 | ``` 55 | 56 | - Timestamp dependence attack 57 | 58 | We find out if the ```path_conditions``` variable contains the symbolic variable corresponding to the block timestamp. If so, the program can be concluded to take a path in the program which makes use of the block timestamp, making it vulnerable to the Timestamp dependence attack. 59 | 60 | - Reentrancy bug 61 | 62 | This presence of this bug is analysed in the ```check_reentrancy_bug``` function in analysis.py. At each CALL that is encountered, we obtain the path condition for the execution before the CALL is executed. We then check if such condition with updated variables (e.g., storage values) still holds (i.e., if the call can be executed again). If so, we consider this a vulnerability, since it is possible for the callee to re-execute the call before finishing it. 63 | We also consider the case that users use `sender` and `transfer` instead of `call` function. It is safe to use `sender` and `transfer` because of the limited gas as part of `send` and `transfer`. To check whethera contract is safe or not based on the gas as part of these functions, we set a threshold to 2300 which is the amount of gas that `sender` and `transfer` provide. And then comparing the gas sent along with these functions with the threshold. If the gas is greater than the threshold, we flag the contract as being vulnerable to the reentrancy attack. Otherwise, we flag it as being safe. 64 | 65 | - Concurrency bug 66 | 67 | We track the sender, recepient and the value transferred at each ```CALL``` and ```SUICIDE``` instruction in the ```update_analysis``` function. If these values are different for different flows, we report the bug in the ```detect_money_concurrency``` function. 68 | 69 | - Assertion fails 70 | 71 | This feature is active only if the option `-a` is used. 72 | 73 | The feature verifies Solidity assertions, which tries to report `assert` fails if INVALID instruction is reachable in the program. Because INVALID can be caused from different cases other than `assert`, there would be some cases that lead to false positives due to the ambiguity between an INVALID generated by `assert` and other types of INVALID. Currently, we consider all INVALID instructions to be derived from `assert` except thoses that follow a sequences of JUMPDEST, CALLVALUE, ISZERO, PUSH, JUMPI instructions. To find the function that contains the assertion fails, we record the path that leads to the INVALID instruction. By using this path, we can trace back and find the top-level function that causes the failure in the ```check_assertions``` function in `symExec.py`. 74 | 75 | ### *vargenerator.py* 76 | 77 | This is a utility class to provide unqiue symbolic variables required for analysis 78 | 79 | ### source_map.py 80 | 81 | This is a utility class to map problematic opcodes into the source code 82 | 83 | ### Tests 84 | Testing opcodes in Oyente in order to check if opcodes are implemented correctly based on the final state of the storage and the memory. The tests are based on the [VM tests of Ethereum](http://ethdocs.org/en/latest/contracts-and-transactions/ethereum-tests/vm_tests/index.html). 85 | 86 | The flow of testing: 87 | - Load test data (using the existing EVM tests in [here](https://github.com/ethereum/tests/tree/develop/VMTests)) 88 | - Run oyente with the input was specified in the test data 89 | - Compare the results (storage, memory and gas) after running oyente with the results being specified in the test data 90 | - Report bugs 91 | 92 | #### *run_tests.py* 93 | This is the main entry point to the testing program. The program loads a specific test data file in folder ```test_evm/test_data/``` and start running `oyente.py `with the input being specified in the loaded test data to get an exit code which is returned from `oyente.py.` From this exit code the testing program can report the bug 94 | 95 | #### *evm_unit_test.py* 96 | A utility class to extract concerned sections and fields (`code`, `storage`, `out`, `gas` and `gas` in `exec` section) in the test data, run the tests, compare the results and return an exit code 97 | 98 | #### *symExec.py* 99 | ```compare_storage_and_gas_unit_test(global_state, analysis)``` starts comparing the results and return an exit code after the final opcode is implemented 100 | -------------------------------------------------------------------------------- /misc_utils/get_source.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import re 3 | import os 4 | import json 5 | from tqdm import tqdm 6 | 7 | def get_contract_code(cadd): 8 | sourcepattern = r"style='max-height: 250px; margin-top: 5px;'>([\s\S]+?)<\/pre>" 9 | namepattern = r"Contract Name:[\n.]<\/td>[\n.][.\n]([\s\S]+?)[\n.]<\/td>" 10 | command = "wget -S -O - 'https://etherscan.io/address/%s#code'" % cadd 11 | DEVNULL = open(os.devnull, 'wb') 12 | wget = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=DEVNULL) 13 | outp = wget.stdout.read() 14 | return (re.findall(namepattern, outp), re.findall(sourcepattern, outp)) 15 | 16 | savedcontracts = [] 17 | 18 | def save_callstack_source(dirname): 19 | if not dirname.endswith('/'): dirname += '/' 20 | print "Loading callstack file..." 21 | cstkfile = json.load(open('callstack_stats.json')) 22 | cstbfile = json.load(open('cterror_balances.json')) 23 | for contract in tqdm(cstkfile): 24 | name, source = get_contract_code(contract) 25 | if(len(source) <= 0): continue 26 | source = source[0] 27 | fname = name[0] if len(name) > 0 else contract 28 | # print "Saved contract %s to %s.sol" % (contract, fname) 29 | if os.path.isfile(dirname+fname+'.sol'): 30 | i=0; 31 | while os.path.isfile(dirname+fname+str(i)+'.sol'): 32 | i+=1 33 | fname += str(i) 34 | fname += ".sol" 35 | savedcontracts.append((contract, fname, cstbfile[cstkfile.index(contract)]/1000000000000000000.0)) 36 | with open(dirname + fname, 'w') as of: 37 | of.write('// '+contract+'\n// '+str(cstbfile[cstkfile.index(contract)]/1000000000000000000.0)+'\n') 38 | of.write(source) 39 | of.flush() 40 | of.close() 41 | 42 | if __name__ == '__main__': 43 | save_callstack_source('source') 44 | -------------------------------------------------------------------------------- /misc_utils/stats-collect.py: -------------------------------------------------------------------------------- 1 | from subprocess import * 2 | import json 3 | import re 4 | import os 5 | import sys 6 | from tqdm import tqdm 7 | 8 | contracts = {} 9 | opcodes = {} 10 | callstack_error_contracts = [] 11 | cterror_balances = [] 12 | 13 | cbalancefile = json.load(open('../contracts/contract_data/contract_balance.json')) 14 | 15 | write_out = False 16 | 17 | # Disassemble individual contracts - disasm is the disassembly tool from go-ethereum/build/bin 18 | def get_contract_disasm(inp): 19 | process = Popen("disasm", stdin=PIPE, stdout=PIPE, stderr=PIPE) 20 | process.stdin.write(inp+'\n') 21 | return process.communicate()[0] 22 | 23 | def check_callstack_attack(disasm): 24 | problematic_instructions = ['CALL', 'CALLCODE'] 25 | for i in xrange(0, len(disasm)): 26 | instruction = disasm[i] 27 | if instruction[1] in problematic_instructions: 28 | error = True 29 | for j in xrange(i+1, len(disasm)): 30 | if disasm[j][1] in problematic_instructions: 31 | break 32 | if disasm[j][1] == 'ISZERO': 33 | error = False 34 | break 35 | if error == True: return True 36 | return False 37 | 38 | def update_stats_from_disasm(chash, ctx, inpinit, inpmain): 39 | jump_instructions = ["JUMP","JUMPI","CALL","CALLCODE"] 40 | pattern = r"([\d]+) +([A-Z]+)([\d]?){1}(?: +(?:=> )?(\d+)?)?" 41 | imain = re.findall(pattern, inpmain) 42 | iinit = re.findall(pattern, inpinit) 43 | ifull = imain+iinit 44 | 45 | # Check for callstack attack 46 | if check_callstack_attack(imain) or check_callstack_attack(iinit): 47 | if chash in cbalancefile: 48 | cterror_balances.append(cbalancefile[chash]) 49 | else: 50 | cterror_balances.append(0) 51 | callstack_error_contracts.append(chash) 52 | 53 | # Extract opcode data 54 | used = [] 55 | for instruction in ifull: 56 | opcode = instruction[1] 57 | if opcode not in opcodes: 58 | # with open('tmp/'+opcode, 'w') as ofile: 59 | # ofile.write('Opcode: '+opcode+' args - '+instruction[0]+', '+instruction[2]+','+instruction[3]+'\n\nInit:\n\n') 60 | # ofile.write(inpinit) 61 | # ofile.write('Main: \n\n') 62 | # ofile.write(inpmain) 63 | # ofile.flush() 64 | # ofile.close() 65 | opcodes[opcode] = {} 66 | opcodes[opcode]['freq'] = 0 67 | opcodes[opcode]['contracts'] = 0 68 | opcodes[opcode]['freq'] += 1 69 | if opcode not in used: 70 | opcodes[opcode]['contracts'] += 1 71 | used.append(opcode) 72 | 73 | # Extract contract data 74 | if chash not in contracts: 75 | contracts[chash] = {} 76 | contracts[chash]['tx'] = ctx 77 | contracts[chash]['oplength'] = len(ifull) 78 | contracts[chash]['mainlength'] = len(imain) 79 | contracts[chash]['initlength'] = len(iinit) 80 | contracts[chash]['jumps'] = 0 81 | contracts[chash]['opcodes'] = {} 82 | copcodes = contracts[chash]['opcodes'] 83 | # TODO: Number of jumps, number of opcodes 84 | for instruction in ifull: 85 | opcode = instruction[1] 86 | if opcode in jump_instructions: 87 | contracts[chash]['jumps'] += 1 88 | if opcode not in copcodes: 89 | copcodes[opcode] = {} 90 | copcodes[opcode]['freq'] = 0 91 | copcodes[opcode]['freq'] += 1 92 | 93 | def load_contract_file(path): 94 | try: 95 | cfile = json.loads(open(path).read()) 96 | # Iterate through contracts 97 | for contract in tqdm(cfile): 98 | cinit = cfile[contract][0] 99 | cmain = cfile[contract][1] 100 | ctx = cfile[contract][2] 101 | cmaindisasm = get_contract_disasm(cmain[2:]) 102 | cinitdisasm = get_contract_disasm(cinit[2:]) 103 | update_stats_from_disasm(contract, ctx, cinitdisasm, cmaindisasm) 104 | except: 105 | return 106 | 107 | def load_contracts_dir(path): 108 | files = os.listdir(path) 109 | if path[-1] != '/': path += '/' 110 | print "Files loaded from path %s" % path 111 | for i in tqdm(xrange(0, len(files))): 112 | if(files[i].endswith('.json')): 113 | load_contract_file(path+files[i]) 114 | if(write_out): 115 | save_json(contracts, 'contracts.json') 116 | save_json(opcodes, 'opcodes.json') 117 | save_json(callstack_error_contracts, 'callstack_stats.json') 118 | save_json(cterror_balances, 'cterror_balances.json') 119 | 120 | def save_json(inp, filename): 121 | with open(filename, 'w') as outfile: 122 | json.dump(inp, outfile) 123 | 124 | 125 | sample_path = "../contracts/contract_data" 126 | 127 | if __name__ == '__main__': 128 | load_contracts_dir(sample_path) -------------------------------------------------------------------------------- /misc_utils/transaction_scrape 2.py: -------------------------------------------------------------------------------- 1 | import mmap 2 | import os 3 | import re 4 | from subprocess import call 5 | import sys 6 | import string 7 | import ntpath 8 | import re 9 | import lxml.html 10 | 11 | # Using GNU Parallel - O. Tange (2011): GNU Parallel - The Command-Line Power Tool, 12 | # ;login: The USENIX Magazine, February 2011:42-47. 13 | 14 | def strip_html(x): 15 | return lxml.html.fromstring(x).text_content() if x else '' 16 | 17 | def path_leaf(path): 18 | head, tail = ntpath.split(path) 19 | return tail or ntpath.basename(head) 20 | 21 | core_url = "https://etherscan.io/txsInternal?valid=false&p=%d" 22 | core_url2 = "https://etherscan.io/txsInternal?p=%s" 23 | txinfo_headers = ["transaction","from","to","gas","gas used","cumulative gas used","transaction value"] 24 | cinfo_headers = ["contract code url","contract value"] 25 | 26 | def download_txs(dirn): 27 | # Load the first page 28 | print "Downloading transactions..." 29 | os.system("wget https://etherscan.io/txsInternal?valid=false -O tmp.html") 30 | max_p = run_re_file(r"Page 1<\/b> of (\d+)<\/b><\/span>", "tmp.html")[0] 31 | print "Max pages = %d" % (int(max_p)) 32 | os.system("curl 'https://etherscan.io/txsInternal?valid=false&p=[1-%d]'' -o %stxs#1.html" % (max_p,dirn)) 33 | print "Download complete." 34 | os.system("rm tmp.html") 35 | 36 | def download_txs_all(dirn): 37 | # Load the first page 38 | print "Downloading transactions..." 39 | os.system("wget https://etherscan.io/txsInternal -O tmp.html") 40 | max_p = run_re_file(r"Page 1<\/b> of (\d+)<\/b><\/span>", "tmp.html")[0] 41 | print "Max pages = %d" % (int(max_p)) 42 | os.system("curl 'https://etherscan.io/txsInternal?p=[1-%d]' -o %stxs#1.html" % (int(max_p),dirn)) 43 | print "Download complete." 44 | os.system("rm tmp.html") 45 | 46 | 47 | def get_tx_from_dir(dirn): 48 | txs = [] 49 | print "" 50 | for filen in os.listdir(dirn): 51 | sys.stdout.write("Loaded File %s.\r" % filen) 52 | txs += get_tx_from_file(dirn+"/"+filen) 53 | print "" 54 | return txs 55 | 56 | def get_contracts_info(contracts, clean): 57 | print "Getting info for contracts..." 58 | tmp_dir = "contracts_tmp" 59 | tmp_tmp_dir = tmp_dir+"/tmp" 60 | tmp_file = tmp_dir+"/contracts.txt" 61 | template = "https://etherscan.io/address/%s" 62 | os.system("mkdir "+tmp_dir) 63 | with open(tmp_file, 'w') as tfile: 64 | for contract in contracts: 65 | if( not os.path.isfile(tmp_tmp_dir+"/"+contract)): 66 | tfile.write((template % contract)+"\n") 67 | else: 68 | sys.stdout.write("Skipped contract %s\r" % contract) 69 | print " " 70 | os.system("cat %s | parallel --will-cite --progress --bar -j 60 wget --quiet --directory-prefix=%s" % (tmp_file, tmp_tmp_dir)) 71 | print "Loading values..." 72 | values = [] 73 | for i in xrange(0, len(contracts)): 74 | sys.stdout.write("%0.2f percent done.\r" % (float(i)/len(contracts))*100) 75 | values.append(load_contractfile(tmp_tmp_dir+"/"+contracts[i])) 76 | print " " 77 | # for contract in contracts: 78 | # values.append(load_contractfile(tmp_tmp_dir+"/"+contract)) 79 | # for filen in os.listdir(tmp_tmp_dir): 80 | # if(filen.find('.') != -1): continue 81 | 82 | # values.append(load_contractfile(tmp_tmp_dir+"/"+filen)) 83 | if(clean): 84 | os.system("rm -r "+tmp_dir) 85 | return values 86 | 87 | def process_value(inp): 88 | inp = string.join(string.split(strip_html(inp),","),"") 89 | units = ["wei","Kwei","Mwei","Gwei","szabo","finney","ether","Kether","Mether","Gether","Tether"] 90 | conv_ratio = [1.0/1000000000000000000.0, 1.0/1000000000000000.0,1.0/1000000000000.0,1.0/1000000000.0,1.0/1000000000.0,1.0/1000.0,1,1000,1000000,1000000000,1000000000000] 91 | re_digit = r"([\d.]+)" 92 | unit_conv_ratio = 1 93 | for i in xrange(0, len(units)): 94 | if re.search(units[i], inp, re.IGNORECASE): 95 | unit_conv_ratio = conv_ratio[i] 96 | break 97 | number = float(re.match(re_digit, inp).group(0)) 98 | return str(number*unit_conv_ratio) 99 | 100 | 101 | def load_contractfile(fn): 102 | code_dir = "contract_code" 103 | re_code_value = r"
([\s\S]+?)<\/pre>"
104 |     re_code_url = r"Click To View<\/b><\/a>"
105 |     re_str_value = r"ETH Balance:\n<\/td>\n\n(.+?)\n<\/td>"
106 | 
107 |     if(len(run_re_file(re_str_value, fn)) < 1):
108 |         print "Balance retrieval failed for file %s" % fn
109 |         print "Downloading again..."
110 |         os.system("rm %s"+fn)
111 |         os.system("wget -O %s https://etherscan.io/address/%s" % (fn, path_leaf(fn)))
112 |         if(len(run_re_file(re_str_value, fn)) < 1):
113 |             print "Balance retrieval failed hopelessly for file %s" % fn
114 | 
115 |     if(not os.path.exists(code_dir)):   
116 |         os.system("mkdir %s" % code_dir)
117 |     code_url = run_re_file(re_code_url, fn)    
118 |     retval = []
119 |     try:
120 |         if(len(code_url) < 1):
121 |             code_url = ['']
122 |         code = run_re_file(re_code_value, fn)
123 |         if(len(code) > 0):
124 |             with open(code_dir+"/"+path_leaf(fn),'w') as cfile:
125 |                 cfile.write(code[0])
126 |         retval = [code_url[0], process_value(run_re_file(re_str_value, fn)[0])]
127 |     except IndexError:
128 |         print "IndexError in transaction %s" % (path_leaf(fn))
129 |         retval = ['', '']
130 |     return retval
131 | 
132 | 
133 | def load_txfile(fn):
134 |     re_str_from = r"From:\n<\/td>\n\n"
135 |     re_str_to = r"Contract "
136 |     re_str_gasgiven = r"\n(\d+?)\n<\/td>"
137 |     re_str_gasused = r"\n(\d+?)<\/span><\/td>"
138 |     re_str_cumgas = r"span title=\"The total amount of gas used when this transaction was executed in the block\. If being used with a smart contact, this would show the total amount of gas that has been used to interact with it\">\n(\d+?)<\/span><\/td>"
139 |     re_str_value = r"\n(.+?)<\/span>"
140 | 
141 |     return [run_re_file(re_str_from, fn)[0], run_re_file(re_str_to, fn)[0], run_re_file(re_str_gasgiven, fn)[0], run_re_file(re_str_gasused, fn)[0], run_re_file(re_str_cumgas, fn)[0], process_value(run_re_file(re_str_value, fn)[0])]
142 | 
143 | def get_txinfo(txs, fn, dirn, clean):
144 |     with open(fn, 'w') as urlfile:
145 |         for tx in txs:
146 |             if not os.path.isfile(dirn+"/"+tx):
147 |                 urlfile.write("https://etherscan.io/tx/%s\n" % tx)
148 |             else:
149 |                 sys.stdout.write("Skipping file %s\r" % tx)
150 |     os.system("mkdir %s" % dirn)
151 |     os.system("cat %s | parallel --will-cite --progress --bar -j 60 wget --quiet --directory-prefix=%s" % (fn, dirn))
152 |     print "downloaded files. reading data..."
153 |     tx_data = []
154 |     to_contracts = []
155 |     for filen in os.listdir(dirn):
156 |         if(filen.find('.') != -1): continue
157 |         sys.stdout.write("Reading file %s\r" % (filen))
158 |         try:
159 |             tx_data_file = [filen] + (load_txfile(dirn+"/"+filen))
160 |         except IndexError:
161 |             print "IndexError in file %s\t\t\t." % filen
162 |             continue
163 |         tx_data.append(tx_data_file)
164 |         to_contracts.append(tx_data_file[txinfo_headers.index("to")])
165 |     print ""
166 |     if(clean):
167 |         os.system("rm -r %s" % dirn)  
168 |     print "loading contract info..."
169 |     cinfo = get_contracts_info(to_contracts, clean)
170 |     return (tx_data, cinfo)
171 | 
172 | 
173 | def save_csv(data, headers, filen):
174 |     for i in xrange(1, len(data)):
175 |         if(len(data[i]) != len(data[i-1])):
176 |             print "Unequal lists"
177 |             return
178 |     with open(filen, 'w') as csvfile:
179 |         final_header = []
180 |         for header in headers:
181 |             final_header += header
182 |             csvfile.write(string.join(final_header, ",")+"\n")
183 |         for i in xrange(0, len(data[0])):
184 |             row = []
185 |             for j in xrange(0, len(data)):
186 |                 row += data[j][i]
187 |             csvfile.write(string.join(row, ",")+"\n")
188 | 
189 | 
190 | def run(clean=False):
191 |     txs = get_tx_from_dir("test")
192 |     txinfo, cinfo = get_txinfo(txs, "urls.txt", "tx_dir", clean)
193 |     save_csv([txinfo, cinfo], [txinfo_headers, cinfo_headers], "output3.csv")
194 |     return txinfo,cinfo
195 | 
196 | # def load_txfiles(dirn)
197 | 
198 | def get_tx_from_file(fn):
199 |     re_str = r""
200 |     return run_re_file(re_str, fn)
201 | 
202 | def run_re_file(re_str, fn):
203 |     size = os.stat(fn).st_size
204 |     with open(fn, 'r') as tf:
205 |         data = mmap.mmap(tf.fileno(), size, access=mmap.ACCESS_READ)
206 |         return re.findall(re_str, data)


--------------------------------------------------------------------------------
/misc_utils/transaction_scrape.py:
--------------------------------------------------------------------------------
  1 | import mmap
  2 | import os
  3 | import re
  4 | from subprocess import call
  5 | import sys
  6 | import string
  7 | import ntpath
  8 | import re
  9 | import lxml.html
 10 | 
 11 | # Using GNU Parallel - O. Tange (2011): GNU Parallel - The Command-Line Power Tool,
 12 | # ;login: The USENIX Magazine, February 2011:42-47.
 13 | 
 14 | def strip_html(x):
 15 |     return lxml.html.fromstring(x).text_content() if x else ''
 16 | 
 17 | def path_leaf(path):
 18 |     head, tail = ntpath.split(path)
 19 |     return tail or ntpath.basename(head)
 20 | 
 21 | core_url = "https://etherscan.io/txsInternal?valid=false&p=%d"
 22 | core_url2 = "https://etherscan.io/txsInternal?p=%s"
 23 | txinfo_headers = ["transaction","from","to","gas","gas used","cumulative gas used","transaction value"]
 24 | cinfo_headers = ["contract code url","contract value"]
 25 | 
 26 | def download_txs(dirn):
 27 |     # Load the first page
 28 |     print "Downloading transactions..."
 29 |     os.system("wget https://etherscan.io/txsInternal?valid=false -O tmp.html")
 30 |     max_p = run_re_file(r"Page 1<\/b> of (\d+)<\/b><\/span>", "tmp.html")[0]
 31 |     print "Max pages = %d" % (int(max_p)) 
 32 |     os.system("curl 'https://etherscan.io/txsInternal?valid=false&p=[1-%d]'' -o %stxs#1.html" % (max_p,dirn))
 33 |     print "Download complete."
 34 |     os.system("rm tmp.html")
 35 | 
 36 | def download_txs_all(dirn):
 37 |     # Load the first page
 38 |     print "Downloading transactions..."
 39 |     os.system("wget https://etherscan.io/txsInternal -O tmp.html")
 40 |     max_p = run_re_file(r"Page 1<\/b> of (\d+)<\/b><\/span>", "tmp.html")[0]
 41 |     print "Max pages = %d" % (int(max_p)) 
 42 |     os.system("curl 'https://etherscan.io/txsInternal?p=[1-%d]' -o %stxs#1.html" % (int(max_p),dirn))
 43 |     print "Download complete."
 44 |     os.system("rm tmp.html")
 45 | 
 46 | 
 47 | def get_tx_from_dir(dirn):
 48 |     txs = []
 49 |     print ""
 50 |     for filen in os.listdir(dirn):
 51 |         sys.stdout.write("Loaded File %s.\r" % filen)
 52 |         txs += get_tx_from_file(dirn+"/"+filen)
 53 |     print ""
 54 |     return txs
 55 | 
 56 | def get_contracts_info(contracts, clean):
 57 |     print "Getting info for contracts..."
 58 |     tmp_dir = "contracts_tmp"
 59 |     tmp_tmp_dir = tmp_dir+"/tmp"
 60 |     tmp_file = tmp_dir+"/contracts.txt"
 61 |     template = "https://etherscan.io/address/%s"
 62 |     os.system("mkdir "+tmp_dir)
 63 |     with open(tmp_file, 'w') as tfile:
 64 |         for contract in contracts:
 65 |             if( not os.path.isfile(tmp_tmp_dir+"/"+contract)):
 66 |                 tfile.write((template % contract)+"\n")
 67 |             else:
 68 |                 sys.stdout.write("Skipped contract %s\r" % contract)
 69 |     print " "
 70 |     os.system("cat %s | parallel --will-cite --progress --bar -j 60 wget --quiet --directory-prefix=%s" % (tmp_file, tmp_tmp_dir))
 71 |     print "Loading values..."
 72 |     values = []
 73 |     for i in xrange(0, len(contracts)):
 74 |         sys.stdout.write("%0.2f percent done.\r" % (float(i)/len(contracts))*100)
 75 |         values.append(load_contractfile(tmp_tmp_dir+"/"+contracts[i]))
 76 |     print " "    
 77 |     # for contract in contracts:
 78 |     #     values.append(load_contractfile(tmp_tmp_dir+"/"+contract))
 79 |     # for filen in os.listdir(tmp_tmp_dir):
 80 |     #     if(filen.find('.') != -1): continue
 81 | 
 82 |     #     values.append(load_contractfile(tmp_tmp_dir+"/"+filen))
 83 |     if(clean):
 84 |         os.system("rm -r "+tmp_dir)
 85 |     return values
 86 | 
 87 | def process_value(inp):
 88 |     inp = string.join(string.split(strip_html(inp),","),"")
 89 |     units = ["wei","Kwei","Mwei","Gwei","szabo","finney","ether","Kether","Mether","Gether","Tether"]
 90 |     conv_ratio = [1.0/1000000000000000000.0, 1.0/1000000000000000.0,1.0/1000000000000.0,1.0/1000000000.0,1.0/1000000000.0,1.0/1000.0,1,1000,1000000,1000000000,1000000000000]
 91 |     re_digit = r"([\d.]+)"
 92 |     unit_conv_ratio = 1
 93 |     for i in xrange(0, len(units)):
 94 |         if re.search(units[i], inp, re.IGNORECASE):
 95 |             unit_conv_ratio = conv_ratio[i]
 96 |             break
 97 |     number = float(re.match(re_digit, inp).group(0))
 98 |     return str(number*unit_conv_ratio)
 99 | 
100 | 
101 | def load_contractfile(fn):
102 |     code_dir = "contract_code"
103 |     re_code_value = r"
([\s\S]+?)<\/pre>"
104 |     re_code_url = r"Click To View<\/b><\/a>"
105 |     re_str_value = r"ETH Balance:\n<\/td>\n\n(.+?)\n<\/td>"
106 | 
107 |     if(len(run_re_file(re_str_value, fn)) < 1):
108 |         print "Balance retrieval failed for file %s" % fn
109 |         print "Downloading again..."
110 |         os.system("rm %s"+fn)
111 |         os.system("wget -O %s https://etherscan.io/address/%s" % (fn, path_leaf(fn)))
112 |         if(len(run_re_file(re_str_value, fn)) < 1):
113 |             print "Balance retrieval failed hopelessly for file %s" % fn
114 | 
115 |     if(not os.path.exists(code_dir)):   
116 |         os.system("mkdir %s" % code_dir)
117 |     code_url = run_re_file(re_code_url, fn)    
118 |     retval = []
119 |     try:
120 |         if(len(code_url) < 1):
121 |             code_url = ['']
122 |         code = run_re_file(re_code_value, fn)
123 |         if(len(code) > 0):
124 |             with open(code_dir+"/"+path_leaf(fn),'w') as cfile:
125 |                 cfile.write(code[0])
126 |         retval = [code_url[0], process_value(run_re_file(re_str_value, fn)[0])]
127 |     except IndexError:
128 |         print "IndexError in transaction %s" % (path_leaf(fn))
129 |         retval = ['', '']
130 |     return retval
131 | 
132 | 
133 | def load_txfile(fn):
134 |     re_str_from = r"From:\n<\/td>\n\n"
135 |     re_str_to = r"Contract "
136 |     re_str_gasgiven = r"\n(\d+?)\n<\/td>"
137 |     re_str_gasused = r"\n(\d+?)<\/span><\/td>"
138 |     re_str_cumgas = r"span title=\"The total amount of gas used when this transaction was executed in the block\. If being used with a smart contact, this would show the total amount of gas that has been used to interact with it\">\n(\d+?)<\/span><\/td>"
139 |     re_str_value = r"\n(.+?)<\/span>"
140 | 
141 |     return [run_re_file(re_str_from, fn)[0], run_re_file(re_str_to, fn)[0], run_re_file(re_str_gasgiven, fn)[0], run_re_file(re_str_gasused, fn)[0], run_re_file(re_str_cumgas, fn)[0], process_value(run_re_file(re_str_value, fn)[0])]
142 | 
143 | def get_txinfo(txs, fn, dirn, clean):
144 |     with open(fn, 'w') as urlfile:
145 |         for tx in txs:
146 |             if not os.path.isfile(dirn+"/"+tx):
147 |                 urlfile.write("https://etherscan.io/tx/%s\n" % tx)
148 |             else:
149 |                 sys.stdout.write("Skipping file %s\r" % tx)
150 |     os.system("mkdir %s" % dirn)
151 |     os.system("cat %s | parallel --will-cite --progress --bar -j 60 wget --quiet --directory-prefix=%s" % (fn, dirn))
152 |     print "downloaded files. reading data..."
153 |     tx_data = []
154 |     to_contracts = []
155 |     for filen in os.listdir(dirn):
156 |         if(filen.find('.') != -1): continue
157 |         sys.stdout.write("Reading file %s\r" % (filen))
158 |         try:
159 |             tx_data_file = [filen] + (load_txfile(dirn+"/"+filen))
160 |         except IndexError:
161 |             print "IndexError in file %s\t\t\t." % filen
162 |             continue
163 |         tx_data.append(tx_data_file)
164 |         to_contracts.append(tx_data_file[txinfo_headers.index("to")])
165 |     print ""
166 |     if(clean):
167 |         os.system("rm -r %s" % dirn)  
168 |     print "loading contract info..."
169 |     cinfo = get_contracts_info(to_contracts, clean)
170 |     return (tx_data, cinfo)
171 | 
172 | 
173 | def save_csv(data, headers, filen):
174 |     for i in xrange(1, len(data)):
175 |         if(len(data[i]) != len(data[i-1])):
176 |             print "Unequal lists"
177 |             return
178 |     with open(filen, 'w') as csvfile:
179 |         final_header = []
180 |         for header in headers:
181 |             final_header += header
182 |             csvfile.write(string.join(final_header, ",")+"\n")
183 |         for i in xrange(0, len(data[0])):
184 |             row = []
185 |             for j in xrange(0, len(data)):
186 |                 row += data[j][i]
187 |             csvfile.write(string.join(row, ",")+"\n")
188 | 
189 | 
190 | def run(clean=False):
191 |     txs = get_tx_from_dir("test")
192 |     txinfo, cinfo = get_txinfo(txs, "urls.txt", "tx_dir", clean)
193 |     save_csv([txinfo, cinfo], [txinfo_headers, cinfo_headers], "output3.csv")
194 |     return txinfo,cinfo
195 | 
196 | # def load_txfiles(dirn)
197 | 
198 | def get_tx_from_file(fn):
199 |     re_str = r""
200 |     return run_re_file(re_str, fn)
201 | 
202 | def run_re_file(re_str, fn):
203 |     size = os.stat(fn).st_size
204 |     with open(fn, 'r') as tf:
205 |         data = mmap.mmap(tf.fileno(), size, access=mmap.ACCESS_READ)
206 |         return re.findall(re_str, data)


--------------------------------------------------------------------------------
/misc_utils/tx-stats.py:
--------------------------------------------------------------------------------
 1 | import re
 2 | import os
 3 | from tqdm import tqdm
 4 | import json
 5 | 
 6 | def get_transactions(fname):
 7 |     pattern  = r"(>(0x[\da-f]+)<|block\/(\d+)|(\d.+?))"
 8 |     res = re.findall(pattern, open(fname).read(), re.IGNORECASE)
 9 |     txs = []
10 |     curtx = {}
11 | 
12 |     for match in res:
13 |         if(match[2] != ''):
14 |             if(len(txs) > 0):
15 |                 txs[-1]['transactions'] = txs[-1]['transactions'][:-1]
16 |             curtx = {}
17 |             curtx['transactions'] = []
18 |             curtx['transactions'].append({})
19 |             txs.append(curtx)    	
20 |         if(match[1] != ''):
21 |         	if 'txid' not in curtx:
22 |         		curtx['txid'] = match[1]
23 |         	else:
24 |         		if 'from' not in curtx['transactions'][-1]:
25 |         			curtx['transactions'][-1]['from'] = match[1]
26 |         		elif 'to' not in curtx['transactions'][-1]:
27 |         			curtx['transactions'][-1]['to']   = match[1]
28 |         if(match[3] != ''):
29 |         	curtx['transactions'][-1]['worth'] = match[3]
30 |         	curtx['transactions'].append({})
31 |     return txs
32 | 
33 | def load_txdir(path):
34 | 	files = os.listdir(path)
35 | 	if path[-1] != '/': path += '/'
36 | 	txs = []
37 | 	for f in tqdm(files):
38 | 		if f.endswith('.html'):
39 | 			txs += get_transactions(path+f)
40 | 	return txs
41 | 
42 | def pprint(fn):
43 | 	inj = json.loads(open(fn).read())
44 | 	outj = open(fn, 'w')
45 | 	outj.write(json.dumps(inj, indent=1))
46 | 	outj.flush()
47 | 	outj.close()
48 | 
49 | txs_dir = "../transaction-scraper/transactions"
50 | print "Loading transactions..."
51 | txs = load_txdir(txs_dir)
52 | print "Saving..."
53 | with open('transactions.json','w') as tfile:
54 | 	tfile.write(json.dumps(txs, indent=1))
55 | 	tfile.close()
56 | print "Done."
57 | 


--------------------------------------------------------------------------------
/oyente/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/oyente/__init__.py


--------------------------------------------------------------------------------
/oyente/ast_helper.py:
--------------------------------------------------------------------------------
  1 | from utils import run_command
  2 | from ast_walker import AstWalker
  3 | import json
  4 | 
  5 | class AstHelper:
  6 |     def __init__(self, filename, input_type, remap, allow_paths=""):
  7 |         self.input_type = input_type
  8 |         self.allow_paths = allow_paths
  9 |         if input_type == "solidity":
 10 |             self.remap = remap
 11 |             self.source_list = self.get_source_list(filename)
 12 |         elif input_type == "standard json":
 13 |             self.source_list = self.get_source_list_standard_json(filename)
 14 |         else:
 15 |             raise Exception("There is no such type of input")
 16 |         self.contracts = self.extract_contract_definitions(self.source_list)
 17 | 
 18 |     def get_source_list_standard_json(self, filename):
 19 |         with open('standard_json_output', 'r') as f:
 20 |             out = f.read()
 21 |         out = json.loads(out)
 22 |         return out["sources"]
 23 | 
 24 |     def get_source_list(self, filename):
 25 |         if self.allow_paths:
 26 |             cmd = "solc --combined-json ast %s %s --allow-paths %s" % (self.remap, filename, self.allow_paths)
 27 |         else:
 28 |             cmd = "solc --combined-json ast %s %s" % (self.remap, filename)
 29 |         out = run_command(cmd)
 30 |         out = json.loads(out)
 31 |         return out["sources"]
 32 | 
 33 |     def extract_contract_definitions(self, sourcesList):
 34 |         ret = {
 35 |             "contractsById": {},
 36 |             "contractsByName": {},
 37 |             "sourcesByContract": {}
 38 |         }
 39 |         walker = AstWalker()
 40 |         for k in sourcesList:
 41 |             if self.input_type == "solidity":
 42 |                 ast = sourcesList[k]["AST"]
 43 |             else:
 44 |                 ast = sourcesList[k]["legacyAST"]
 45 |             nodes = []
 46 |             walker.walk(ast, {"name": "ContractDefinition"}, nodes)
 47 |             for node in nodes:
 48 |                 ret["contractsById"][node["id"]] = node
 49 |                 ret["sourcesByContract"][node["id"]] = k
 50 |                 ret["contractsByName"][k + ':' + node["attributes"]["name"]] = node
 51 |         return ret
 52 | 
 53 |     def get_linearized_base_contracts(self, id, contractsById):
 54 |         return map(lambda id: contractsById[id], contractsById[id]["attributes"]["linearizedBaseContracts"])
 55 | 
 56 |     def extract_state_definitions(self, c_name):
 57 |         node = self.contracts["contractsByName"][c_name]
 58 |         state_vars = []
 59 |         if node:
 60 |             base_contracts = self.get_linearized_base_contracts(node["id"], self.contracts["contractsById"])
 61 |             base_contracts = list(base_contracts)
 62 |             base_contracts = list(reversed(base_contracts))
 63 |             for contract in base_contracts:
 64 |                 if "children" in contract:
 65 |                     for item in contract["children"]:
 66 |                         if item["name"] == "VariableDeclaration":
 67 |                             state_vars.append(item)
 68 |         return state_vars
 69 | 
 70 |     def extract_states_definitions(self):
 71 |         ret = {}
 72 |         for contract in self.contracts["contractsById"]:
 73 |             name = self.contracts["contractsById"][contract]["attributes"]["name"]
 74 |             source = self.contracts["sourcesByContract"][contract]
 75 |             full_name = source + ":" + name
 76 |             ret[full_name] = self.extract_state_definitions(full_name)
 77 |         return ret
 78 | 
 79 |     def extract_func_call_definitions(self, c_name):
 80 |         node = self.contracts["contractsByName"][c_name]
 81 |         walker = AstWalker()
 82 |         nodes = []
 83 |         if node:
 84 |             walker.walk(node, {"name":  "FunctionCall"}, nodes)
 85 |         return nodes
 86 | 
 87 |     def extract_func_calls_definitions(self):
 88 |         ret = {}
 89 |         for contract in self.contracts["contractsById"]:
 90 |             name = self.contracts["contractsById"][contract]["attributes"]["name"]
 91 |             source = self.contracts["sourcesByContract"][contract]
 92 |             full_name = source + ":" + name
 93 |             ret[full_name] = self.extract_func_call_definitions(full_name)
 94 |         return ret
 95 | 
 96 |     def extract_state_variable_names(self, c_name):
 97 |         state_variables = self.extract_states_definitions()[c_name]
 98 |         var_names = []
 99 |         for var_name in state_variables:
100 |             var_names.append(var_name["attributes"]["name"])
101 |         return var_names
102 | 
103 |     def extract_func_call_srcs(self, c_name):
104 |         func_calls = self.extract_func_calls_definitions()[c_name]
105 |         func_call_srcs = []
106 |         for func_call in func_calls:
107 |             func_call_srcs.append(func_call["src"])
108 |         return func_call_srcs
109 | 
110 |     def get_callee_src_pairs(self, c_name):
111 |         node = self.contracts["contractsByName"][c_name]
112 |         walker = AstWalker()
113 |         nodes = []
114 |         if node:
115 |             list_of_attributes = [
116 |                 {"attributes": {"member_name": "delegatecall"}},
117 |                 {"attributes": {"member_name": "call"}},
118 |                 {"attributes": {"member_name": "callcode"}}
119 |             ]
120 |             walker.walk(node, list_of_attributes, nodes)
121 | 
122 |         callee_src_pairs = []
123 |         for node in nodes:
124 |             if "children" in node and node["children"]:
125 |                 type_of_first_child = node["children"][0]["attributes"]["type"]
126 |                 if type_of_first_child.split(" ")[0] == "contract":
127 |                     contract = type_of_first_child.split(" ")[1]
128 |                     contract_path = self._find_contract_path(self.contracts["contractsByName"].keys(), contract)
129 |                     callee_src_pairs.append((contract_path, node["src"]))
130 |         return callee_src_pairs
131 | 
132 |     def get_func_name_to_params(self, c_name):
133 |         node = self.contracts['contractsByName'][c_name]
134 |         walker = AstWalker()
135 |         func_def_nodes = []
136 |         if node:
137 |             walker.walk(node, {'name': 'FunctionDefinition'}, func_def_nodes)
138 | 
139 |         func_name_to_params = {}
140 |         for func_def_node in func_def_nodes:
141 |             func_name = func_def_node['attributes']['name']
142 |             params_nodes = []
143 |             walker.walk(func_def_node, {'name': 'ParameterList'}, params_nodes)
144 | 
145 |             params_node = params_nodes[0]
146 |             param_nodes = []
147 |             walker.walk(params_node, {'name': 'VariableDeclaration'}, param_nodes)
148 | 
149 |             for param_node in param_nodes:
150 |                 var_name = param_node['attributes']['name']
151 |                 type_name = param_node['children'][0]['name']
152 |                 if type_name == 'ArrayTypeName':
153 |                     literal_nodes = []
154 |                     walker.walk(param_node, {'name': 'Literal'}, literal_nodes)
155 |                     if literal_nodes:
156 |                         array_size = int(literal_nodes[0]['attributes']['value'])
157 |                     else:
158 |                         array_size = 1
159 |                     param = {'name': var_name, 'type': type_name, 'value': array_size}
160 |                 elif type_name == 'ElementaryTypeName':
161 |                     param = {'name': var_name, 'type': type_name}
162 |                 else:
163 |                     param = {'name': var_name, 'type': type_name}
164 | 
165 |                 if func_name not in func_name_to_params:
166 |                     func_name_to_params[func_name] = [param]
167 |                 else:
168 |                     func_name_to_params[func_name].append(param)
169 |         return func_name_to_params
170 | 
171 |     def _find_contract_path(self, contract_paths, contract):
172 |         for path in contract_paths:
173 |             cname = path.split(":")[-1]
174 |             if contract == cname:
175 |                 return path
176 |         return ""
177 | 


--------------------------------------------------------------------------------
/oyente/ast_walker.py:
--------------------------------------------------------------------------------
 1 | class AstWalker:
 2 |     def walk(self, node, attributes, nodes):
 3 |         if isinstance(attributes, dict):
 4 |             self._walk_with_attrs(node, attributes, nodes)
 5 |         else:
 6 |             self._walk_with_list_of_attrs(node, attributes, nodes)
 7 | 
 8 |     def _walk_with_attrs(self, node, attributes, nodes):
 9 |         if self._check_attributes(node, attributes):
10 |             nodes.append(node)
11 |         else:
12 |             if "children" in node and node["children"]:
13 |                 for child in node["children"]:
14 |                     self._walk_with_attrs(child, attributes, nodes)
15 | 
16 |     def _walk_with_list_of_attrs(self, node, list_of_attributes, nodes):
17 |         if self._check_list_of_attributes(node, list_of_attributes):
18 |             nodes.append(node)
19 |         else:
20 |             if "children" in node and node["children"]:
21 |                 for child in node["children"]:
22 |                     self._walk_with_list_of_attrs(child, list_of_attributes, nodes)
23 | 
24 |     def _check_attributes(self, node, attributes):
25 |         for name in attributes:
26 |             if name == "attributes":
27 |                 if "attributes" not in node or not self._check_attributes(node["attributes"], attributes["attributes"]):
28 |                     return False
29 |             else:
30 |                 if name not in node or node[name] != attributes[name]:
31 |                     return False
32 |         return True
33 | 
34 |     def _check_list_of_attributes(self, node, list_of_attributes):
35 |         for attrs in list_of_attributes:
36 |             if self._check_attributes(node, attrs):
37 |                 return True
38 |         return False
39 | 


--------------------------------------------------------------------------------
/oyente/basicblock.py:
--------------------------------------------------------------------------------
 1 | import six
 2 | 
 3 | class BasicBlock:
 4 |     def __init__(self, start_address, end_address):
 5 |         self.start = start_address
 6 |         self.end = end_address
 7 |         self.instructions = []  # each instruction is a string
 8 |         self.jump_target = 0
 9 | 
10 |     def get_start_address(self):
11 |         return self.start
12 | 
13 |     def get_end_address(self):
14 |         return self.end
15 | 
16 |     def add_instruction(self, instruction):
17 |         self.instructions.append(instruction)
18 | 
19 |     def get_instructions(self):
20 |         return self.instructions
21 | 
22 |     def set_block_type(self, type):
23 |         self.type = type
24 | 
25 |     def get_block_type(self):
26 |         return self.type
27 | 
28 |     def set_falls_to(self, address):
29 |         self.falls_to = address
30 | 
31 |     def get_falls_to(self):
32 |         return self.falls_to
33 | 
34 |     def set_jump_target(self, address):
35 |         if isinstance(address, six.integer_types):
36 |             self.jump_target = address
37 |         else:
38 |             self.jump_target = -1
39 | 
40 |     def get_jump_target(self):
41 |         return self.jump_target
42 | 
43 |     def set_branch_expression(self, branch):
44 |         self.branch_expression = branch
45 | 
46 |     def get_branch_expression(self):
47 |         return self.branch_expression
48 | 
49 |     def display(self):
50 |         six.print_("================")
51 |         six.print_("start address: %d" % self.start)
52 |         six.print_("end address: %d" % self.end)
53 |         six.print_("end statement type: " + self.type)
54 |         for instr in self.instructions:
55 |             six.print_(instr)
56 | 


--------------------------------------------------------------------------------
/oyente/batch_run.py:
--------------------------------------------------------------------------------
 1 | import json
 2 | import glob
 3 | from tqdm import tqdm
 4 | import os
 5 | import sys
 6 | import urllib2
 7 | 
 8 | contract_dir = 'contract_data'
 9 | 
10 | cfiles = glob.glob(contract_dir+'/contract1.json')
11 | 
12 | cjson = {}
13 | 
14 | print "Loading contracts..."
15 | 
16 | for cfile in tqdm(cfiles):
17 | 	cjson.update(json.loads(open(cfile).read()))
18 | 
19 | results = {}
20 | missed = []
21 | 
22 | print "Running analysis..."
23 | 
24 | contracts = cjson.keys()
25 | 
26 | if os.path.isfile('results.json'):
27 | 	old_res = json.loads(open('results.json').read())
28 | 	old_res = old_res.keys()
29 | 	contracts = [c for c in contracts if c not in old_res]
30 | 
31 | cores=0
32 | job=0
33 | 
34 | if len(sys.argv)>=3:
35 | 	cores = int(sys.argv[1])
36 | 	job = int(sys.argv[2])
37 | 	contracts = contracts[(len(contracts)/cores)*job:(len(contracts)/cores)*(job+1)]
38 | 	print "Job %d: Running on %d contracts..." % (job, len(contracts))
39 | 
40 | for c in tqdm(contracts):
41 | 	with open('tmp.evm','w') as of:
42 | 		of.write(cjson[c][1][2:])
43 | 	os.system('python oyente.py -ll 30 -s tmp.evm -j -b')
44 | 	try:
45 | 		results[c] = json.loads(open('tmp.evm.json').read())
46 | 	except:
47 | 		missed.append(c)
48 | 	with open('results.json', 'w') as of:
49 | 		of.write(json.dumps(results,indent=1))
50 | 	with open('missed.json', 'w') as of:
51 | 		of.write(json.dumps(missed,indent=1))
52 | 	# urllib2.urlopen('https://dweet.io/dweet/for/oyente-%d-%d?completed=%d&missed=%d&remaining=%d' % (job,cores,len(results),len(missed),len(contracts)-len(results)-len(missed)))
53 | 
54 | print "Completed."
55 | 


--------------------------------------------------------------------------------
/oyente/ethereum_data.py:
--------------------------------------------------------------------------------
 1 | # this the interface to create your own data source
 2 | # this class pings etherscan to get the latest code and balance information
 3 | 
 4 | import requests
 5 | import logging
 6 | 
 7 | log = logging.getLogger(__name__)
 8 | 
 9 | class EthereumData:
10 |         def __init__(self, contract_address):
11 |             self.apiDomain = "https://api.etherscan.io/api"
12 |             self.apikey = "VT4IW6VK7VES1Q9NYFI74YKH8U7QW9XRHN"
13 |             self.contract_addr = contract_address
14 | 
15 |         def getBalance(self, address):
16 |             try:
17 |                 apiEndPoint = "%s?module=account&action=balance&address=%s&tag=latest&apikey=%s" % (self.apiDomain, address, self.apikey)
18 |                 r = requests.get(apiEndPoint)
19 |                 result = r.json()
20 |                 status = result['message']
21 |                 if status == "OK":
22 |                     result = result['result']
23 |             except Exception as e:
24 |                 log.exception("Error at: contract address: %s" % address)
25 |                 raise e
26 |             return result
27 | 
28 |         def getCode(self, address):
29 |             try:
30 |                 apiEndPoint = "%s?module=proxy&action=eth_getCode&address=%s&tag=latest&apikey=%s" % (self.apiDomain, address, self.apikey)
31 |                 r = requests.get(apiEndPoint)
32 |                 result = r.json()["result"]
33 |             except Exception as e:
34 |                 log.exception("Error at: contract address: %s" % address)
35 |                 raise e
36 |             return result
37 | 
38 |         def getStorageAt(self, position):
39 |             try:
40 |                 position = hex(position)
41 |                 if position[-1] == "L":
42 |                     position = position[:-1]
43 |                 apiEndPoint = "%s?module=proxy&action=eth_getStorageAt&address=%s&position=%s&tag=latest&apikey=%s" % (self.apiDomain, self.contract_addr, position, self.apikey)
44 |                 r = requests.get(apiEndPoint)
45 |                 result = r.json()["result"]
46 |             except Exception as e:
47 |                 if str(e) != 'timeout':
48 |                     log.exception("Error at: contract address: %s, position: %s" % (self.contract_addr, position))
49 |                 raise
50 |             return int(result, 16)
51 | 


--------------------------------------------------------------------------------
/oyente/ethereum_data1.py:
--------------------------------------------------------------------------------
 1 | # this the interface to create your own data source 
 2 | # this class pings a private / public blockchain to get the balance and code information 
 3 | 
 4 | from web3 import Web3, KeepAliveRPCProvider
 5 | 
 6 | class EthereumData:
 7 | 	def __init__(self):
 8 | 		self.host = 'x.x.x.x'
 9 | 		self.port = '8545'
10 | 		self.web3 = Web3(KeepAliveRPCProvider(host=self.host, port=self.port))		
11 | 
12 | 	def getBalance(self, address): 
13 | 		return self.web3.eth.getBalance(address)
14 | 
15 | 	def getCode(self, address):		
16 | 		return self.web3.eth.getCode(address)


--------------------------------------------------------------------------------
/oyente/global_params.py:
--------------------------------------------------------------------------------
 1 | # enable reporting of the result
 2 | REPORT_MODE = 0
 3 | 
 4 | #print everything in the console
 5 | PRINT_MODE = 0
 6 | 
 7 | # enable log file to print all exception
 8 | DEBUG_MODE = 0
 9 | 
10 | # check false positive in concurrency
11 | CHECK_CONCURRENCY_FP = 0
12 | 
13 | # Timeout for z3 in ms
14 | TIMEOUT = 100
15 | 
16 | # Set this flag to 2 if we want to do evm real value unit test
17 | # Set this flag to 3 if we want to do evm symbolic unit test
18 | UNIT_TEST = 0
19 | 
20 | # timeout to run symbolic execution (in secs)
21 | GLOBAL_TIMEOUT = 50
22 | 
23 | # timeout to run symbolic execution (in secs) for testing
24 | GLOBAL_TIMEOUT_TEST = 2
25 | 
26 | # print path conditions
27 | PRINT_PATHS = 0
28 | 
29 | # WEB = 1 means that we are using Oyente for web service
30 | WEB = 0
31 | 
32 | # Redirect results to a json file.
33 | STORE_RESULT = 0
34 | 
35 | # depth limit for DFS
36 | DEPTH_LIMIT = 50
37 | 
38 | GAS_LIMIT = 4000000
39 | 
40 | LOOP_LIMIT = 10
41 | 
42 | # Use a public blockchain to speed up the symbolic execution
43 | USE_GLOBAL_BLOCKCHAIN = 0
44 | 
45 | USE_GLOBAL_STORAGE = 0
46 | 
47 | # Take state data from state.json to speed up the symbolic execution
48 | INPUT_STATE = 0
49 | 
50 | # Check assertions
51 | CHECK_ASSERTIONS = 0
52 | 
53 | GENERATE_TEST_CASES = 0
54 | 
55 | # Run Oyente in parallel
56 | PARALLEL = 0
57 | 
58 | # Iterable of targeted smart contract names
59 | TARGET_CONTRACTS = None
60 | 


--------------------------------------------------------------------------------
/oyente/input_helper.py:
--------------------------------------------------------------------------------
  1 | import shlex
  2 | import subprocess
  3 | import os
  4 | import re
  5 | import logging
  6 | import json
  7 | import global_params
  8 | import six
  9 | from source_map import SourceMap
 10 | from utils import run_command, run_command_with_err
 11 | from crytic_compile import CryticCompile, InvalidCompilation
 12 | 
 13 | class InputHelper:
 14 |     BYTECODE = 0
 15 |     SOLIDITY = 1
 16 |     STANDARD_JSON = 2
 17 |     STANDARD_JSON_OUTPUT = 3
 18 | 
 19 |     def __init__(self, input_type, **kwargs):
 20 |         self.input_type = input_type
 21 | 
 22 |         if input_type == InputHelper.BYTECODE:
 23 |             attr_defaults = {
 24 |                 'source': None,
 25 |                 'evm': False,
 26 |             }
 27 |         elif input_type == InputHelper.SOLIDITY:
 28 |             attr_defaults = {
 29 |                 'source': None,
 30 |                 'evm': False,
 31 |                 'root_path': "",
 32 |                 'compiled_contracts': [],
 33 |                 'compilation_err': False,
 34 |                 'remap': "",
 35 |                 'allow_paths': ""
 36 |             }
 37 |         elif input_type == InputHelper.STANDARD_JSON:
 38 |             attr_defaults = {
 39 |                 'source': None,
 40 |                 'evm': False,
 41 |                 'root_path': "",
 42 |                 'allow_paths': None,
 43 |                 'compiled_contracts': []
 44 |             }
 45 |         elif input_type == InputHelper.STANDARD_JSON_OUTPUT:
 46 |             attr_defaults = {
 47 |                 'source': None,
 48 |                 'evm': False,
 49 |                 'root_path': "",
 50 |                 'compiled_contracts': [],
 51 |             }
 52 | 
 53 |         for (attr, default) in six.iteritems(attr_defaults):
 54 |             val = kwargs.get(attr, default)
 55 |             if val == None:
 56 |                 raise Exception("'%s' attribute can't be None" % attr)
 57 |             else:
 58 |                 setattr(self, attr, val)
 59 | 
 60 |     def get_inputs(self, targetContracts=None):
 61 |         inputs = []
 62 |         if self.input_type == InputHelper.BYTECODE:
 63 |             with open(self.source, 'r') as f:
 64 |                 bytecode = f.read()
 65 |             self._prepare_disasm_file(self.source, bytecode)
 66 | 
 67 |             disasm_file = self._get_temporary_files(self.source)['disasm']
 68 |             inputs.append({'disasm_file': disasm_file})
 69 |         else:
 70 |             contracts = self._get_compiled_contracts()
 71 |             self._prepare_disasm_files_for_analysis(contracts)
 72 |             for contract, _ in contracts:
 73 |                 c_source, cname = contract.split(':')
 74 |                 if targetContracts is not None and cname not in targetContracts:
 75 |                     continue
 76 |                 c_source = re.sub(self.root_path, "", c_source)
 77 |                 if self.input_type == InputHelper.SOLIDITY:
 78 |                     source_map = SourceMap(contract, self.source, 'solidity', self.root_path, self.remap, self.allow_paths)
 79 |                 else:
 80 |                     source_map = SourceMap(contract, self.source, 'standard json', self.root_path)
 81 |                 disasm_file = self._get_temporary_files(contract)['disasm']
 82 |                 inputs.append({
 83 |                     'contract': contract,
 84 |                     'source_map': source_map,
 85 |                     'source': self.source,
 86 |                     'c_source': c_source,
 87 |                     'c_name': cname,
 88 |                     'disasm_file': disasm_file
 89 |                 })
 90 |         if targetContracts is not None and not inputs:
 91 |             raise ValueError("Targeted contracts weren't found in the source code!")
 92 |         return inputs
 93 | 
 94 |     def rm_tmp_files(self):
 95 |         if self.input_type == InputHelper.BYTECODE:
 96 |             self._rm_tmp_files(self.source)
 97 |         else:
 98 |             self._rm_tmp_files_of_multiple_contracts(self.compiled_contracts)
 99 | 
100 |     def _get_compiled_contracts(self):
101 |         if not self.compiled_contracts:
102 |             if self.input_type == InputHelper.SOLIDITY:
103 |                 self.compiled_contracts = self._compile_solidity()
104 |             elif self.input_type == InputHelper.STANDARD_JSON:
105 |                 self.compiled_contracts = self._compile_standard_json()
106 |             elif self.input_type == InputHelper.STANDARD_JSON_OUTPUT:
107 |                 self.compiled_contracts = self._compile_standard_json_output(self.source)
108 | 
109 |         return self.compiled_contracts
110 | 
111 |     def _extract_bin_obj(self, com: CryticCompile):
112 |         return [(com.contracts_filenames[name].absolute + ':' + name, com.bytecode_runtime(name)) for name in com.contracts_names if com.bytecode_runtime(name)]
113 | 
114 |     def _compile_solidity(self):
115 |         try:
116 |             options = []
117 |             if self.allow_paths:
118 |                 options.append(F"--allow-paths {self.allow_paths}")
119 |                 
120 |             com = CryticCompile(self.source, solc_remaps=self.remap, solc_args=' '.join(options))
121 |             contracts = self._extract_bin_obj(com)
122 | 
123 |             libs = com.contracts_names.difference(com.contracts_names_without_libraries)
124 |             if libs:
125 |                 return self._link_libraries(self.source, libs)
126 |             
127 |             return contracts
128 |         except InvalidCompilation as err:
129 |             if not self.compilation_err:
130 |                 logging.critical("Solidity compilation failed. Please use -ce flag to see the detail.")
131 |                 if global_params.WEB:
132 |                     six.print_({"error": "Solidity compilation failed."})
133 |             else:
134 |                 logging.critical("solc output:\n" + self.source)
135 |                 logging.critical(err)
136 |                 logging.critical("Solidity compilation failed.")
137 |                 if global_params.WEB:
138 |                     six.print_({"error": err})
139 |             exit(1)
140 | 
141 | 
142 |     def _compile_standard_json(self):
143 |         FNULL = open(os.devnull, 'w')
144 |         cmd = "cat %s" % self.source
145 |         p1 = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=FNULL)
146 |         cmd = "solc --allow-paths %s --standard-json" % self.allow_paths
147 |         p2 = subprocess.Popen(shlex.split(cmd), stdin=p1.stdout, stdout=subprocess.PIPE, stderr=FNULL)
148 |         p1.stdout.close()
149 |         out = p2.communicate()[0]
150 |         with open('standard_json_output', 'w') as of:
151 |             of.write(out)
152 | 
153 |         return self._compile_standard_json_output('standard_json_output')
154 | 
155 |     def _compile_standard_json_output(self, json_output_file):
156 |         with open(json_output_file, 'r') as f:
157 |             out = f.read()
158 |         j = json.loads(out)
159 |         contracts = []
160 |         for source in j['sources']:
161 |             for contract in j['contracts'][source]:
162 |                 cname = source + ":" + contract
163 |                 evm = j['contracts'][source][contract]['evm']['deployedBytecode']['object']
164 |                 contracts.append((cname, evm))
165 |         return contracts
166 | 
167 |     def _removeSwarmHash(self, evm):
168 |         evm_without_hash = re.sub(r"a165627a7a72305820\S{64}0029$", "", evm)
169 |         return evm_without_hash
170 | 
171 |     def _link_libraries(self, filename, libs):
172 |         options = []
173 |         for idx, lib in enumerate(libs):
174 |             lib_address = "0x" + hex(idx+1)[2:].zfill(40)
175 |             options.append("--libraries %s:%s" % (lib, lib_address))
176 |         if self.allow_paths:
177 |             options.append(F"--allow-paths {self.allow_paths}")
178 |         com = CryticCompile(target=self.source, solc_args=' '.join(options), solc_remaps=self.remap)
179 | 
180 |         return self._extract_bin_obj(com)
181 | 
182 |     def _prepare_disasm_files_for_analysis(self, contracts):
183 |         for contract, bytecode in contracts:
184 |             self._prepare_disasm_file(contract, bytecode)
185 | 
186 |     def _prepare_disasm_file(self, target, bytecode):
187 |         self._write_evm_file(target, bytecode)
188 |         self._write_disasm_file(target)
189 | 
190 |     def _get_temporary_files(self, target):
191 |         return {
192 |             "evm": target + ".evm",
193 |             "disasm": target + ".evm.disasm",
194 |             "log": target + ".evm.disasm.log"
195 |         }
196 | 
197 |     def _write_evm_file(self, target, bytecode):
198 |         evm_file = self._get_temporary_files(target)["evm"]
199 |         with open(evm_file, 'w') as of:
200 |             of.write(self._removeSwarmHash(bytecode))
201 | 
202 |     def _write_disasm_file(self, target):
203 |         tmp_files = self._get_temporary_files(target)
204 |         evm_file = tmp_files["evm"]
205 |         disasm_file = tmp_files["disasm"]
206 |         disasm_out = ""
207 |         try:
208 |             disasm_p = subprocess.Popen(
209 |                 ["evm", "disasm", evm_file], stdout=subprocess.PIPE)
210 |             disasm_out = disasm_p.communicate()[0].decode('utf-8', 'strict')
211 |         except:
212 |             logging.critical("Disassembly failed.")
213 |             exit()
214 | 
215 |         with open(disasm_file, 'w') as of:
216 |             of.write(disasm_out)
217 | 
218 |     def _rm_tmp_files_of_multiple_contracts(self, contracts):
219 |         if self.input_type in ['standard_json', 'standard_json_output']:
220 |             self._rm_file('standard_json_output')
221 |         for contract, _ in contracts:
222 |             self._rm_tmp_files(contract)
223 | 
224 |     def _rm_tmp_files(self, target):
225 |         tmp_files = self._get_temporary_files(target)
226 |         if not self.evm:
227 |             self._rm_file(tmp_files["evm"])
228 |             self._rm_file(tmp_files["disasm"])
229 |         self._rm_file(tmp_files["log"])
230 | 
231 |     def _rm_file(self, path):
232 |         if os.path.isfile(path):
233 |             os.unlink(path)
234 | 


--------------------------------------------------------------------------------
/oyente/opcodes.py:
--------------------------------------------------------------------------------
  1 | # list of all opcodes except the PUSHi and DUPi
  2 | # opcodes[name] has a list of [value (index), no. of items removed from stack, no. of items added to stack]
  3 | opcodes = {
  4 |     "STOP": [0x00, 0, 0],
  5 |     "ADD": [0x01, 2, 1],
  6 |     "MUL": [0x02, 2, 1],
  7 |     "SUB": [0x03, 2, 1],
  8 |     "DIV": [0x04, 2, 1],
  9 |     "SDIV": [0x05, 2, 1],
 10 |     "MOD": [0x06, 2, 1],
 11 |     "SMOD": [0x07, 2, 1],
 12 |     "ADDMOD": [0x08, 3, 1],
 13 |     "MULMOD": [0x09, 3, 1],
 14 |     "EXP": [0x0a, 2, 1],
 15 |     "SIGNEXTEND": [0x0b, 2, 1],
 16 |     "LT": [0x10, 2, 1],
 17 |     "GT": [0x11, 2, 1],
 18 |     "SLT": [0x12, 2, 1],
 19 |     "SGT": [0x13, 2, 1],
 20 |     "EQ": [0x14, 2, 1],
 21 |     "ISZERO": [0x15, 1, 1],
 22 |     "AND": [0x16, 2, 1],
 23 |     "OR": [0x17, 2, 1],
 24 |     "XOR": [0x18, 2, 1],
 25 |     "NOT": [0x19, 1, 1],
 26 |     "BYTE": [0x1a, 2, 1],
 27 |     "SHA3": [0x20, 2, 1],
 28 |     "ADDRESS": [0x30, 0, 1],
 29 |     "BALANCE": [0x31, 1, 1],
 30 |     "ORIGIN": [0x32, 0, 1],
 31 |     "CALLER": [0x33, 0, 1],
 32 |     "CALLVALUE": [0x34, 0, 1],
 33 |     "CALLDATALOAD": [0x35, 1, 1],
 34 |     "CALLDATASIZE": [0x36, 0, 1],
 35 |     "CALLDATACOPY": [0x37, 3, 0],
 36 |     "CODESIZE": [0x38, 0, 1],
 37 |     "CODECOPY": [0x39, 3, 0],
 38 |     "GASPRICE": [0x3a, 0, 1],
 39 |     "EXTCODESIZE": [0x3b, 1, 1],
 40 |     "EXTCODECOPY": [0x3c, 4, 0],
 41 |     "MCOPY": [0x3d, 3, 0],
 42 |     "BLOCKHASH": [0x40, 1, 1],
 43 |     "COINBASE": [0x41, 0, 1],
 44 |     "TIMESTAMP": [0x42, 0, 1],
 45 |     "NUMBER": [0x43, 0, 1],
 46 |     "DIFFICULTY": [0x44, 0, 1],
 47 |     "GASLIMIT": [0x45, 0, 1],
 48 |     "POP": [0x50, 1, 0],
 49 |     "MLOAD": [0x51, 1, 1],
 50 |     "MSTORE": [0x52, 2, 0],
 51 |     "MSTORE8": [0x53, 2, 0],
 52 |     "SLOAD": [0x54, 1, 1],
 53 |     "SSTORE": [0x55, 2, 0],
 54 |     "JUMP": [0x56, 1, 0],
 55 |     "JUMPI": [0x57, 2, 0],
 56 |     "PC": [0x58, 0, 1],
 57 |     "MSIZE": [0x59, 0, 1],
 58 |     "GAS": [0x5a, 0, 1],
 59 |     "JUMPDEST": [0x5b, 0, 0],
 60 |     "SLOADEXT": [0x5c, 2, 1],
 61 |     "SSTOREEXT": [0x5d, 3, 0],
 62 |     "SLOADBYTESEXT": [0x5c, 4, 0],
 63 |     "SSTOREBYTESEXT": [0x5d, 4, 0],
 64 |     "LOG0": [0xa0, 2, 0],
 65 |     "LOG1": [0xa1, 3, 0],
 66 |     "LOG2": [0xa2, 4, 0],
 67 |     "LOG3": [0xa3, 5, 0],
 68 |     "LOG4": [0xa4, 6, 0],
 69 |     "CREATE": [0xf0, 3, 1],
 70 |     "CALL": [0xf1, 7, 1],
 71 |     "CALLCODE": [0xf2, 7, 1],
 72 |     "RETURN": [0xf3, 2, 0],
 73 |     "REVERT": [0xfd, 2, 0],
 74 |     "ASSERTFAIL": [0xfe, 0, 0],
 75 |     "DELEGATECALL": [0xf4, 6, 1],
 76 |     "BREAKPOINT": [0xf5, 0, 0],
 77 |     "RNGSEED": [0xf6, 1, 1],
 78 |     "SSIZEEXT": [0xf7, 2, 1],
 79 |     "SLOADBYTES": [0xf8, 3, 0],
 80 |     "SSTOREBYTES": [0xf9, 3, 0],
 81 |     "SSIZE": [0xfa, 1, 1],
 82 |     "STATEROOT": [0xfb, 1, 1],
 83 |     "TXEXECGAS": [0xfc, 0, 1],
 84 |     "CALLSTATIC": [0xfd, 7, 1],
 85 |     "INVALID": [0xfe, 0, 0],  # Not an opcode use to cause an exception
 86 |     "SUICIDE": [0xff, 1, 0],
 87 |     "---END---": [0x00, 0, 0]
 88 | }
 89 | 
 90 | # TO BE UPDATED IF ETHEREUM VM CHANGES their fee structure
 91 | 
 92 | GCOST = {
 93 |     "Gzero": 0,
 94 |     "Gbase": 2,
 95 |     "Gverylow": 3,
 96 |     "Glow": 5,
 97 |     "Gmid": 8,
 98 |     "Ghigh": 10,
 99 |     "Gextcode": 20,
100 |     "Gbalance": 400,
101 |     "Gsload": 50,
102 |     "Gjumpdest": 1,
103 |     "Gsset": 20000,
104 |     "Gsreset": 5000,
105 |     "Rsclear": 15000,
106 |     "Rsuicide": 24000,
107 |     "Gsuicide": 5000,
108 |     "Gcreate": 32000,
109 |     "Gcodedeposit": 200,
110 |     "Gcall": 40,
111 |     "Gcallvalue": 9000,
112 |     "Gcallstipend": 2300,
113 |     "Gnewaccount": 25000,
114 |     "Gexp": 10,
115 |     "Gexpbyte": 10,
116 |     "Gmemory": 3,
117 |     "Gtxcreate": 32000,
118 |     "Gtxdatazero": 4,
119 |     "Gtxdatanonzero": 68,
120 |     "Gtransaction": 21000,
121 |     "Glog": 375,
122 |     "Glogdata": 8,
123 |     "Glogtopic": 375,
124 |     "Gsha3": 30,
125 |     "Gsha3word": 6,
126 |     "Gcopy": 3,
127 |     "Gblockhash": 20
128 | }
129 | 
130 | Wzero = ("STOP", "RETURN", "REVERT", "ASSERTFAIL")
131 | 
132 | Wbase = ("ADDRESS", "ORIGIN", "CALLER", "CALLVALUE", "CALLDATASIZE",
133 |          "CODESIZE", "GASPRICE", "COINBASE", "TIMESTAMP", "NUMBER",
134 |          "DIFFICULTY", "GASLIMIT", "POP", "PC", "MSIZE", "GAS")
135 | 
136 | Wverylow = ("ADD", "SUB", "NOT", "LT", "GT", "SLT", "SGT", "EQ",
137 |             "ISZERO", "AND", "OR", "XOR", "BYTE", "CALLDATALOAD",
138 |             "MLOAD", "MSTORE", "MSTORE8", "PUSH", "DUP", "SWAP")
139 | 
140 | Wlow = ("MUL", "DIV", "SDIV", "MOD", "SMOD", "SIGNEXTEND")
141 | 
142 | Wmid = ("ADDMOD", "MULMOD", "JUMP")
143 | 
144 | Whigh = ("JUMPI")
145 | 
146 | Wext = ("EXTCODESIZE")
147 | 
148 | def get_opcode(opcode):
149 |     if opcode in opcodes:
150 |         return opcodes[opcode]
151 |     # check PUSHi
152 |     for i in range(32):
153 |         if opcode == 'PUSH' + str(i + 1):
154 |             return [hex(0x60 + i), 0, 1]
155 | 
156 |     # check DUPi
157 |     for i in range(16):
158 |         if opcode == 'DUP' + str(i + 1):
159 |             return [hex(0x80 + i), i + 1, i + 2]
160 | 
161 |     # check SWAPi
162 |     for i in range(16):
163 |         if opcode == 'SWAP' + str(i + 1):
164 |             return [hex(0x90 + i), i + 2, i + 2]
165 |     raise ValueError('Bad Opcode' + opcode)
166 | 
167 | 
168 | def get_ins_cost(opcode):
169 |     if opcode in Wzero:
170 |         return GCOST["Gzero"]
171 |     elif opcode in Wbase:
172 |         return GCOST["Gbase"]
173 |     elif opcode in Wverylow or opcode.startswith("PUSH") or opcode.startswith("DUP") or opcode.startswith("SWAP"):
174 |         return GCOST["Gverylow"]
175 |     elif opcode in Wlow:
176 |         return GCOST["Glow"]
177 |     elif opcode in Wmid:
178 |         return GCOST["Gmid"]
179 |     elif opcode in Whigh:
180 |         return GCOST["Ghigh"]
181 |     elif opcode in Wext:
182 |         return GCOST["Gextcode"]
183 |     elif opcode == "EXP":
184 |         return GCOST["Gexp"]
185 |     elif opcode == "SLOAD":
186 |         return GCOST["Gsload"]
187 |     elif opcode == "JUMPDEST":
188 |         return GCOST["Gjumpdest"]
189 |     elif opcode == "SHA3":
190 |         return GCOST["Gsha3"]
191 |     elif opcode == "CREATE":
192 |         return GCOST["Gcreate"]
193 |     elif opcode in ("CALL", "CALLCODE"):
194 |         return GCOST["Gcall"]
195 |     elif opcode in ("LOG0", "LOG1", "LOG2", "LOG3", "LOG4"):
196 |         num_topics = int(opcode[3:])
197 |         return GCOST["Glog"] + num_topics * GCOST["Glogtopic"]
198 |     elif opcode == "EXTCODECOPY":
199 |         return GCOST["Gextcode"]
200 |     elif opcode in ("CALLDATACOPY", "CODECOPY"):
201 |         return GCOST["Gverylow"]
202 |     elif opcode == "BALANCE":
203 |         return GCOST["Gbalance"]
204 |     elif opcode == "BLOCKHASH":
205 |         return GCOST["Gblockhash"]
206 |     return 0
207 | 


--------------------------------------------------------------------------------
/oyente/run_tests.py:
--------------------------------------------------------------------------------
  1 | #!/usr/bin/env python
  2 | 
  3 | import glob
  4 | import json
  5 | import os
  6 | import pickle
  7 | 
  8 | os.chdir(os.path.dirname(__file__))
  9 | 
 10 | from test_evm.global_test_params import (
 11 |     PASS, FAIL, TIME_OUT, UNKNOWN_INSTRUCTION, EXCEPTION, EMPTY_RESULT,
 12 |     INCORRECT_GAS, PICKLE_PATH)
 13 | from test_evm.evm_unit_test import EvmUnitTest
 14 | 
 15 | def remove_temporary_files():
 16 |     rm_file('bytecode')
 17 |     rm_file('bytecode.disasm')
 18 |     rm_file(PICKLE_PATH)
 19 | 
 20 | def rm_file(path):
 21 |     if os.path.isfile(path):
 22 |         os.unlink(path)
 23 | 
 24 | 
 25 | def status(exit_code):
 26 |     if exit_code == 100: return "Pass"
 27 |     if exit_code == 101: return "Fail"
 28 |     if exit_code == 102: return "Time out"
 29 |     if exit_code == 103: return "Unkown instruction"
 30 |     if exit_code == 104: return "Exception"
 31 |     if exit_code == 105: return "Empty result"
 32 |     if exit_code == 106: return "Incorrect gas tracked"
 33 | 
 34 |     return str(exit_code)
 35 | 
 36 | 
 37 | def main():
 38 |     test_dir = 'test_evm/test_data'
 39 |     files = glob.glob(test_dir + '/*.json')
 40 |     test_cases = {}
 41 | 
 42 |     num_tests = num_passes = num_fails = \
 43 |         num_time_outs = num_unkown_instrs = \
 44 |         num_exceptions = num_empty_res = num_incorrect_gas = 0
 45 | 
 46 |     fails, time_outs, \
 47 |         unkown_instrs, exceptions, empty_res, \
 48 |         incorrect_gas = [], [], [], [], [], []
 49 | 
 50 |     for f in files:
 51 |         test_cases.update(json.loads(open(f).read()))
 52 | 
 53 |     print "*****************************************************"
 54 |     print "                  *************                      "
 55 |     print "                      Start                          "
 56 |     for testname, testdata in list(test_cases.items()):
 57 |         print
 58 |         print
 59 |         print "===============Loading: %s====================" % testname
 60 | 
 61 |         current_test = EvmUnitTest(testname, testdata)
 62 | 
 63 |         pickle.dump(current_test, open(PICKLE_PATH, 'wb'), pickle.HIGHEST_PROTOCOL)
 64 | 
 65 |         exit_code = current_test.run_test()
 66 | 
 67 |         # Special case when symExec run into exception but it is correct result
 68 |         if exit_code == EXCEPTION and current_test.is_exception_case():
 69 |             exit_code = PASS
 70 | 
 71 |         if exit_code:
 72 |             print "===============%s!====================" % status(exit_code).upper()
 73 |         else:
 74 |             print "no exit code returned"
 75 | 
 76 |         testname = testname.encode('utf8')
 77 |         num_tests += 1
 78 |         if exit_code == PASS:
 79 |             num_passes += 1
 80 |         elif exit_code == FAIL:
 81 |             fails.append(testname)
 82 |             num_fails += 1
 83 |         elif exit_code == TIME_OUT:
 84 |             time_outs.append(testname)
 85 |             num_time_outs += 1
 86 |         elif exit_code == UNKNOWN_INSTRUCTION:
 87 |             unkown_instrs.append(testname)
 88 |             num_unkown_instrs += 1
 89 |         elif exit_code == EXCEPTION:
 90 |             exceptions.append(testname)
 91 |             num_exceptions += 1
 92 |         elif exit_code == EMPTY_RESULT:
 93 |             empty_res.append(testname)
 94 |             num_empty_res += 1
 95 |         elif exit_code == INCORRECT_GAS:
 96 |             incorrect_gas.append(testname)
 97 |             num_incorrect_gas += 1
 98 |         remove_temporary_files()
 99 | 
100 |     print "Done!"
101 |     print "Total: ", num_tests
102 |     print
103 |     print "Pass: ", num_passes
104 |     print
105 |     print "Fail: ", num_fails, fails
106 |     print
107 |     print "Time out: ", num_time_outs, time_outs
108 |     print
109 |     print "Unkown instruction: ", num_unkown_instrs, unkown_instrs
110 |     print
111 |     print "Exception: ", num_exceptions, exceptions
112 |     print
113 |     print "Empty result: ", num_empty_res, empty_res
114 |     print
115 |     print "Incorrect gas tracked", num_incorrect_gas, incorrect_gas
116 | 
117 | 
118 | if __name__ == '__main__':
119 |     main()
120 | 


--------------------------------------------------------------------------------
/oyente/source_map.py:
--------------------------------------------------------------------------------
  1 | import re
  2 | import six
  3 | import ast
  4 | import json
  5 | 
  6 | import global_params
  7 | 
  8 | from utils import run_command
  9 | from ast_helper import AstHelper
 10 | 
 11 | class Source:
 12 |     def __init__(self, filename):
 13 |         self.filename = filename
 14 |         self.content = self._load_content()
 15 |         self.line_break_positions = self._load_line_break_positions()
 16 | 
 17 |     def _load_content(self):
 18 |         with open(self.filename, 'rb') as f:
 19 |             content = f.read().decode('UTF-8')
 20 |         return content
 21 | 
 22 |     def _load_line_break_positions(self):
 23 |         return [i for i, letter in enumerate(self.content) if letter == '\n']
 24 | 
 25 | class SourceMap:
 26 |     parent_filename = ""
 27 |     position_groups = {}
 28 |     sources = {}
 29 |     ast_helper = None
 30 |     func_to_sig_by_contract = {}
 31 |     remap = ""
 32 |     allow_paths = ""
 33 | 
 34 |     def __init__(self, cname, parent_filename, input_type, root_path="", remap="", allow_paths=""):
 35 |         self.root_path = root_path
 36 |         self.cname = cname
 37 |         self.input_type = input_type
 38 |         if not SourceMap.parent_filename:
 39 |             SourceMap.remap = remap
 40 |             SourceMap.allow_paths = allow_paths
 41 |             SourceMap.parent_filename = parent_filename
 42 |             if input_type == "solidity":
 43 |                 SourceMap.position_groups = SourceMap._load_position_groups()
 44 |             elif input_type == "standard json":
 45 |                 SourceMap.position_groups = SourceMap._load_position_groups_standard_json()
 46 |             else:
 47 |                 raise Exception("There is no such type of input")
 48 |             SourceMap.ast_helper = AstHelper(SourceMap.parent_filename, input_type, SourceMap.remap, SourceMap.allow_paths)
 49 |             SourceMap.func_to_sig_by_contract = SourceMap._get_sig_to_func_by_contract()
 50 |         self.source = self._get_source()
 51 |         self.positions = self._get_positions()
 52 |         self.instr_positions = {}
 53 |         self.var_names = self._get_var_names()
 54 |         self.func_call_names = self._get_func_call_names()
 55 |         self.callee_src_pairs = self._get_callee_src_pairs()
 56 |         self.func_name_to_params = self._get_func_name_to_params()
 57 |         self.sig_to_func = self._get_sig_to_func()
 58 | 
 59 |     def get_source_code(self, pc):
 60 |         try:
 61 |             pos = self.instr_positions[pc]
 62 |         except:
 63 |             return ""
 64 |         begin = pos['begin']
 65 |         end = pos['end']
 66 |         return self.source.content[begin:end]
 67 | 
 68 |     def get_source_code_from_src(self, src):
 69 |         src = src.split(":")
 70 |         start = int(src[0])
 71 |         end = start + int(src[1])
 72 |         return self.source.content[start:end]
 73 | 
 74 |     def get_buggy_line(self, pc):
 75 |         try:
 76 |             pos = self.instr_positions[pc]
 77 |         except:
 78 |             return ""
 79 |         location = self.get_location(pc)
 80 |         begin = self.source.line_break_positions[location['begin']['line'] - 1] + 1
 81 |         end = pos['end']
 82 |         return self.source.content[begin:end]
 83 | 
 84 |     def get_buggy_line_from_src(self, src):
 85 |         pos = self._convert_src_to_pos(src)
 86 |         location = self.get_location_from_src(src)
 87 |         begin = self.source.line_break_positions[location['begin']['line'] - 1] + 1
 88 |         end = pos['end']
 89 |         return self.source.content[begin:end]
 90 | 
 91 |     def get_location(self, pc):
 92 |         pos = self.instr_positions[pc]
 93 |         return self._convert_offset_to_line_column(pos)
 94 | 
 95 |     def get_location_from_src(self, src):
 96 |         pos = self._convert_src_to_pos(src)
 97 |         return self._convert_offset_to_line_column(pos)
 98 | 
 99 |     def get_parameter_or_state_var(self, var_name):
100 |         try:
101 |             names = [
102 |                 node.id for node in ast.walk(ast.parse(var_name))
103 |                 if isinstance(node, ast.Name)
104 |             ]
105 |             if names[0] in self.var_names:
106 |                 return var_name
107 |         except:
108 |             return None
109 |         return None
110 | 
111 |     def _convert_src_to_pos(self, src):
112 |         pos = {}
113 |         src = src.split(":")
114 |         pos['begin'] = int(src[0])
115 |         length = int(src[1])
116 |         pos['end'] = pos['begin'] + length - 1
117 |         return pos
118 | 
119 |     def _get_sig_to_func(self):
120 |         func_to_sig = SourceMap.func_to_sig_by_contract[self.cname]['hashes']
121 |         return dict((sig, func) for func, sig in six.iteritems(func_to_sig))
122 | 
123 |     def _get_func_name_to_params(self):
124 |         func_name_to_params = SourceMap.ast_helper.get_func_name_to_params(self.cname)
125 |         for func_name in func_name_to_params:
126 |             calldataload_position = 0
127 |             for param in func_name_to_params[func_name]:
128 |                 if param['type'] == 'ArrayTypeName':
129 |                     param['position'] = calldataload_position
130 |                     calldataload_position += param['value']
131 |                 else:
132 |                     param['position'] = calldataload_position
133 |                     calldataload_position += 1
134 |         return func_name_to_params
135 | 
136 |     def _get_source(self):
137 |         fname = self.get_filename()
138 |         if fname not in SourceMap.sources:
139 |             SourceMap.sources[fname] = Source(fname)
140 |         return SourceMap.sources[fname]
141 | 
142 |     def _get_callee_src_pairs(self):
143 |         return SourceMap.ast_helper.get_callee_src_pairs(self.cname)
144 | 
145 |     def _get_var_names(self):
146 |         return SourceMap.ast_helper.extract_state_variable_names(self.cname)
147 | 
148 |     def _get_func_call_names(self):
149 |         func_call_srcs = SourceMap.ast_helper.extract_func_call_srcs(self.cname)
150 |         func_call_names = []
151 |         for src in func_call_srcs:
152 |             src = src.split(":")
153 |             start = int(src[0])
154 |             end = start + int(src[1])
155 |             func_call_names.append(self.source.content[start:end])
156 |         return func_call_names
157 | 
158 |     @classmethod
159 |     def _get_sig_to_func_by_contract(cls):
160 |         if cls.allow_paths:
161 |             cmd = 'solc --combined-json hashes %s %s --allow-paths %s' % (cls.remap, cls.parent_filename, cls.allow_paths)
162 |         else:
163 |             cmd = 'solc --combined-json hashes %s %s' % (cls.remap, cls.parent_filename)
164 |         out = run_command(cmd)
165 |         out = json.loads(out)
166 |         return out['contracts']
167 | 
168 |     @classmethod
169 |     def _load_position_groups_standard_json(cls):
170 |         with open('standard_json_output', 'r') as f:
171 |             output = f.read()
172 |         output = json.loads(output)
173 |         return output["contracts"]
174 | 
175 |     @classmethod
176 |     def _load_position_groups(cls):
177 |         if cls.allow_paths:
178 |             cmd = "solc --combined-json asm %s %s --allow-paths %s" % (cls.remap, cls.parent_filename, cls.allow_paths)
179 |         else:
180 |             cmd = "solc --combined-json asm %s %s" % (cls.remap, cls.parent_filename)
181 |         out = run_command(cmd)
182 |         out = json.loads(out)
183 |         return out['contracts']
184 | 
185 |     def _get_positions(self):
186 |         if self.input_type == "solidity":
187 |             asm = SourceMap.position_groups[self.cname]['asm']['.data']['0']
188 |         else:
189 |             filename, contract_name = self.cname.split(":")
190 |             asm = SourceMap.position_groups[filename][contract_name]['evm']['legacyAssembly']['.data']['0']
191 |         positions = asm['.code']
192 |         while(True):
193 |             try:
194 |                 positions.append(None)
195 |                 positions += asm['.data']['0']['.code']
196 |                 asm = asm['.data']['0']
197 |             except:
198 |                 break
199 |         return positions
200 | 
201 |     def _convert_offset_to_line_column(self, pos):
202 |         ret = {}
203 |         ret['begin'] = None
204 |         ret['end'] = None
205 |         if pos['begin'] >= 0 and (pos['end'] - pos['begin'] + 1) >= 0:
206 |             ret['begin'] = self._convert_from_char_pos(pos['begin'])
207 |             ret['end'] = self._convert_from_char_pos(pos['end'])
208 |         return ret
209 | 
210 |     def _convert_from_char_pos(self, pos):
211 |         line = self._find_lower_bound(pos, self.source.line_break_positions)
212 |         if self.source.line_break_positions[line] != pos:
213 |             line += 1
214 |         begin_col = 0 if line == 0 else self.source.line_break_positions[line - 1] + 1
215 |         col = pos - begin_col
216 |         return {'line': line, 'column': col}
217 | 
218 |     def _find_lower_bound(self, target, array):
219 |         start = 0
220 |         length = len(array)
221 |         while length > 0:
222 |             half = length >> 1
223 |             middle = start + half
224 |             if array[middle] <= target:
225 |                 length = length - 1 - half
226 |                 start = middle + 1
227 |             else:
228 |                 length = half
229 |         return start - 1
230 | 
231 |     def get_filename(self):
232 |         return self.cname.split(":")[0]
233 | 


--------------------------------------------------------------------------------
/oyente/state.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "env": {
 3 |     "currentCoinbase": "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
 4 |     "currentDifficulty": "0x0100",
 5 |     "currentGasLimit": "0x0f4240",
 6 |     "currentNumber": "0x00"
 7 |   },
 8 |   "exec": {
 9 |     "data": "0xff",
10 |     "gas": "0x0186a0",
11 |     "gasPrice": "0x5af3107a4000",
12 |     "origin": "cd1722f3947def4cf144679da39c4c32bdc35681",
13 |     "value": "0x0de0b6b3a7640000"
14 |   },
15 |   "gas": "0x013874",
16 |   "Is": {
17 |   	"address": "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
18 |     "balance": "0xd3c21bcecceda1000000"
19 |   },
20 |   "Ia": {
21 |   	"address": "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
22 |     "balance": "0xd3c21bcecceda1000000",
23 |     "storage": {
24 |       "0x00": "0x2222"
25 |     }
26 |   }
27 | }


--------------------------------------------------------------------------------
/oyente/test_evm/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/oyente/test_evm/__init__.py


--------------------------------------------------------------------------------
/oyente/test_evm/evm_unit_test.py:
--------------------------------------------------------------------------------
 1 | import os
 2 | 
 3 | from z3 import *
 4 | 
 5 | from global_params import *
 6 | from utils import to_unsigned
 7 | 
 8 | from global_test_params import *
 9 | 
10 | 
11 | class EvmUnitTest(object):
12 |     def __init__(self, name, data):
13 |         self.name = name
14 |         self.data = data
15 | 
16 |     def bytecode(self):
17 |         return self.data['exec']['code'][2:]
18 | 
19 |     def storage(self):
20 |         storage = self.data['post'].values()[0]['storage']
21 |         return storage if storage != None else {"0": "0"}
22 | 
23 |     def gas_info(self):
24 |         gas_limit = long(self.data['exec']['gas'], 0)
25 |         gas_remaining = long(self.data['gas'], 0)
26 |         return (gas_limit, gas_remaining)
27 | 
28 |     def run_test(self):
29 |         return self._execute_vm(self.bytecode())
30 | 
31 |     def compare_with_symExec_result(self, global_state, analysis):
32 |         if UNIT_TEST == 2: return self.compare_real_value(global_state, analysis)
33 |         if UNIT_TEST == 3: return self.compare_symbolic(global_state)
34 | 
35 |     def compare_real_value(self, global_state, analysis):
36 |         storage_status = self._compare_storage_value(global_state)
37 |         gas_status = self._compare_gas_value(analysis)
38 |         if storage_status != PASS: return storage_status
39 |         if gas_status != PASS: return gas_status
40 |         return PASS
41 | 
42 |     def compare_symbolic(self, global_state):
43 |         for key, value in self.storage().items():
44 |             key, value = long(key, 0), long(value, 0)
45 |             try:
46 |                 symExec_result = global_state['Ia'][str(key)]
47 |             except:
48 |                 return EMPTY_RESULT
49 | 
50 |             s = Solver()
51 |             s.add(symExec_result == BitVecVal(value, 256))
52 |             if s.check() == unsat: # Unsatisfy
53 |                 return FAIL
54 |         return PASS
55 | 
56 |     def is_exception_case(self): # no post, gas field in data
57 |         try:
58 |             post = self.data['post']
59 |             gas = self.data['gas']
60 |             return False
61 |         except:
62 |             return True
63 | 
64 |     def _execute_vm(self, bytecode):
65 |         self._create_bytecode_file(bytecode)
66 |         cmd = os.system('python oyente.py -b -s bytecode')
67 |         exit_code = os.WEXITSTATUS(cmd)
68 |         return exit_code
69 | 
70 |     def _create_bytecode_file(self, bytecode):
71 |         with open('bytecode', 'w') as code_file:
72 |             code_file.write(bytecode)
73 |             code_file.write('\n')
74 |             code_file.close()
75 | 
76 |     def _compare_storage_value(self, global_state):
77 |         for key, value in self.storage().items():
78 |             key, value = long(key, 0), long(value, 0)
79 | 
80 |             try:
81 |                 storage = to_unsigned(long(global_state['Ia'][key]))
82 |             except:
83 |                 return EMPTY_RESULT
84 | 
85 |             if storage != value:
86 |                 return FAIL
87 |         return PASS
88 | 
89 |     def _compare_gas_value(self, analysis):
90 |         gas_used = analysis['gas']
91 |         gas_limit, gas_remaining = self.gas_info()
92 |         if gas_used == gas_limit - gas_remaining:
93 |             return PASS
94 |         else:
95 |             return INCORRECT_GAS
96 | 


--------------------------------------------------------------------------------
/oyente/test_evm/global_test_params.py:
--------------------------------------------------------------------------------
 1 | PASS = 100
 2 | FAIL = 101
 3 | TIME_OUT = 102
 4 | UNKNOWN_INSTRUCTION = 103
 5 | EXCEPTION = 104
 6 | EMPTY_RESULT = 105
 7 | INCORRECT_GAS = 106
 8 | 
 9 | PICKLE_PATH = 'current_test.pickle'
10 | 


--------------------------------------------------------------------------------
/oyente/test_evm/test_data/msize0.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "msize0" : {
 3 |         "_info" : {
 4 |             "comment" : "",
 5 |             "filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++",
 6 |             "lllcversion" : "Version: 0.4.18-develop.2017.9.25+commit.a72237f2.Linux.g++",
 7 |             "source" : "src/VMTestsFiller/vmIOandFlowOperations/msize0Filler.json",
 8 |             "sourceHash" : "5246307dee87f8628255062a3fbdafccff73c6d4a8ba9ed414bfa73f3dda75d8"
 9 |         },
10 |         "callcreates" : [
11 |         ],
12 |         "env" : {
13 |             "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
14 |             "currentDifficulty" : "0x0100",
15 |             "currentGasLimit" : "0x0f4240",
16 |             "currentNumber" : "0x00",
17 |             "currentTimestamp" : "0x01"
18 |         },
19 |         "exec" : {
20 |             "address" : "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
21 |             "caller" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
22 |             "code" : "0x60ff60005259600055",
23 |             "data" : "0x",
24 |             "gas" : "0x0186a0",
25 |             "gasPrice" : "0x5af3107a4000",
26 |             "origin" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
27 |             "value" : "0x0de0b6b3a7640000"
28 |         },
29 |         "gas" : "0x01386f",
30 |         "logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
31 |         "out" : "0x",
32 |         "post" : {
33 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
34 |                 "balance" : "0x152d02c7e14af6800000",
35 |                 "code" : "0x60ff60005259600055",
36 |                 "nonce" : "0x00",
37 |                 "storage" : {
38 |                     "0x00" : "0x20"
39 |                 }
40 |             }
41 |         },
42 |         "pre" : {
43 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
44 |                 "balance" : "0x152d02c7e14af6800000",
45 |                 "code" : "0x60ff60005259600055",
46 |                 "nonce" : "0x00",
47 |                 "storage" : {
48 |                 }
49 |             }
50 |         }
51 |     }
52 | }


--------------------------------------------------------------------------------
/oyente/test_evm/test_data/msize1.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "msize1" : {
 3 |         "_info" : {
 4 |             "comment" : "",
 5 |             "filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++",
 6 |             "lllcversion" : "Version: 0.4.18-develop.2017.9.25+commit.a72237f2.Linux.g++",
 7 |             "source" : "src/VMTestsFiller/vmIOandFlowOperations/msize1Filler.json",
 8 |             "sourceHash" : "c05b6682215374c094f377904324535fbf187e46f8f87b6266924985525920cd"
 9 |         },
10 |         "callcreates" : [
11 |         ],
12 |         "env" : {
13 |             "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
14 |             "currentDifficulty" : "0x0100",
15 |             "currentGasLimit" : "0x0f4240",
16 |             "currentNumber" : "0x00",
17 |             "currentTimestamp" : "0x01"
18 |         },
19 |         "exec" : {
20 |             "address" : "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
21 |             "caller" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
22 |             "code" : "0x64ffffffffff60005259600055",
23 |             "data" : "0x",
24 |             "gas" : "0x0186a0",
25 |             "gasPrice" : "0x5af3107a4000",
26 |             "origin" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
27 |             "value" : "0x0de0b6b3a7640000"
28 |         },
29 |         "gas" : "0x01386f",
30 |         "logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
31 |         "out" : "0x",
32 |         "post" : {
33 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
34 |                 "balance" : "0x152d02c7e14af6800000",
35 |                 "code" : "0x64ffffffffff60005259600055",
36 |                 "nonce" : "0x00",
37 |                 "storage" : {
38 |                     "0x00" : "0x20"
39 |                 }
40 |             }
41 |         },
42 |         "pre" : {
43 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
44 |                 "balance" : "0x152d02c7e14af6800000",
45 |                 "code" : "0x64ffffffffff60005259600055",
46 |                 "nonce" : "0x00",
47 |                 "storage" : {
48 |                 }
49 |             }
50 |         }
51 |     }
52 | }


--------------------------------------------------------------------------------
/oyente/test_evm/test_data/msize2.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "msize2" : {
 3 |         "_info" : {
 4 |             "comment" : "",
 5 |             "filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++",
 6 |             "lllcversion" : "Version: 0.4.18-develop.2017.9.25+commit.a72237f2.Linux.g++",
 7 |             "source" : "src/VMTestsFiller/vmIOandFlowOperations/msize2Filler.json",
 8 |             "sourceHash" : "c35303fa36e3ccc67a101077f50ea23b3435cf855d488be64a7539fa73e56b17"
 9 |         },
10 |         "callcreates" : [
11 |         ],
12 |         "env" : {
13 |             "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
14 |             "currentDifficulty" : "0x0100",
15 |             "currentGasLimit" : "0x0f4240",
16 |             "currentNumber" : "0x00",
17 |             "currentTimestamp" : "0x01"
18 |         },
19 |         "exec" : {
20 |             "address" : "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
21 |             "caller" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
22 |             "code" : "0x64ffffffffff60005261eeee60205259600055",
23 |             "data" : "0x",
24 |             "gas" : "0x0186a0",
25 |             "gasPrice" : "0x5af3107a4000",
26 |             "origin" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
27 |             "value" : "0x0de0b6b3a7640000"
28 |         },
29 |         "gas" : "0x013863",
30 |         "logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
31 |         "out" : "0x",
32 |         "post" : {
33 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
34 |                 "balance" : "0x152d02c7e14af6800000",
35 |                 "code" : "0x64ffffffffff60005261eeee60205259600055",
36 |                 "nonce" : "0x00",
37 |                 "storage" : {
38 |                     "0x00" : "0x40"
39 |                 }
40 |             }
41 |         },
42 |         "pre" : {
43 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
44 |                 "balance" : "0x152d02c7e14af6800000",
45 |                 "code" : "0x64ffffffffff60005261eeee60205259600055",
46 |                 "nonce" : "0x00",
47 |                 "storage" : {
48 |                 }
49 |             }
50 |         }
51 |     }
52 | }


--------------------------------------------------------------------------------
/oyente/test_evm/test_data/msize3.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "msize3" : {
 3 |         "_info" : {
 4 |             "comment" : "",
 5 |             "filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++",
 6 |             "lllcversion" : "Version: 0.4.18-develop.2017.9.25+commit.a72237f2.Linux.g++",
 7 |             "source" : "src/VMTestsFiller/vmIOandFlowOperations/msize3Filler.json",
 8 |             "sourceHash" : "2cb140ab799864ce64b602e462a5fb3697a6bf7fe9621286ca5d4be0961afb0f"
 9 |         },
10 |         "callcreates" : [
11 |         ],
12 |         "env" : {
13 |             "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
14 |             "currentDifficulty" : "0x0100",
15 |             "currentGasLimit" : "0x0f4240",
16 |             "currentNumber" : "0x00",
17 |             "currentTimestamp" : "0x01"
18 |         },
19 |         "exec" : {
20 |             "address" : "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
21 |             "caller" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
22 |             "code" : "0x64ffffffffff60005261eeee605a5259600055",
23 |             "data" : "0x",
24 |             "gas" : "0x0186a0",
25 |             "gasPrice" : "0x5af3107a4000",
26 |             "origin" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
27 |             "value" : "0x0de0b6b3a7640000"
28 |         },
29 |         "gas" : "0x01385d",
30 |         "logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
31 |         "out" : "0x",
32 |         "post" : {
33 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
34 |                 "balance" : "0x152d02c7e14af6800000",
35 |                 "code" : "0x64ffffffffff60005261eeee605a5259600055",
36 |                 "nonce" : "0x00",
37 |                 "storage" : {
38 |                     "0x00" : "0x80"
39 |                 }
40 |             }
41 |         },
42 |         "pre" : {
43 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
44 |                 "balance" : "0x152d02c7e14af6800000",
45 |                 "code" : "0x64ffffffffff60005261eeee605a5259600055",
46 |                 "nonce" : "0x00",
47 |                 "storage" : {
48 |                 }
49 |             }
50 |         }
51 |     }
52 | }


--------------------------------------------------------------------------------
/oyente/test_evm/test_data/mstore0.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "mstore0" : {
 3 |         "_info" : {
 4 |             "comment" : "",
 5 |             "filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++",
 6 |             "lllcversion" : "Version: 0.4.18-develop.2017.9.25+commit.a72237f2.Linux.g++",
 7 |             "source" : "src/VMTestsFiller/vmIOandFlowOperations/mstore0Filler.json",
 8 |             "sourceHash" : "3b641446154f3ccf59d40592a119b8f3526ff4220800a35f28d1eeda7d7f01ad"
 9 |         },
10 |         "callcreates" : [
11 |         ],
12 |         "env" : {
13 |             "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
14 |             "currentDifficulty" : "0x0100",
15 |             "currentGasLimit" : "0x0f4240",
16 |             "currentNumber" : "0x00",
17 |             "currentTimestamp" : "0x01"
18 |         },
19 |         "exec" : {
20 |             "address" : "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
21 |             "caller" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
22 |             "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600152600151600155",
23 |             "data" : "0x",
24 |             "gas" : "0x0186a0",
25 |             "gasPrice" : "0x5af3107a4000",
26 |             "origin" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
27 |             "value" : "0x0de0b6b3a7640000"
28 |         },
29 |         "gas" : "0x013868",
30 |         "logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
31 |         "out" : "0x",
32 |         "post" : {
33 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
34 |                 "balance" : "0x152d02c7e14af6800000",
35 |                 "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600152600151600155",
36 |                 "nonce" : "0x00",
37 |                 "storage" : {
38 |                     "0x01" : "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
39 |                 }
40 |             }
41 |         },
42 |         "pre" : {
43 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
44 |                 "balance" : "0x152d02c7e14af6800000",
45 |                 "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600152600151600155",
46 |                 "nonce" : "0x00",
47 |                 "storage" : {
48 |                 }
49 |             }
50 |         }
51 |     }
52 | }


--------------------------------------------------------------------------------
/oyente/test_evm/test_data/mstore1.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "mstore1" : {
 3 |         "_info" : {
 4 |             "comment" : "",
 5 |             "filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++",
 6 |             "lllcversion" : "Version: 0.4.18-develop.2017.9.25+commit.a72237f2.Linux.g++",
 7 |             "source" : "src/VMTestsFiller/vmIOandFlowOperations/mstore1Filler.json",
 8 |             "sourceHash" : "28766d6a75aaf16f55d51716b24a47a000d2ef9266ccd2126f745801b5b8e356"
 9 |         },
10 |         "callcreates" : [
11 |         ],
12 |         "env" : {
13 |             "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
14 |             "currentDifficulty" : "0x0100",
15 |             "currentGasLimit" : "0x0f4240",
16 |             "currentNumber" : "0x00",
17 |             "currentTimestamp" : "0x01"
18 |         },
19 |         "exec" : {
20 |             "address" : "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
21 |             "caller" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
22 |             "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600201600152600151600155",
23 |             "data" : "0x",
24 |             "gas" : "0x0186a0",
25 |             "gasPrice" : "0x5af3107a4000",
26 |             "origin" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
27 |             "value" : "0x0de0b6b3a7640000"
28 |         },
29 |         "gas" : "0x013862",
30 |         "logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
31 |         "out" : "0x",
32 |         "post" : {
33 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
34 |                 "balance" : "0x152d02c7e14af6800000",
35 |                 "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600201600152600151600155",
36 |                 "nonce" : "0x00",
37 |                 "storage" : {
38 |                     "0x01" : "0x01"
39 |                 }
40 |             }
41 |         },
42 |         "pre" : {
43 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
44 |                 "balance" : "0x152d02c7e14af6800000",
45 |                 "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600201600152600151600155",
46 |                 "nonce" : "0x00",
47 |                 "storage" : {
48 |                 }
49 |             }
50 |         }
51 |     }
52 | }


--------------------------------------------------------------------------------
/oyente/test_evm/test_data/mstore8MemExp.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "mstore8MemExp" : {
 3 |         "_info" : {
 4 |             "comment" : "",
 5 |             "filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++",
 6 |             "lllcversion" : "Version: 0.4.18-develop.2017.9.25+commit.a72237f2.Linux.g++",
 7 |             "source" : "src/VMTestsFiller/vmIOandFlowOperations/mstore8MemExpFiller.json",
 8 |             "sourceHash" : "794876662f03c8b7b211061293e045898d347e624f2d5030d9f6724f4d838792"
 9 |         },
10 |         "env" : {
11 |             "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
12 |             "currentDifficulty" : "0x0100",
13 |             "currentGasLimit" : "0x01f4153d80",
14 |             "currentNumber" : "0x00",
15 |             "currentTimestamp" : "0x01"
16 |         },
17 |         "exec" : {
18 |             "address" : "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
19 |             "caller" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
20 |             "code" : "0x60f1630fffffff53",
21 |             "data" : "0x",
22 |             "gas" : "0x800570",
23 |             "gasPrice" : "0x01",
24 |             "origin" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
25 |             "value" : "0x0de0b6b3a7640000"
26 |         },
27 |         "pre" : {
28 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
29 |                 "balance" : "0x152d02c7e14af6800000",
30 |                 "code" : "0x60f1630fffffff53",
31 |                 "nonce" : "0x00",
32 |                 "storage" : {
33 |                 }
34 |             }
35 |         }
36 |     }
37 | }


--------------------------------------------------------------------------------
/oyente/test_evm/test_data/mstore8WordToBigError.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "mstore8WordToBigError" : {
 3 |         "_info" : {
 4 |             "comment" : "",
 5 |             "filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++",
 6 |             "lllcversion" : "Version: 0.4.18-develop.2017.9.25+commit.a72237f2.Linux.g++",
 7 |             "source" : "src/VMTestsFiller/vmIOandFlowOperations/mstore8WordToBigErrorFiller.json",
 8 |             "sourceHash" : "81921779550607de4b111ba7a36aefeabbab0453f1c71c52a92280e5e4f37854"
 9 |         },
10 |         "callcreates" : [
11 |         ],
12 |         "env" : {
13 |             "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
14 |             "currentDifficulty" : "0x0100",
15 |             "currentGasLimit" : "0x0f4240",
16 |             "currentNumber" : "0x00",
17 |             "currentTimestamp" : "0x01"
18 |         },
19 |         "exec" : {
20 |             "address" : "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
21 |             "caller" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
22 |             "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600153600151600155",
23 |             "data" : "0x",
24 |             "gas" : "0x0186a0",
25 |             "gasPrice" : "0x5af3107a4000",
26 |             "origin" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
27 |             "value" : "0x0de0b6b3a7640000"
28 |         },
29 |         "gas" : "0x013868",
30 |         "logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
31 |         "out" : "0x",
32 |         "post" : {
33 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
34 |                 "balance" : "0x152d02c7e14af6800000",
35 |                 "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600153600151600155",
36 |                 "nonce" : "0x00",
37 |                 "storage" : {
38 |                     "0x01" : "0xff00000000000000000000000000000000000000000000000000000000000000"
39 |                 }
40 |             }
41 |         },
42 |         "pre" : {
43 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
44 |                 "balance" : "0x152d02c7e14af6800000",
45 |                 "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600153600151600155",
46 |                 "nonce" : "0x00",
47 |                 "storage" : {
48 |                 }
49 |             }
50 |         }
51 |     }
52 | }


--------------------------------------------------------------------------------
/oyente/test_evm/test_data/mstore8_0.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "mstore8_0" : {
 3 |         "_info" : {
 4 |             "comment" : "",
 5 |             "filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++",
 6 |             "lllcversion" : "Version: 0.4.18-develop.2017.9.25+commit.a72237f2.Linux.g++",
 7 |             "source" : "src/VMTestsFiller/vmIOandFlowOperations/mstore8_0Filler.json",
 8 |             "sourceHash" : "a17124580c3ab3f73d1e59b6791b03f4c491f0ca2e9595538c9e807d6bcc4908"
 9 |         },
10 |         "callcreates" : [
11 |         ],
12 |         "env" : {
13 |             "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
14 |             "currentDifficulty" : "0x0100",
15 |             "currentGasLimit" : "0x0f4240",
16 |             "currentNumber" : "0x00",
17 |             "currentTimestamp" : "0x01"
18 |         },
19 |         "exec" : {
20 |             "address" : "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
21 |             "caller" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
22 |             "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600153600151600155",
23 |             "data" : "0x",
24 |             "gas" : "0x0186a0",
25 |             "gasPrice" : "0x5af3107a4000",
26 |             "origin" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
27 |             "value" : "0x0de0b6b3a7640000"
28 |         },
29 |         "gas" : "0x013868",
30 |         "logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
31 |         "out" : "0x",
32 |         "post" : {
33 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
34 |                 "balance" : "0x152d02c7e14af6800000",
35 |                 "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600153600151600155",
36 |                 "nonce" : "0x00",
37 |                 "storage" : {
38 |                     "0x01" : "0xff00000000000000000000000000000000000000000000000000000000000000"
39 |                 }
40 |             }
41 |         },
42 |         "pre" : {
43 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
44 |                 "balance" : "0x152d02c7e14af6800000",
45 |                 "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600153600151600155",
46 |                 "nonce" : "0x00",
47 |                 "storage" : {
48 |                 }
49 |             }
50 |         }
51 |     }
52 | }


--------------------------------------------------------------------------------
/oyente/test_evm/test_data/mstore8_1.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "mstore8_1" : {
 3 |         "_info" : {
 4 |             "comment" : "",
 5 |             "filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++",
 6 |             "lllcversion" : "Version: 0.4.18-develop.2017.9.25+commit.a72237f2.Linux.g++",
 7 |             "source" : "src/VMTestsFiller/vmIOandFlowOperations/mstore8_1Filler.json",
 8 |             "sourceHash" : "4b6afbb90e2aad6c7d2834e511f6a72881a74aafd2387d7f8a780a97c92bfa23"
 9 |         },
10 |         "callcreates" : [
11 |         ],
12 |         "env" : {
13 |             "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
14 |             "currentDifficulty" : "0x0100",
15 |             "currentGasLimit" : "0x0f4240",
16 |             "currentNumber" : "0x00",
17 |             "currentTimestamp" : "0x01"
18 |         },
19 |         "exec" : {
20 |             "address" : "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
21 |             "caller" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
22 |             "code" : "0x60ff60015360ee600253600051600155",
23 |             "data" : "0x",
24 |             "gas" : "0x0186a0",
25 |             "gasPrice" : "0x5af3107a4000",
26 |             "origin" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
27 |             "value" : "0x0de0b6b3a7640000"
28 |         },
29 |         "gas" : "0x013862",
30 |         "logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
31 |         "out" : "0x",
32 |         "post" : {
33 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
34 |                 "balance" : "0x152d02c7e14af6800000",
35 |                 "code" : "0x60ff60015360ee600253600051600155",
36 |                 "nonce" : "0x00",
37 |                 "storage" : {
38 |                     "0x01" : "0xffee0000000000000000000000000000000000000000000000000000000000"
39 |                 }
40 |             }
41 |         },
42 |         "pre" : {
43 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
44 |                 "balance" : "0x152d02c7e14af6800000",
45 |                 "code" : "0x60ff60015360ee600253600051600155",
46 |                 "nonce" : "0x00",
47 |                 "storage" : {
48 |                 }
49 |             }
50 |         }
51 |     }
52 | }


--------------------------------------------------------------------------------
/oyente/test_evm/test_data/mstoreMemExp.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "mstoreMemExp" : {
 3 |         "_info" : {
 4 |             "comment" : "",
 5 |             "filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++",
 6 |             "lllcversion" : "Version: 0.4.18-develop.2017.9.25+commit.a72237f2.Linux.g++",
 7 |             "source" : "src/VMTestsFiller/vmIOandFlowOperations/mstoreMemExpFiller.json",
 8 |             "sourceHash" : "2d3bca692de504313800dd757e9bc51a52fd1f216fb8cfe232ad10f72f30e183"
 9 |         },
10 |         "env" : {
11 |             "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
12 |             "currentDifficulty" : "0x0100",
13 |             "currentGasLimit" : "0x01f4153d80",
14 |             "currentNumber" : "0x00",
15 |             "currentTimestamp" : "0x01"
16 |         },
17 |         "exec" : {
18 |             "address" : "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
19 |             "caller" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
20 |             "code" : "0x60f1630fffffff52",
21 |             "data" : "0x",
22 |             "gas" : "0x01f4153d80",
23 |             "gasPrice" : "0x01",
24 |             "origin" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
25 |             "value" : "0x0de0b6b3a7640000"
26 |         },
27 |         "pre" : {
28 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
29 |                 "balance" : "0x152d02c7e14af6800000",
30 |                 "code" : "0x60f1630fffffff52",
31 |                 "nonce" : "0x00",
32 |                 "storage" : {
33 |                 }
34 |             }
35 |         }
36 |     }
37 | }


--------------------------------------------------------------------------------
/oyente/test_evm/test_data/mstoreWordToBigError.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "mstoreWordToBigError" : {
 3 |         "_info" : {
 4 |             "comment" : "",
 5 |             "filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++",
 6 |             "lllcversion" : "Version: 0.4.18-develop.2017.9.25+commit.a72237f2.Linux.g++",
 7 |             "source" : "src/VMTestsFiller/vmIOandFlowOperations/mstoreWordToBigErrorFiller.json",
 8 |             "sourceHash" : "46919c99307c822c29fe5b7db2d682352d7e2e56749f33f6f1f08abe3b381cde"
 9 |         },
10 |         "callcreates" : [
11 |         ],
12 |         "env" : {
13 |             "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
14 |             "currentDifficulty" : "0x0100",
15 |             "currentGasLimit" : "0x01f4153d80",
16 |             "currentNumber" : "0x00",
17 |             "currentTimestamp" : "0x01"
18 |         },
19 |         "exec" : {
20 |             "address" : "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
21 |             "caller" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
22 |             "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600152600151600155",
23 |             "data" : "0x",
24 |             "gas" : "0x0186a0",
25 |             "gasPrice" : "0x5af3107a4000",
26 |             "origin" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
27 |             "value" : "0x0de0b6b3a7640000"
28 |         },
29 |         "gas" : "0x013868",
30 |         "logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
31 |         "out" : "0x",
32 |         "post" : {
33 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
34 |                 "balance" : "0x152d02c7e14af6800000",
35 |                 "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600152600151600155",
36 |                 "nonce" : "0x00",
37 |                 "storage" : {
38 |                     "0x01" : "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
39 |                 }
40 |             }
41 |         },
42 |         "pre" : {
43 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
44 |                 "balance" : "0x152d02c7e14af6800000",
45 |                 "code" : "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600152600151600155",
46 |                 "nonce" : "0x00",
47 |                 "storage" : {
48 |                 }
49 |             }
50 |         }
51 |     }
52 | }


--------------------------------------------------------------------------------
/oyente/test_evm/test_data/mstore_mload0.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     "mstore_mload0" : {
 3 |         "_info" : {
 4 |             "comment" : "",
 5 |             "filledwith" : "cpp-1.3.0+commit.6e0ce939.Linux.g++",
 6 |             "lllcversion" : "Version: 0.4.18-develop.2017.9.25+commit.a72237f2.Linux.g++",
 7 |             "source" : "src/VMTestsFiller/vmIOandFlowOperations/mstore_mload0Filler.json",
 8 |             "sourceHash" : "32b0acbc536458485dd91e892b931f997aac5cec5041fe2007624b9e6f6209dc"
 9 |         },
10 |         "callcreates" : [
11 |         ],
12 |         "env" : {
13 |             "currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
14 |             "currentDifficulty" : "0x0100",
15 |             "currentGasLimit" : "0x0f4240",
16 |             "currentNumber" : "0x00",
17 |             "currentTimestamp" : "0x01"
18 |         },
19 |         "exec" : {
20 |             "address" : "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
21 |             "caller" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
22 |             "code" : "0x6017600052600051600155",
23 |             "data" : "0x",
24 |             "gas" : "0x0186a0",
25 |             "gasPrice" : "0x5af3107a4000",
26 |             "origin" : "0xcd1722f3947def4cf144679da39c4c32bdc35681",
27 |             "value" : "0x0de0b6b3a7640000"
28 |         },
29 |         "gas" : "0x01386b",
30 |         "logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
31 |         "out" : "0x",
32 |         "post" : {
33 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
34 |                 "balance" : "0x152d02c7e14af6800000",
35 |                 "code" : "0x6017600052600051600155",
36 |                 "nonce" : "0x00",
37 |                 "storage" : {
38 |                     "0x01" : "0x17"
39 |                 }
40 |             }
41 |         },
42 |         "pre" : {
43 |             "0x0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
44 |                 "balance" : "0x152d02c7e14af6800000",
45 |                 "code" : "0x6017600052600051600155",
46 |                 "nonce" : "0x00",
47 |                 "storage" : {
48 |                 }
49 |             }
50 |         }
51 |     }
52 | }


--------------------------------------------------------------------------------
/oyente/vargenerator.py:
--------------------------------------------------------------------------------
 1 | class Generator:
 2 |     def __init__(self):
 3 |         self.countstack = 0
 4 |         self.countdata = 0
 5 |         self.count = 0
 6 | 
 7 |     def gen_stack_var(self):
 8 |         self.countstack += 1
 9 |         return "s" + str(self.countstack)
10 | 
11 |     def gen_data_var(self):
12 |         self.countdata += 1
13 |         return "Id_" + str(self.countdata)
14 | 
15 |     def gen_data_var(self, position):
16 |         self.countdata += 1
17 |         return "Id_" + str(self.countdata)
18 | 
19 |     def gen_data_size(self):
20 |         return "Id_size"
21 | 
22 |     def gen_mem_var(self, address):
23 |         return "mem_" + str(address)
24 | 
25 |     def gen_arbitrary_var(self):
26 |         self.count += 1
27 |         return "some_var_" + str(self.count)
28 | 
29 |     def gen_arbitrary_address_var(self):
30 |         self.count += 1
31 |         return "some_address_" + str(self.count)
32 | 
33 |     def gen_owner_store_var(self, position, var_name=""):
34 |         return "Ia_store-%s-%s" % (str(position), var_name)
35 | 
36 |     def gen_gas_var(self):
37 |         self.count += 1
38 |         return "gas_" + str(self.count)
39 | 
40 |     def gen_gas_price_var(self):
41 |         return "Ip"
42 | 
43 |     def gen_address_var(self):
44 |         return "Ia"
45 | 
46 |     def gen_caller_var(self):
47 |         return "Is"
48 | 
49 |     def gen_origin_var(self):
50 |         return "Io"
51 | 
52 |     def gen_balance_var(self):
53 |         self.count += 1
54 |         return "balance_" + str(self.count)
55 | 
56 |     def gen_code_var(self, address, position, bytecount):
57 |         return "code_" + str(address) + "_" + str(position) + "_" + str(bytecount)
58 | 
59 |     def gen_code_size_var(self, address):
60 |         return "code_size_" + str(address)
61 | 


--------------------------------------------------------------------------------
/oyente/vulnerability.py:
--------------------------------------------------------------------------------
  1 | import re
  2 | 
  3 | class Vulnerability:
  4 |     def __init__(self, source_map, pcs):
  5 |         self.source_map = source_map
  6 |         self.pcs = self._rm_general_false_positives(pcs)
  7 |         if source_map:
  8 |             self.warnings = self._warnings()
  9 | 
 10 |     def is_vulnerable(self):
 11 |         return bool(self.pcs)
 12 | 
 13 |     def get_warnings(self):
 14 |         return self.warnings
 15 | 
 16 |     def _rm_general_false_positives(self, pcs):
 17 |         new_pcs = pcs
 18 |         if self.source_map:
 19 |             new_pcs = self._rm_pcs_having_no_source_code(new_pcs)
 20 |             new_pcs = self._reduce_pcs_having_the_same_pos(new_pcs)
 21 |         return new_pcs
 22 | 
 23 |     def _rm_pcs_having_no_source_code(self, pcs):
 24 |         return [pc for pc in pcs if self.source_map.get_source_code(pc)]
 25 | 
 26 |     def _reduce_pcs_having_the_same_pos(self, pcs):
 27 |         d = {}
 28 |         for pc in pcs:
 29 |             pos = str(self.source_map.instr_positions[pc])
 30 |             if pos not in d:
 31 |                 d[pos] = pc
 32 |         return d.values()
 33 | 
 34 |     def _warnings(self):
 35 |         warnings = []
 36 |         for pc in self.pcs:
 37 |             source_code = self.source_map.get_source_code(pc)
 38 |             if not source_code:
 39 |                 continue
 40 | 
 41 |             source_code = self.source_map.get_buggy_line(pc)
 42 |             s = self._warning_content(pc, source_code)
 43 |             if s:
 44 |                 warnings.append(s)
 45 |         return warnings
 46 | 
 47 |     def _warning_content(self, pc, source_code):
 48 |         new_line_idx = source_code.find('\n')
 49 |         source_code = source_code.split('\n', 1)[0]
 50 |         location = self.source_map.get_location(pc)
 51 | 
 52 |         source = re.sub(self.source_map.root_path, '', self.source_map.get_filename())
 53 |         line = location['begin']['line'] + 1
 54 |         column = location['begin']['column'] + 1
 55 |         s = '%s:%s:%s: Warning: %s.\n' % (source, line, column, self.name)
 56 |         s += source_code
 57 |         if new_line_idx != -1:
 58 |             s += '\n' + self._leading_spaces(source_code) + '^\n'
 59 |             s += 'Spanning multiple lines.'
 60 |         return s
 61 | 
 62 |     def _leading_spaces(self, s):
 63 |         stripped_s = s.lstrip('[ \t]')
 64 |         len_of_leading_spaces = len(s) - len(stripped_s)
 65 |         return s[0:len_of_leading_spaces]
 66 | 
 67 |     def __str__(self):
 68 |         s = ''
 69 |         for warning in self.warnings:
 70 |             s += '\n' + warning
 71 |         return s.lstrip('\n')
 72 | 
 73 | class CallStack(Vulnerability):
 74 |     def __init__(self, source_map, pcs, calls_affect_state):
 75 |         self.source_map = source_map
 76 |         self.pcs = self._rm_false_positives(pcs, calls_affect_state)
 77 |         if source_map:
 78 |             self.name = 'Callstack Depth Attack Vulnerability'
 79 |             self.warnings = Vulnerability._warnings(self)
 80 | 
 81 |     def _rm_false_positives(self, pcs, calls_affect_state):
 82 |         new_pcs = Vulnerability._rm_general_false_positives(self, pcs)
 83 |         return self._rm_pcs_not_affect_state(new_pcs, calls_affect_state)
 84 | 
 85 |     def _rm_pcs_not_affect_state(self, pcs, calls_affect_state):
 86 |         new_pcs = []
 87 |         for pc in pcs:
 88 |             if pc in calls_affect_state and calls_affect_state[pc] or pc not in calls_affect_state:
 89 |                 new_pcs.append(pc)
 90 |         return new_pcs
 91 | 
 92 | class TimeDependency(Vulnerability):
 93 |     def __init__(self, source_map, pcs):
 94 |         self.name = 'Timestamp Dependency'
 95 |         Vulnerability.__init__(self, source_map, pcs)
 96 | 
 97 | class Reentrancy(Vulnerability):
 98 |     def __init__(self, source_map, pcs):
 99 |         self.name = 'Re-Entrancy Vulnerability'
100 |         Vulnerability.__init__(self, source_map, pcs)
101 | 
102 | class MoneyConcurrency(Vulnerability):
103 |     def __init__(self, source_map, flows):
104 |         self.name = 'Transaction-Ordering Dependency'
105 |         self.source_map = source_map
106 |         self.flows = flows
107 |         if source_map:
108 |             self.warnings_of_flows = self._warnings_of_flows()
109 | 
110 |     def is_vulnerable(self):
111 |         return bool(self.flows)
112 | 
113 |     def get_warnings_of_flows(self):
114 |         return self.warnings_of_flows
115 | 
116 |     def _warnings_of_flows(self):
117 |         warnings_of_flows = []
118 |         for pcs in self.flows:
119 |             s = ''
120 |             pcs = Vulnerability._rm_general_false_positives(self, pcs)
121 |             warnings = []
122 |             for pc in pcs:
123 |                 source_code = self.source_map.get_source_code(pc)
124 |                 if not source_code:
125 |                     continue
126 | 
127 |                 source_code = self.source_map.get_buggy_line(pc)
128 |                 s = Vulnerability._warning_content(self, pc, source_code)
129 |                 if s:
130 |                     warnings.append(s)
131 |             warnings_of_flows.append(warnings)
132 |         return warnings_of_flows
133 | 
134 |     def __str__(self):
135 |         s = ''
136 |         for i, warnings in enumerate(self.warnings_of_flows):
137 |             if i != 0:
138 |                 s += '\n'
139 |             s += 'Flow' + str(i + 1)
140 |             for warning in warnings:
141 |                 s += '\n' + warning
142 |         return s
143 | 
144 | class AssertionFailure(Vulnerability):
145 |     def __init__(self, source_map, assertions):
146 |         self.source_map = source_map
147 |         self.assertions = self._reduce_pcs_having_the_same_pos(assertions)
148 |         self.name = ' '.join(re.findall('[A-Z][a-z]+', self.__class__.__name__))
149 |         if self.name == 'AssertionFailure' and not source_map:
150 |             raise Exception("source_map attribute can't be None")
151 |         self.warnings = self._warnings()
152 | 
153 |     def is_vulnerable(self):
154 |         return bool(self.assertions)
155 | 
156 |     def _reduce_pcs_having_the_same_pos(self, assertions):
157 |         d = {}
158 |         for asrt in assertions:
159 |             pos = str(self.source_map.instr_positions[asrt.pc])
160 |             if pos not in d:
161 |                 d[pos] = asrt
162 |         return d.values()
163 | 
164 |     def _warnings(self):
165 |         warnings = []
166 |         for asrt in self.assertions:
167 |             source_code = self.source_map.get_buggy_line(asrt.pc)
168 |             s = Vulnerability._warning_content(self, asrt.pc, source_code)
169 | 
170 |             model = ''
171 |             for variable in asrt.model.decls():
172 |                 var_name = str(variable)
173 |                 if len(var_name.split('-')) > 2:
174 |                     var_name = var_name.split('-')[2]
175 |                 if self.source_map.get_parameter_or_state_var(var_name):
176 |                     model += '\n    ' + var_name + ' = ' + str(asrt.model[variable])
177 |             if model:
178 |                 model = "\n%s occurs if:%s" % (self.name, model)
179 |                 s += model
180 |             if s:
181 |                 warnings.append(s)
182 |         return warnings
183 | 
184 | class IntegerUnderflow(AssertionFailure):
185 |     pass
186 | 
187 | class IntegerOverflow(AssertionFailure):
188 |     pass
189 | 
190 | class ParityMultisigBug2(Vulnerability):
191 |     def __init__(self, source_map):
192 |         self.source_map = source_map
193 |         self.pairs = self._get_contracts_containing_selfdestruct_opcode()
194 |         self.warnings = self._warnings()
195 | 
196 |     def is_vulnerable(self):
197 |         return bool(self.pairs)
198 | 
199 |     def _warnings(self):
200 |         warnings = []
201 |         for pair in self.pairs:
202 |             source_code = self.source_map.get_buggy_line_from_src(pair[1])
203 |             new_line_idx = source_code.find('\n')
204 |             source_code = source_code.split('\n', 1)[0]
205 |             location = self.source_map.get_location_from_src(pair[1])
206 | 
207 |             source = re.sub(self.source_map.root_path, '', self.source_map.get_filename())
208 |             line = location['begin']['line'] + 1
209 |             column = location['begin']['column'] + 1
210 | 
211 |             s = '%s:%s:%s: Warning: Parity Multisig Bug 2.\n' % (source, line, column)
212 |             s += source_code
213 |             if new_line_idx != -1:
214 |                 s += '\n' + Vulnerability._leading_spaces(self, source_code) + '^\n'
215 |                 s += 'Spanning multiple lines.'
216 |             warnings.append(s)
217 |         return warnings
218 | 
219 |     def _get_contracts_containing_selfdestruct_opcode(self):
220 |         ret = []
221 |         for pair in self.source_map.callee_src_pairs:
222 |             disasm_data = open(pair[0] + ".evm.disasm").read()
223 |             regex = re.compile("SELFDESTRUCT|SUICIDE")
224 |             if regex.search(disasm_data):
225 |                 ret.append(pair)
226 |         return ret
227 | 
228 | 


--------------------------------------------------------------------------------
/pylintrc:
--------------------------------------------------------------------------------
1 | [master]
2 | disable=invalid-name,missing-docstring,too-many-statements


--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
 1 | from setuptools import find_packages, setup
 2 | 
 3 | setup(
 4 |     name='oyente',
 5 |     version='0.2.7',
 6 |     author='Loi Luu',
 7 |     # author_email='',
 8 |     url='https://github.com/melonport/oyente',
 9 |     description='An analysis tool for smart contracts',
10 |     long_description=open('README.md').read(),
11 |     license='GPL',
12 |     keywords='ethereum smart contracts',
13 |     classifiers=[
14 |         'Environment :: Console',
15 |         'Development Status :: 4 - Beta',
16 |         'Intended Audience :: Developers',
17 |         'Intended Audience :: End Users/Desktop',
18 |         'Intended Audience :: Information Technology',
19 |         'Intended Audience :: System Administrators',
20 |         'License :: OSI Approved :: BSD License',
21 |         'Operating System :: POSIX',
22 |         'Operating System :: MacOS :: MacOS X',
23 |         'Programming Language :: Python',
24 |         'Topic :: Utilities',
25 |     ],
26 |     packages=find_packages(),
27 |     package_data={
28 |         'oyente': ['state.json'],
29 |     },
30 |     entry_points={
31 |         'console_scripts': [
32 |             'oyente = oyente.oyente:main',
33 |         ]
34 |     },
35 |     install_requires=[
36 |         'requests',
37 |         'web3',
38 |         'z3-solver',
39 |         'crytic-compile',
40 |     ]
41 | )
42 | 


--------------------------------------------------------------------------------
/web/.gitignore:
--------------------------------------------------------------------------------
 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files.
 2 | #
 3 | # If you find yourself ignoring temporary files generated by your text editor
 4 | # or operating system, you probably want to add a global ignore instead:
 5 | #   git config --global core.excludesfile '~/.gitignore_global'
 6 | 
 7 | # Ignore bundler config.
 8 | /.bundle
 9 | 
10 | # Ignore the default SQLite database.
11 | /db/*.sqlite3
12 | /db/*.sqlite3-journal
13 | 
14 | # Ignore all logfiles and tempfiles.
15 | /log/*
16 | /tmp/*
17 | !/log/.keep
18 | !/tmp/.keep
19 | 
20 | /node_modules
21 | /yarn-error.log
22 | 
23 | .byebug_history
24 | 


--------------------------------------------------------------------------------
/web/Gemfile:
--------------------------------------------------------------------------------
 1 | source 'https://rubygems.org'
 2 | 
 3 | git_source(:github) do |repo_name|
 4 |   repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
 5 |   "https://github.com/#{repo_name}.git"
 6 | end
 7 | 
 8 | 
 9 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
10 | gem 'rails', '~> 5.1.0'
11 | # Use sqlite3 as the database for Active Record
12 | gem 'sqlite3'
13 | # Use Puma as the app server
14 | gem 'puma', '~> 3.7'
15 | # Use SCSS for stylesheets
16 | gem 'sass-rails', '~> 5.0'
17 | # Use Uglifier as compressor for JavaScript assets
18 | gem 'uglifier', '>= 1.3.0'
19 | # See https://github.com/rails/execjs#readme for more supported runtimes
20 | # gem 'therubyracer', platforms: :ruby
21 | 
22 | # Use CoffeeScript for .coffee assets and views
23 | gem 'coffee-rails', '~> 4.2'
24 | # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
25 | gem 'turbolinks', '~> 5'
26 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
27 | gem 'jbuilder', '~> 2.5'
28 | # Use Redis adapter to run Action Cable in production
29 | # gem 'redis', '~> 3.0'
30 | # Use ActiveModel has_secure_password
31 | # gem 'bcrypt', '~> 3.1.7'
32 | 
33 | # Use Capistrano for deployment
34 | # gem 'capistrano-rails', group: :development
35 | 
36 | gem 'browserify-rails', '~> 4.2'
37 | gem 'jquery-rails', '~> 4.3', '>= 4.3.1'
38 | 
39 | group :development, :test do
40 |   gem 'letter_opener', '~> 1.4', '>= 1.4.1'
41 |   # Call 'byebug' anywhere in the code to stop execution and get a debugger console
42 |   gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
43 |   # Adds support for Capybara system testing and selenium driver
44 |   gem 'capybara', '~> 2.13.0'
45 |   gem 'selenium-webdriver'
46 | end
47 | 
48 | group :development do
49 |   # Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
50 |   gem 'web-console', '>= 3.3.0'
51 |   gem 'listen', '>= 3.0.5', '< 3.2'
52 |   # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
53 |   gem 'spring'
54 |   gem 'spring-watcher-listen', '~> 2.0.0'
55 | end
56 | 
57 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
58 | gem 'tzinfo-data'
59 | 


--------------------------------------------------------------------------------
/web/Gemfile.lock:
--------------------------------------------------------------------------------
  1 | GEM
  2 |   remote: https://rubygems.org/
  3 |   specs:
  4 |     actioncable (5.1.6)
  5 |       actionpack (= 5.1.6)
  6 |       nio4r (~> 2.0)
  7 |       websocket-driver (~> 0.6.1)
  8 |     actionmailer (5.1.6)
  9 |       actionpack (= 5.1.6)
 10 |       actionview (= 5.1.6)
 11 |       activejob (= 5.1.6)
 12 |       mail (~> 2.5, >= 2.5.4)
 13 |       rails-dom-testing (~> 2.0)
 14 |     actionpack (5.1.6)
 15 |       actionview (= 5.1.6)
 16 |       activesupport (= 5.1.6)
 17 |       rack (~> 2.0)
 18 |       rack-test (>= 0.6.3)
 19 |       rails-dom-testing (~> 2.0)
 20 |       rails-html-sanitizer (~> 1.0, >= 1.0.2)
 21 |     actionview (5.1.6)
 22 |       activesupport (= 5.1.6)
 23 |       builder (~> 3.1)
 24 |       erubi (~> 1.4)
 25 |       rails-dom-testing (~> 2.0)
 26 |       rails-html-sanitizer (~> 1.0, >= 1.0.3)
 27 |     activejob (5.1.6)
 28 |       activesupport (= 5.1.6)
 29 |       globalid (>= 0.3.6)
 30 |     activemodel (5.1.6)
 31 |       activesupport (= 5.1.6)
 32 |     activerecord (5.1.6)
 33 |       activemodel (= 5.1.6)
 34 |       activesupport (= 5.1.6)
 35 |       arel (~> 8.0)
 36 |     activesupport (5.1.6)
 37 |       concurrent-ruby (~> 1.0, >= 1.0.2)
 38 |       i18n (>= 0.7, < 2)
 39 |       minitest (~> 5.1)
 40 |       tzinfo (~> 1.1)
 41 |     addressable (2.5.2)
 42 |       public_suffix (>= 2.0.2, < 4.0)
 43 |     arel (8.0.0)
 44 |     bindex (0.5.0)
 45 |     browserify-rails (4.3.0)
 46 |       addressable (>= 2.4.0)
 47 |       railties (>= 4.0.0)
 48 |       sprockets (>= 3.6.0)
 49 |     builder (3.2.3)
 50 |     byebug (10.0.2)
 51 |     capybara (2.13.0)
 52 |       addressable
 53 |       mime-types (>= 1.16)
 54 |       nokogiri (>= 1.3.3)
 55 |       rack (>= 1.0.0)
 56 |       rack-test (>= 0.5.4)
 57 |       xpath (~> 2.0)
 58 |     childprocess (0.9.0)
 59 |       ffi (~> 1.0, >= 1.0.11)
 60 |     coffee-rails (4.2.2)
 61 |       coffee-script (>= 2.2.0)
 62 |       railties (>= 4.0.0)
 63 |     coffee-script (2.4.1)
 64 |       coffee-script-source
 65 |       execjs
 66 |     coffee-script-source (1.12.2)
 67 |     concurrent-ruby (1.0.5)
 68 |     crass (1.0.4)
 69 |     erubi (1.7.1)
 70 |     execjs (2.7.0)
 71 |     ffi (1.9.25)
 72 |     globalid (0.4.1)
 73 |       activesupport (>= 4.2.0)
 74 |     i18n (1.0.1)
 75 |       concurrent-ruby (~> 1.0)
 76 |     jbuilder (2.7.0)
 77 |       activesupport (>= 4.2.0)
 78 |       multi_json (>= 1.2)
 79 |     jquery-rails (4.3.3)
 80 |       rails-dom-testing (>= 1, < 3)
 81 |       railties (>= 4.2.0)
 82 |       thor (>= 0.14, < 2.0)
 83 |     launchy (2.4.3)
 84 |       addressable (~> 2.3)
 85 |     letter_opener (1.6.0)
 86 |       launchy (~> 2.2)
 87 |     listen (3.1.5)
 88 |       rb-fsevent (~> 0.9, >= 0.9.4)
 89 |       rb-inotify (~> 0.9, >= 0.9.7)
 90 |       ruby_dep (~> 1.2)
 91 |     loofah (2.2.2)
 92 |       crass (~> 1.0.2)
 93 |       nokogiri (>= 1.5.9)
 94 |     mail (2.7.0)
 95 |       mini_mime (>= 0.1.1)
 96 |     method_source (0.9.0)
 97 |     mime-types (3.1)
 98 |       mime-types-data (~> 3.2015)
 99 |     mime-types-data (3.2016.0521)
100 |     mini_mime (1.0.0)
101 |     mini_portile2 (2.3.0)
102 |     minitest (5.11.3)
103 |     multi_json (1.13.1)
104 |     nio4r (2.3.1)
105 |     nokogiri (1.8.2)
106 |       mini_portile2 (~> 2.3.0)
107 |     public_suffix (3.0.2)
108 |     puma (3.11.4)
109 |     rack (2.0.5)
110 |     rack-test (1.0.0)
111 |       rack (>= 1.0, < 3)
112 |     rails (5.1.6)
113 |       actioncable (= 5.1.6)
114 |       actionmailer (= 5.1.6)
115 |       actionpack (= 5.1.6)
116 |       actionview (= 5.1.6)
117 |       activejob (= 5.1.6)
118 |       activemodel (= 5.1.6)
119 |       activerecord (= 5.1.6)
120 |       activesupport (= 5.1.6)
121 |       bundler (>= 1.3.0)
122 |       railties (= 5.1.6)
123 |       sprockets-rails (>= 2.0.0)
124 |     rails-dom-testing (2.0.3)
125 |       activesupport (>= 4.2.0)
126 |       nokogiri (>= 1.6)
127 |     rails-html-sanitizer (1.0.4)
128 |       loofah (~> 2.2, >= 2.2.2)
129 |     railties (5.1.6)
130 |       actionpack (= 5.1.6)
131 |       activesupport (= 5.1.6)
132 |       method_source
133 |       rake (>= 0.8.7)
134 |       thor (>= 0.18.1, < 2.0)
135 |     rake (12.3.1)
136 |     rb-fsevent (0.10.3)
137 |     rb-inotify (0.9.10)
138 |       ffi (>= 0.5.0, < 2)
139 |     ruby_dep (1.5.0)
140 |     rubyzip (1.2.1)
141 |     sass (3.5.6)
142 |       sass-listen (~> 4.0.0)
143 |     sass-listen (4.0.0)
144 |       rb-fsevent (~> 0.9, >= 0.9.4)
145 |       rb-inotify (~> 0.9, >= 0.9.7)
146 |     sass-rails (5.0.7)
147 |       railties (>= 4.0.0, < 6)
148 |       sass (~> 3.1)
149 |       sprockets (>= 2.8, < 4.0)
150 |       sprockets-rails (>= 2.0, < 4.0)
151 |       tilt (>= 1.1, < 3)
152 |     selenium-webdriver (3.12.0)
153 |       childprocess (~> 0.5)
154 |       rubyzip (~> 1.2)
155 |     spring (2.0.2)
156 |       activesupport (>= 4.2)
157 |     spring-watcher-listen (2.0.1)
158 |       listen (>= 2.7, < 4.0)
159 |       spring (>= 1.2, < 3.0)
160 |     sprockets (3.7.1)
161 |       concurrent-ruby (~> 1.0)
162 |       rack (> 1, < 3)
163 |     sprockets-rails (3.2.1)
164 |       actionpack (>= 4.0)
165 |       activesupport (>= 4.0)
166 |       sprockets (>= 3.0.0)
167 |     sqlite3 (1.3.13)
168 |     thor (0.20.0)
169 |     thread_safe (0.3.6)
170 |     tilt (2.0.8)
171 |     turbolinks (5.1.1)
172 |       turbolinks-source (~> 5.1)
173 |     turbolinks-source (5.1.0)
174 |     tzinfo (1.2.5)
175 |       thread_safe (~> 0.1)
176 |     tzinfo-data (1.2018.5)
177 |       tzinfo (>= 1.0.0)
178 |     uglifier (4.1.11)
179 |       execjs (>= 0.3.0, < 3)
180 |     web-console (3.6.2)
181 |       actionview (>= 5.0)
182 |       activemodel (>= 5.0)
183 |       bindex (>= 0.4.0)
184 |       railties (>= 5.0)
185 |     websocket-driver (0.6.5)
186 |       websocket-extensions (>= 0.1.0)
187 |     websocket-extensions (0.1.3)
188 |     xpath (2.1.0)
189 |       nokogiri (~> 1.3)
190 | 
191 | PLATFORMS
192 |   ruby
193 | 
194 | DEPENDENCIES
195 |   browserify-rails (~> 4.2)
196 |   byebug
197 |   capybara (~> 2.13.0)
198 |   coffee-rails (~> 4.2)
199 |   jbuilder (~> 2.5)
200 |   jquery-rails (~> 4.3, >= 4.3.1)
201 |   letter_opener (~> 1.4, >= 1.4.1)
202 |   listen (>= 3.0.5, < 3.2)
203 |   puma (~> 3.7)
204 |   rails (~> 5.1.0)
205 |   sass-rails (~> 5.0)
206 |   selenium-webdriver
207 |   spring
208 |   spring-watcher-listen (~> 2.0.0)
209 |   sqlite3
210 |   turbolinks (~> 5)
211 |   tzinfo-data
212 |   uglifier (>= 1.3.0)
213 |   web-console (>= 3.3.0)
214 | 
215 | BUNDLED WITH
216 |    1.16.2
217 | 


--------------------------------------------------------------------------------
/web/README.md:
--------------------------------------------------------------------------------
 1 | # README
 2 | 
 3 | This README would normally document whatever steps are necessary to get the
 4 | application up and running.
 5 | 
 6 | Things you may want to cover:
 7 | 
 8 | * Ruby version
 9 | 
10 | * System dependencies
11 | 
12 | * Configuration
13 | 
14 | * Database creation
15 | 
16 | * Database initialization
17 | 
18 | * How to run the test suite
19 | 
20 | * Services (job queues, cache servers, search engines, etc.)
21 | 
22 | * Deployment instructions
23 | 
24 | * ...
25 | 


--------------------------------------------------------------------------------
/web/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 | 
4 | require_relative 'config/application'
5 | 
6 | Rails.application.load_tasks
7 | 


--------------------------------------------------------------------------------
/web/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../javascripts .js
3 | //= link_directory ../stylesheets .css
4 | 


--------------------------------------------------------------------------------
/web/app/assets/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/app/assets/fonts/FontAwesome.otf


--------------------------------------------------------------------------------
/web/app/assets/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/app/assets/fonts/fontawesome-webfont.eot


--------------------------------------------------------------------------------
/web/app/assets/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/app/assets/fonts/fontawesome-webfont.ttf


--------------------------------------------------------------------------------
/web/app/assets/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/app/assets/fonts/fontawesome-webfont.woff


--------------------------------------------------------------------------------
/web/app/assets/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/app/assets/fonts/fontawesome-webfont.woff2


--------------------------------------------------------------------------------
/web/app/assets/images/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/app/assets/images/.keep


--------------------------------------------------------------------------------
/web/app/assets/images/remix_logo_512x512.svg:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 | 
 6 | 
 7 | 	
13 | 	
25 | 	
38 | 	
42 | 	
47 | 
48 | 
49 | 	
50 | 		
51 | 			
52 | 				
53 | 			
54 | 		
55 | 		
56 | 			
57 | 				
58 | 			
59 | 		
60 | 		
61 | 			
62 | 				
63 | 			
64 | 		
65 | 		
66 | 			
67 | 				
68 | 			
69 | 		
70 | 		
71 | 			
72 | 				
73 | 			
74 | 		
75 | 	
76 | 
77 | 
78 | 


--------------------------------------------------------------------------------
/web/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
 2 | // listed below.
 3 | //
 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
 5 | // vendor/assets/javascripts directory can be referenced here using a relative path.
 6 | //
 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
 8 | // compiled file. JavaScript code in this file should be added after the last require_* statement.
 9 | //
10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11 | // about supported directives.
12 | //
13 | //= require jquery
14 | //= require jquery_ujs
15 | //= require browser-solidity
16 | //= require rails-ujs
17 | //= require turbolinks
18 | 


--------------------------------------------------------------------------------
/web/app/assets/javascripts/cable.js:
--------------------------------------------------------------------------------
 1 | // Action Cable provides the framework to deal with WebSockets in Rails.
 2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command.
 3 | //
 4 | //= require action_cable
 5 | //= require_self
 6 | //= require_tree ./channels
 7 | 
 8 | (function() {
 9 |   this.App || (this.App = {});
10 | 
11 |   App.cable = ActionCable.createConsumer();
12 | 
13 | }).call(this);
14 | 


--------------------------------------------------------------------------------
/web/app/assets/javascripts/channels/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/app/assets/javascripts/channels/.keep


--------------------------------------------------------------------------------
/web/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
 1 | /*
 2 |  * This is a manifest file that'll be compiled into application.css, which will include all the files
 3 |  * listed below.
 4 |  *
 5 |  * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
 6 |  * vendor/assets/stylesheets directory can be referenced here using a relative path.
 7 |  *
 8 |  * You're free to add application-wide styles to this file and they'll appear at the bottom of the
 9 |  * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10 |  * files in this directory. Styles in this file should be added after the last require_* statement.
11 |  * It is generally better to create a new file per style scope.
12 |  *
13 |  *= require_tree .
14 |  *= require_self
15 |  */
16 | 


--------------------------------------------------------------------------------
/web/app/assets/stylesheets/pygment_trac.css:
--------------------------------------------------------------------------------
 1 | .highlight  { background: #ffffff; }
 2 | .highlight .c { color: #999988; font-style: italic } /* Comment */
 3 | .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
 4 | .highlight .k { font-weight: bold } /* Keyword */
 5 | .highlight .o { font-weight: bold } /* Operator */
 6 | .highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */
 7 | .highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */
 8 | .highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */
 9 | .highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
10 | .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
11 | .highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */
12 | .highlight .ge { font-style: italic } /* Generic.Emph */
13 | .highlight .gr { color: #aa0000 } /* Generic.Error */
14 | .highlight .gh { color: #999999 } /* Generic.Heading */
15 | .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
16 | .highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */
17 | .highlight .go { color: #888888 } /* Generic.Output */
18 | .highlight .gp { color: #555555 } /* Generic.Prompt */
19 | .highlight .gs { font-weight: bold } /* Generic.Strong */
20 | .highlight .gu { color: #800080; font-weight: bold; } /* Generic.Subheading */
21 | .highlight .gt { color: #aa0000 } /* Generic.Traceback */
22 | .highlight .kc { font-weight: bold } /* Keyword.Constant */
23 | .highlight .kd { font-weight: bold } /* Keyword.Declaration */
24 | .highlight .kn { font-weight: bold } /* Keyword.Namespace */
25 | .highlight .kp { font-weight: bold } /* Keyword.Pseudo */
26 | .highlight .kr { font-weight: bold } /* Keyword.Reserved */
27 | .highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */
28 | .highlight .m { color: #009999 } /* Literal.Number */
29 | .highlight .s { color: #d14 } /* Literal.String */
30 | .highlight .na { color: #008080 } /* Name.Attribute */
31 | .highlight .nb { color: #0086B3 } /* Name.Builtin */
32 | .highlight .nc { color: #445588; font-weight: bold } /* Name.Class */
33 | .highlight .no { color: #008080 } /* Name.Constant */
34 | .highlight .ni { color: #800080 } /* Name.Entity */
35 | .highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */
36 | .highlight .nf { color: #990000; font-weight: bold } /* Name.Function */
37 | .highlight .nn { color: #555555 } /* Name.Namespace */
38 | .highlight .nt { color: #000080 } /* Name.Tag */
39 | .highlight .nv { color: #008080 } /* Name.Variable */
40 | .highlight .ow { font-weight: bold } /* Operator.Word */
41 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */
42 | .highlight .mf { color: #009999 } /* Literal.Number.Float */
43 | .highlight .mh { color: #009999 } /* Literal.Number.Hex */
44 | .highlight .mi { color: #009999 } /* Literal.Number.Integer */
45 | .highlight .mo { color: #009999 } /* Literal.Number.Oct */
46 | .highlight .sb { color: #d14 } /* Literal.String.Backtick */
47 | .highlight .sc { color: #d14 } /* Literal.String.Char */
48 | .highlight .sd { color: #d14 } /* Literal.String.Doc */
49 | .highlight .s2 { color: #d14 } /* Literal.String.Double */
50 | .highlight .se { color: #d14 } /* Literal.String.Escape */
51 | .highlight .sh { color: #d14 } /* Literal.String.Heredoc */
52 | .highlight .si { color: #d14 } /* Literal.String.Interpol */
53 | .highlight .sx { color: #d14 } /* Literal.String.Other */
54 | .highlight .sr { color: #009926 } /* Literal.String.Regex */
55 | .highlight .s1 { color: #d14 } /* Literal.String.Single */
56 | .highlight .ss { color: #990073 } /* Literal.String.Symbol */
57 | .highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */
58 | .highlight .vc { color: #008080 } /* Name.Variable.Class */
59 | .highlight .vg { color: #008080 } /* Name.Variable.Global */
60 | .highlight .vi { color: #008080 } /* Name.Variable.Instance */
61 | .highlight .il { color: #009999 } /* Literal.Number.Integer.Long */
62 | 
63 | .type-csharp .highlight .k { color: #0000FF }
64 | .type-csharp .highlight .kt { color: #0000FF }
65 | .type-csharp .highlight .nf { color: #000000; font-weight: normal }
66 | .type-csharp .highlight .nc { color: #2B91AF }
67 | .type-csharp .highlight .nn { color: #000000 }
68 | .type-csharp .highlight .s { color: #A31515 }
69 | .type-csharp .highlight .sc { color: #A31515 }
70 | 


--------------------------------------------------------------------------------
/web/app/channels/application_cable/channel.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 |   class Channel < ActionCable::Channel::Base
3 |   end
4 | end
5 | 


--------------------------------------------------------------------------------
/web/app/channels/application_cable/connection.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 |   class Connection < ActionCable::Connection::Base
3 |   end
4 | end
5 | 


--------------------------------------------------------------------------------
/web/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 |   protect_from_forgery with: :exception, unless: -> { request.format.json? }
3 | end
4 | 


--------------------------------------------------------------------------------
/web/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/app/controllers/concerns/.keep


--------------------------------------------------------------------------------
/web/app/controllers/home_controller.rb:
--------------------------------------------------------------------------------
  1 | class HomeController < ApplicationController
  2 |   def index
  3 |   end
  4 | 
  5 |   def analyze
  6 |     @sources = {}
  7 | 
  8 |     if bytecode_exists?
  9 |       @sources[:error] = "Error"
 10 |       return
 11 |     end
 12 | 
 13 |     @sources[:current_file] = oyente_params[:current_file]
 14 |     unless check_params
 15 |       @sources[:error] = "Invalid input"
 16 |     else
 17 |       FileUtils::mkdir_p "tmp/contracts"
 18 |       current_filename = oyente_params[:current_file].split("/")[-1]
 19 |       dir_path = Dir::Tmpname.make_tmpname "tmp/contracts/#{current_filename}", nil
 20 |       sources = eval(oyente_params[:sources])
 21 |       structure_files sources, dir_path
 22 |       file = File.open("#{dir_path}/#{oyente_params[:current_file]}", "r")
 23 |       begin
 24 |         output = oyente_cmd(file.path, "#{options} -a -rp #{dir_path}")
 25 |         @sources = eval(output)
 26 |         UserMailer.analyzer_result_notification(dir_path, @sources, oyente_params[:email]).deliver_later unless oyente_params[:email].nil?
 27 |       rescue
 28 |         @sources[:error] = "Error"
 29 |       ensure
 30 |         file.close
 31 |       end
 32 |     end
 33 |   end
 34 | 
 35 |   def analyze_bytecode
 36 |     @contract = {}
 37 |     unless bytecode_exists?
 38 |       @contract[:error] = "Error"
 39 |       return
 40 |     end
 41 | 
 42 |     unless check_params
 43 |       @contract[:error] = "Invalid input"
 44 |       return
 45 |     end
 46 | 
 47 |     FileUtils::mkdir_p "tmp/contracts"
 48 |     dir_path = "tmp/contracts/bytecode_#{request.remote_ip}"
 49 |     FileUtils::mkdir_p dir_path
 50 |     filepath = Dir::Tmpname.make_tmpname("#{dir_path}/result_", nil)
 51 | 
 52 |     file = File.open("#{filepath}", "w")
 53 |     begin
 54 |       file.write(oyente_params[:bytecode].gsub(/^0x/, ""))
 55 |     rescue
 56 |       @contract[:error] = "Error"
 57 |       return
 58 |     ensure
 59 |       file.close
 60 |     end
 61 | 
 62 |     begin
 63 |       output = oyente_cmd(file.path, "#{options} -b")
 64 |       @contract = eval(output)
 65 |       UserMailer.bytecode_analysis_result(file.path, @contract, oyente_params[:email]).deliver_later unless oyente_params[:email].nil?
 66 |     rescue
 67 |       @contract[:error] = "Error"
 68 |     end
 69 |   end
 70 | 
 71 |   private
 72 |   def structure_files sources, dir_path
 73 |     sources.each do |key, value|
 74 |       if value.key?(:"/content")
 75 |         file = key
 76 |         File.open "#{dir_path}/#{file}", "w" do |f|
 77 |           f.write value[:"/content"]
 78 |         end
 79 |       else
 80 |         dir = key
 81 |         new_dir_path = "#{dir_path}/#{dir}"
 82 |         FileUtils::mkdir_p new_dir_path
 83 |         structure_files value, new_dir_path
 84 |       end
 85 |     end
 86 |   end
 87 | 
 88 |   def oyente_cmd filepath, options
 89 |     return `python #{ENV['OYENTE']}/oyente.py -s #{filepath} -w#{options}`
 90 |   end
 91 | 
 92 |   def bytecode_exists?
 93 |     return !oyente_params[:bytecode].nil?
 94 |   end
 95 | 
 96 |   def oyente_params
 97 |     params.require(:data).permit(:current_file, :sources, :timeout, :global_timeout, :depthlimit, :gaslimit, :looplimit, :email, :bytecode)
 98 |   end
 99 | 
100 |   def check_params
101 |     oyente_params.each do |opt, val|
102 |       unless ["sources", "current_file", "email", "bytecode"].include?(opt)
103 |         return false unless is_number?(val)
104 |       end
105 |     end
106 |     return true
107 |   end
108 | 
109 |   def options
110 |     opts = ""
111 |     oyente_params.each do |opt, val|
112 |       unless ["sources", "current_file", "email", "bytecode"].include?(opt)
113 |         opt = opt.gsub(/_/, '-')
114 |         opts += " --#{opt} #{val}"
115 |       end
116 |     end
117 |     return opts
118 |   end
119 | 
120 |   def is_number? string
121 |     true if Integer(string) && Integer(string) > 0 rescue false
122 |   end
123 | end
124 | 


--------------------------------------------------------------------------------
/web/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 |   include ByteCodeHelper
3 |   include SourceCodeHelper
4 | end
5 | 


--------------------------------------------------------------------------------
/web/app/helpers/byte_code_helper.rb:
--------------------------------------------------------------------------------
 1 | module ByteCodeHelper
 2 |   def bug_exists_for_bytecode? bool
 3 |     if bool
 4 |       return "True".html_safe
 5 |     else
 6 |       return "False".html_safe
 7 |     end
 8 |   end
 9 | 
10 |   def any_vulnerability_for_bytecode? contract
11 |     contract[:vulnerabilities].each do |vul, bool|
12 |       return true if bool
13 |     end
14 |     return false
15 |   end
16 | 
17 |   def border_color_for_bytecode contract
18 |     if any_vulnerability_for_bytecode?(contract)
19 |       "warning-box"
20 |     else
21 |       "safe-box"
22 |     end
23 |   end
24 | end
25 | 


--------------------------------------------------------------------------------
/web/app/helpers/source_code_helper.rb:
--------------------------------------------------------------------------------
 1 | module SourceCodeHelper
 2 |   def any_vulnerability? contract
 3 |     contract[:vulnerabilities].each do |vul, warnings|
 4 |       return true unless warnings.empty?
 5 |     end
 6 |     return false
 7 |   end
 8 | 
 9 |   def vulnerability_names
10 |     return {
11 |       callstack: "Callstack Depth Attack Vulnerability",
12 |       time_dependency: "Timestamp Dependency",
13 |       reentrancy: "Re-Entrancy Vulnerability",
14 |       money_concurrency: "Transaction-Ordering Dependence (TOD)",
15 |       assertion_failure: "Assertion Failure"
16 |     }
17 |   end
18 | 
19 |   def border_color contract
20 |     if any_vulnerability?(contract)
21 |       "warning-box"
22 |     else
23 |       "safe-box"
24 |     end
25 |   end
26 | end
27 | 


--------------------------------------------------------------------------------
/web/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | class ApplicationJob < ActiveJob::Base
2 | end
3 | 


--------------------------------------------------------------------------------
/web/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 |   default from: 'from@example.com'
3 |   layout 'mailer'
4 | end
5 | 


--------------------------------------------------------------------------------
/web/app/mailers/user_mailer.rb:
--------------------------------------------------------------------------------
 1 | class UserMailer < ApplicationMailer
 2 |   helper ApplicationHelper
 3 | 
 4 |   def analyzer_result_notification dir_path, sources, email
 5 |     @sources = sources
 6 | 
 7 |     @sources.each do |source, contracts|
 8 |       attachments[source.to_s] = File.read("#{dir_path}/#{source}")
 9 |     end
10 | 
11 |     mail to: email, subject: "Analysis results by Oyente"
12 |   end
13 | 
14 |   def bytecode_analysis_result filepath, contract, email
15 |     @contract = contract
16 |     attachments["Runtime bytecode"] = File.read(filepath)
17 |     mail to: email, subject: "Bytecode analysis result by Oyente"
18 |   end
19 | end
20 | 


--------------------------------------------------------------------------------
/web/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 |   self.abstract_class = true
3 | end
4 | 


--------------------------------------------------------------------------------
/web/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/app/models/concerns/.keep


--------------------------------------------------------------------------------
/web/app/views/home/analyze.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.sources @sources
2 | 


--------------------------------------------------------------------------------
/web/app/views/home/analyze_bytecode.json.jbuilder:
--------------------------------------------------------------------------------
1 | json.contract @contract
2 | 


--------------------------------------------------------------------------------
/web/app/views/home/index.html.erb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/app/views/home/index.html.erb


--------------------------------------------------------------------------------
/web/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 |   
 4 |     Oyente - Smart contract security analyzer
 5 |     <%= csrf_meta_tags %>
 6 | 
 7 |     <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
 8 |     <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
 9 |   
10 | 
11 |   
12 |     <%= yield %>
13 |   
14 | 
15 | 


--------------------------------------------------------------------------------
/web/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 |   
 4 |     
 5 |     
47 |   
48 | 
49 |   
50 |     
51 | <%= yield %> 52 |
53 | 54 | 55 | -------------------------------------------------------------------------------- /web/app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /web/app/views/user_mailer/analyzer_result_notification.html.erb: -------------------------------------------------------------------------------- 1 |

Oyente analysis result

2 |
3 | <% @sources.each do |source, contracts|%> 4 |
<%= source %>
5 | <% contracts.each do |contract, result| %> 6 |
7 |
<%= source %>:<%= contract %>
8 |
9 |
EVM Code Coverage
10 |
<%= result[:evm_code_coverage] %>%
11 |
12 | <% result[:vulnerabilities].each do |vul, warnings| %> 13 |
14 |
<%= vulnerability_names[vul] %>:
15 | <% if warnings.empty? %> 16 |
False
17 | <% else %> 18 |
True
19 | <% end %> 20 |
21 | <% end %> 22 | <% if any_vulnerability?(result) %> 23 |
24 |
Details
25 | <% result[:vulnerabilities].each do |vul, warnings| %> 26 | <% warnings.each_with_index do |warning, i| %> 27 | <% if warning.is_a?(Array) %> 28 |
Flow <%= i + 1 %>
29 | <% warning.each do |tod_warning| %> 30 |
<%= tod_warning %>
31 | <% end %> 32 | <% else %> 33 |
<%= warning %>
34 | <% end %> 35 | <% end %> 36 | <% end %> 37 | <% end %> 38 |
39 | <% end %> 40 | <% end %> 41 |
42 | 43 | -------------------------------------------------------------------------------- /web/app/views/user_mailer/analyzer_result_notification.text.erb: -------------------------------------------------------------------------------- 1 | User#analyzer_result_notification 2 | 3 | <%= @greeting %>, find me in app/views/user_mailer/analyzer_result_notification.text.erb 4 | -------------------------------------------------------------------------------- /web/app/views/user_mailer/bytecode_analysis_result.html.erb: -------------------------------------------------------------------------------- 1 |

Oyente analysis result

2 |
3 |
Bytecode analysis result
4 |
5 |
6 |
7 |
EVM Code Coverage:
8 |
<%= @contract[:evm_code_coverage] %>%
9 |
10 |
11 |
Callstack Depth Attack Vulnerability:
12 |
<%= bug_exists_for_bytecode?(@contract[:vulnerabilities][:callstack]) %>
13 |
14 |
15 |
Transaction-Ordering Dependence (TOD):
16 |
<%= bug_exists_for_bytecode?(@contract[:vulnerabilities][:money_concurrency]) %>
17 |
18 |
19 |
Timestamp Dependency:
20 |
<%= bug_exists_for_bytecode?(@contract[:vulnerabilities][:time_dependency]) %>
21 |
22 |
23 |
Re-Entrancy Vulnerability:
24 |
<%= bug_exists_for_bytecode?(@contract[:vulnerabilities][:reentrancy]) %>
25 |
26 |
27 |
28 | -------------------------------------------------------------------------------- /web/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /web/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | APP_PATH = File.expand_path('../config/application', __dir__) 8 | require_relative '../config/boot' 9 | require 'rails/commands' 10 | -------------------------------------------------------------------------------- /web/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | require_relative '../config/boot' 8 | require 'rake' 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /web/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # Install JavaScript dependencies if using Yarn 22 | # system('bin/yarn') 23 | 24 | 25 | # puts "\n== Copying sample files ==" 26 | # unless File.exist?('config/database.yml') 27 | # cp 'config/database.yml.sample', 'config/database.yml' 28 | # end 29 | 30 | puts "\n== Preparing database ==" 31 | system! 'bin/rails db:setup' 32 | 33 | puts "\n== Removing old logs and tempfiles ==" 34 | system! 'bin/rails log:clear tmp:clear' 35 | 36 | puts "\n== Restarting application server ==" 37 | system! 'bin/rails restart' 38 | end 39 | -------------------------------------------------------------------------------- /web/bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) 11 | spring = lockfile.specs.detect { |spec| spec.name == "spring" } 12 | if spring 13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 14 | gem 'spring', spring.version 15 | require 'spring/binstub' 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /web/bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /web/bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | VENDOR_PATH = File.expand_path('..', __dir__) 3 | Dir.chdir(VENDOR_PATH) do 4 | begin 5 | exec "yarnpkg #{ARGV.join(" ")}" 6 | rescue Errno::ENOENT 7 | $stderr.puts "Yarn executable was not detected in the system." 8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 9 | exit 1 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /web/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /web/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module Web 10 | class Application < Rails::Application 11 | # Initialize configuration defaults for originally generated Rails version. 12 | config.load_defaults 5.1 13 | 14 | # Settings in config/environments/* take precedence over those specified here. 15 | # Application configuration should go into files in config/initializers 16 | # -- all .rb files in that directory are automatically loaded. 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /web/config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /web/config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | channel_prefix: web_production 11 | -------------------------------------------------------------------------------- /web/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | -------------------------------------------------------------------------------- /web/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /web/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | 19 | config.cache_store = :memory_store 20 | config.public_file_server.headers = { 21 | 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}" 22 | } 23 | else 24 | config.action_controller.perform_caching = false 25 | 26 | config.cache_store = :null_store 27 | end 28 | 29 | # Don't care if the mailer can't send. 30 | config.action_mailer.raise_delivery_errors = false 31 | 32 | config.action_mailer.perform_caching = false 33 | 34 | config.action_mailer.delivery_method = :letter_opener 35 | 36 | # Print deprecation notices to the Rails logger. 37 | config.active_support.deprecation = :log 38 | 39 | # Raise an error on page load if there are pending migrations. 40 | config.active_record.migration_error = :page_load 41 | 42 | # Debug mode disables concatenation and preprocessing of assets. 43 | # This option may cause significant delays in view rendering with a large 44 | # number of complex assets. 45 | config.assets.debug = true 46 | 47 | # Suppress logger output for asset requests. 48 | config.assets.quiet = true 49 | 50 | # Raises error for missing translations 51 | # config.action_view.raise_on_missing_translations = true 52 | 53 | # Use an evented file watcher to asynchronously detect changes in source code, 54 | # routes, locales, etc. This feature depends on the listen gem. 55 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 56 | end 57 | -------------------------------------------------------------------------------- /web/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Attempt to read encrypted secrets from `config/secrets.yml.enc`. 18 | # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or 19 | # `config/secrets.yml.key`. 20 | config.read_encrypted_secrets = true 21 | 22 | # Disable serving static files from the `/public` folder by default since 23 | # Apache or NGINX already handles this. 24 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 25 | 26 | # Compress JavaScripts and CSS. 27 | config.assets.js_compressor = Uglifier.new(harmony: true) 28 | config.assets.css_compressor = :sass 29 | 30 | # Do not fallback to assets pipeline if a precompiled asset is missed. 31 | config.assets.compile = false 32 | 33 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 34 | 35 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 36 | config.action_controller.asset_host = 'https://oyente.melon.network' 37 | 38 | # Specifies the header that your server uses for sending files. 39 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 40 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 41 | 42 | # Mount Action Cable outside main process or domain 43 | # config.action_cable.mount_path = nil 44 | # config.action_cable.url = 'wss://example.com/cable' 45 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 46 | 47 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 48 | # config.force_ssl = true 49 | 50 | # Use the lowest log level to ensure availability of diagnostic information 51 | # when problems arise. 52 | config.log_level = :debug 53 | 54 | # Prepend all log lines with the following tags. 55 | config.log_tags = [ :request_id ] 56 | 57 | # Use a different cache store in production. 58 | # config.cache_store = :mem_cache_store 59 | 60 | # Use a real queuing backend for Active Job (and separate queues per environment) 61 | # config.active_job.queue_adapter = :resque 62 | # config.active_job.queue_name_prefix = "web_#{Rails.env}" 63 | config.action_mailer.perform_caching = false 64 | 65 | config.action_mailer.delivery_method = :smtp 66 | config.action_mailer.smtp_settings = { 67 | address: 'smtp.gmail.com', 68 | port: 587, 69 | domain: 'gmail.com', 70 | user_name: ENV['GMAIL_USERNAME'], 71 | password: ENV['GMAIL_PASSWORD'], 72 | authentication: 'plain', 73 | enable_starttls_auto: true 74 | } 75 | 76 | # Ignore bad email addresses and do not raise email delivery errors. 77 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 78 | # config.action_mailer.raise_delivery_errors = false 79 | 80 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 81 | # the I18n.default_locale when a translation cannot be found). 82 | config.i18n.fallbacks = true 83 | 84 | # Send deprecation notices to registered listeners. 85 | config.active_support.deprecation = :notify 86 | 87 | # Use default logging formatter so that PID and timestamp are not suppressed. 88 | config.log_formatter = ::Logger::Formatter.new 89 | 90 | # Use a different logger for distributed setups. 91 | # require 'syslog/logger' 92 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 93 | 94 | if ENV["RAILS_LOG_TO_STDOUT"].present? 95 | logger = ActiveSupport::Logger.new(STDOUT) 96 | logger.formatter = config.log_formatter 97 | config.logger = ActiveSupport::TaggedLogging.new(logger) 98 | end 99 | 100 | # Do not dump schema after migrations. 101 | config.active_record.dump_schema_after_migration = false 102 | end 103 | -------------------------------------------------------------------------------- /web/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}" 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | config.action_mailer.perform_caching = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | end 43 | -------------------------------------------------------------------------------- /web/config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /web/config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | # Add Yarn node_modules folder to the asset load path. 9 | 10 | # Precompile additional assets. 11 | # application.js, application.css, and all non-JS/CSS in the app/assets 12 | # folder are already added. 13 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 14 | -------------------------------------------------------------------------------- /web/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /web/config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /web/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /web/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /web/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /web/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /web/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at http://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /web/config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers: a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum; this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 11 | # 12 | port ENV.fetch("PORT") { 3000 } 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch("RAILS_ENV") { "development" } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. If you use this option 30 | # you need to make sure to reconnect any threads in the `on_worker_boot` 31 | # block. 32 | # 33 | # preload_app! 34 | 35 | # If you are preloading your application and using Active Record, it's 36 | # recommended that you close any connections to the database before workers 37 | # are forked to prevent connection leakage. 38 | # 39 | # before_fork do 40 | # ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord) 41 | # end 42 | 43 | # The code in the `on_worker_boot` will be called if you are using 44 | # clustered mode by specifying a number of `workers`. After each worker 45 | # process is booted, this block will be run. If you are using the `preload_app!` 46 | # option, you will want to use this block to reconnect to any threads 47 | # or connections that may have been created at application boot, as Ruby 48 | # cannot share connections between processes. 49 | # 50 | # on_worker_boot do 51 | # ActiveRecord::Base.establish_connection if defined?(ActiveRecord) 52 | # end 53 | # 54 | 55 | # Allow puma to be restarted by `rails restart` command. 56 | plugin :tmp_restart 57 | -------------------------------------------------------------------------------- /web/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html 3 | root 'home#index' 4 | resources :home, only: :index do 5 | post 'analyze', on: :collection 6 | post 'analyze_bytecode', on: :collection 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /web/config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rails secret` to generate a secure secret key. 9 | 10 | development: 11 | secret_key_base: 0030b481235dad6f99d9f6134ce9561589cb28f92fad740d2322671c7deff94000f43099bf4568f421a5cd85b410837dbce698e8d62f21688f0a86eab2894d42 12 | 13 | test: 14 | secret_key_base: 7a05abf434e215f99286f0fbfe231efd89971b23f44761f1ba424313b04c7b130fe6758db6b8197ca8954dd99c0a17714bb3e29cefcbcbc13af6cbeba92d5431 15 | 16 | # Do not keep production secrets in the unencrypted secrets file. 17 | # Instead, either read values from the environment. 18 | # Or, use `bin/rails secrets:setup` to configure encrypted secrets 19 | # and move the `production:` environment over there. 20 | 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /web/config/spring.rb: -------------------------------------------------------------------------------- 1 | %w( 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ).each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /web/db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) 7 | # Character.create(name: 'Luke', movie: movies.first) 8 | -------------------------------------------------------------------------------- /web/lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/lib/assets/.keep -------------------------------------------------------------------------------- /web/lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/lib/tasks/.keep -------------------------------------------------------------------------------- /web/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/log/.keep -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies" : 3 | { 4 | "browserify" : ">=15.2.0", 5 | "browserify-incremental" : "3.1.1" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /web/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /web/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /web/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /web/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /web/public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/public/apple-touch-icon.png -------------------------------------------------------------------------------- /web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/public/favicon.ico -------------------------------------------------------------------------------- /web/public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /web/test/application_system_test_case.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase 4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400] 5 | end 6 | -------------------------------------------------------------------------------- /web/test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/test/controllers/.keep -------------------------------------------------------------------------------- /web/test/fixtures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/test/fixtures/.keep -------------------------------------------------------------------------------- /web/test/fixtures/files/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/test/fixtures/files/.keep -------------------------------------------------------------------------------- /web/test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/test/helpers/.keep -------------------------------------------------------------------------------- /web/test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/test/integration/.keep -------------------------------------------------------------------------------- /web/test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/test/mailers/.keep -------------------------------------------------------------------------------- /web/test/mailers/previews/user_mailer_preview.rb: -------------------------------------------------------------------------------- 1 | # Preview all emails at http://localhost:3000/rails/mailers/user_mailer 2 | class UserMailerPreview < ActionMailer::Preview 3 | 4 | # Preview this email at http://localhost:3000/rails/mailers/user_mailer/analyzer_result_notification 5 | def analyzer_result_notification 6 | UserMailer.analyzer_result_notification 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /web/test/mailers/user_mailer_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserMailerTest < ActionMailer::TestCase 4 | test "analyzer_result_notification" do 5 | mail = UserMailer.analyzer_result_notification 6 | assert_equal "Analyzer result notification", mail.subject 7 | assert_equal ["to@example.org"], mail.to 8 | assert_equal ["from@example.com"], mail.from 9 | assert_match "Hi", mail.body.encoded 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /web/test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/test/models/.keep -------------------------------------------------------------------------------- /web/test/system/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/test/system/.keep -------------------------------------------------------------------------------- /web/test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../config/environment', __FILE__) 2 | require 'rails/test_help' 3 | 4 | class ActiveSupport::TestCase 5 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 6 | fixtures :all 7 | 8 | # Add more helper methods to be used by all tests here... 9 | end 10 | -------------------------------------------------------------------------------- /web/vendor/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enzymefinance/oyente/69dc0a905d37ae27e9055ccae930e30752b398fb/web/vendor/.keep --------------------------------------------------------------------------------