Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/muellerberndt/laser-ethereum
Symbolic virtual machine for Ethereum
https://github.com/muellerberndt/laser-ethereum
Last synced: 3 months ago
JSON representation
Symbolic virtual machine for Ethereum
- Host: GitHub
- URL: https://github.com/muellerberndt/laser-ethereum
- Owner: muellerberndt
- License: mit
- Created: 2017-10-16T08:49:05.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2018-05-29T08:56:57.000Z (over 6 years ago)
- Last Synced: 2024-05-19T21:56:46.528Z (6 months ago)
- Language: Python
- Size: 595 KB
- Stars: 67
- Watchers: 6
- Forks: 20
- Open Issues: 10
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-ethereum-virtual-machine - laser-ethereum
README
# LASER-ethereum
![Master Build Status](https://img.shields.io/circleci/project/github/b-mueller/laser-ethereum/master.svg)
![PyPI version](https://img.shields.io/pypi/v/laser-ethereum.svg)LASER is a symbolic virtual machine (SVM) that runs Ethereum smart contracts. It accurately models most features of the Ethereum virtual machine including inter-contracts calls.
## Installing and Running the Symbolic Virtual Machine
The SVM runs Mythril `Disassembly` objects instead of raw Ethereum bytecode. It is therefore best installed alongside with [Mythril](https://github.com/ConsenSys/mythril). Since mythril only supports python 3, we also requires python 3 here.
```
$ pip3 install mythril
```The `Disassembly` object can be created from Solidity source code or Ethereum bytecode.
```
from laser.ethereum import svm
from mythril.ether.soliditycontract import SolidityContract
contract = SolidityContract("solidity_examples/underflow.sol", "Under")disassembly = contract.disassembly
```It contains a list of instructions in the format `{'address': address, 'opcode': mnemonic, 'argument': argument}`.
```
>>> disassembly.instruction_list
[{'address': 0, 'opcode': 'PUSH1', 'argument': '0x60'}, {'address': 2, 'opcode': 'PUSH1', 'argument': '0x40'}(...)
```To run the code in the symbolic VM it must be mapped to virtual contract accounts. Each account is constructed with an Ethereum address and a `Disassembly` object (the contract code), plus an optional contract name. The LASER constructor expects a mapping of addresses to account objects.
```
address = "0x0000000000000000000000000000000000000000"
account = svm.Account(address, disassembly, "Under")
accounts = {address: account}
```Once initialized, symbolic execution is started with the `sym_exec(entry_address)` method. The `entry_address` argument specifies the contract to be used as the entrypoint (the other mapped contracts are made available for message calls).
```
laser = svm.LaserEVM(accounts)
laser.sym_exec("0x0000000000000000000000000000000000000000")
```## Inspecting Program States
LASER returns an object containing the state space of the smart contract organized as a graph. Each node in the graph represents a basic block of code being executed, and contains a list of global states - one state for each program counter position. Every node also has an associated set of constraints. A list of edges between nodes along with the constraint on each edge is also provided. This can be used to [draw a control flow graph](https://github.com/ConsenSys/mythril#control-flow-graph).
Each node contains a list of state objects, each of which contains the complete global state during that point in execution (PC address).
```
>>> node = laser.nodes[0]
>>> node.states
[, , (...)]
```### The State Object
The state object representes the global system state. It contains an address-to-account mapping, an environment object, and a machine state object. Each account has an associated `storage` dict with a mapping of storage slots to data (this is filled with symbolic and concrete entries during execution).
```
>>> state = node.states[0]
>>> state.accounts['0x0000000000000000000000000000000000000000'].as_dict()
{'nonce': 0, 'code': , 'balance': balance, 'storage': {}}
```The structure of the machine state and execution environment is largely identical to the specification in the [yellow paper](https://github.com/ethereum/yellowpaper).
#### Machine state (μ)
```
The machine state μ is defined as the tuple (g, pc, m, i, s) which are the gas available, the program counter pc ∈ P256, the memory contents, the active number of words in memory (counting continuously from position 0), and the stack contents. The memory contents μm are a series of zeroes of size 256.
```In LASER:
```
>>> state.mstate.as_dict()
{'pc': 0, 'stack': [], 'memory': [], 'memsize': 0, 'gas': 10000000}
```#### Execution Environment (I)
```
- Ia, the address of the account which owns the code that is executing.
- Io,thesenderaddressofthetransactionthatorig- inated this execution.
- Ip, the price of gas in the transaction that origi- nated this execution.
- Id, the byte array that is the input data to this execution; if the execution agent is a transaction, this would be the transaction data.
- Is, the address of the account which caused the code to be executing; if the execution agent is a transaction, this would be the transaction sender.
- Iv, the value, in Wei, passed to this account as part of the same procedure as execution; if the execution agent is a transaction, this would be the transaction value.
- Ib, the byte array that is the machine code to be executed.
- IH , the block header of the present block.
- Ie, the depth of the present message-call or contract-creation (i.e. the number of CALLs or CREATEs being executed at present).
```In LASER:
```
>>> state.environment.as_dict()
{'active_account': , 'sender': caller, 'calldata': [], 'gasprice': gasprice, 'callvalue': callvalue, 'origin': origin, 'calldata_type': }
>>>
```#### Path Constraints
Each node has a list of associated path constraints that can be passed to the Z3 Solver.
```
>>> for c in node.constraints:
... print(z3.simplify(c))
...
ULE(4, calldatasize_Under)
Not(Extract(255, 224, calldata_Under_0) == 404098525)
Not(Extract(255, 224, calldata_Under_0) == 1648476113)
Not(Extract(255, 224, calldata_Under_0) == 1889567281)
Extract(255, 224, calldata_Under_0) == 2736852615
callvalue == 0```
## For developers
You can find how to run tests and generate coverage reports in [README_DEV.md](./README_DEV.md)
## Caveats
- Gas usage is not yet fully simulated
- Native contracts are not yet implemented## What does LASER stand for?
[Light amplification by stimulated emission of radiation](https://en.wikipedia.org/wiki/Laser). The sole reason for calling this software LASER was so there could be a method called `fire_lasers`.