forked from ethereum/evmlab
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 3d0886f
Showing
11 changed files
with
1,751 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
# EVM lab utilities | ||
|
||
This package contains various tools to interact with the Ethereum virtual machine. | ||
|
||
# Compiler | ||
|
||
The 'compiler' is a tool to build evm binaries, using a pythonic way to construct the programs using assembly. | ||
|
||
Here's an example that tests `ecdsaRecover`: | ||
|
||
```python | ||
|
||
p = compiler.Program() | ||
p.mstore(0 ,0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e) | ||
v = 0x000000000000000000000000000000000000000000000000000000000000001b | ||
p.mstore(32 , v) | ||
p.mstore(64 ,0x723841761d213b60ac1cbf063207cbeba6c2725bcaf7c189e63f13d93fc1dc07) | ||
p.mstore(96 ,0x789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02) | ||
p.call(0xfff,1,0,0,0x80,0x80,0x20) | ||
p.rreturn(140,20) | ||
code = p.bytecode() | ||
``` | ||
|
||
Here's an example of stuffing `JUMPDEST` into a program: | ||
|
||
```python | ||
|
||
p = compiler.Program() | ||
p.jump(0x3) | ||
p.jumpdest() | ||
p.rreturn() | ||
for i in range(0,20000): | ||
p.op(JUMPDEST) | ||
|
||
return p.bytecode() | ||
|
||
``` | ||
|
||
# Gethvm | ||
|
||
The `gethvm` provides some ability to execute the `evm` from geth. | ||
Example: | ||
|
||
```python | ||
|
||
vm = gethvm.VM(evmbin) | ||
output = vm.execute(code = bootstrap, genesis = g_path, json = True) | ||
``` | ||
|
||
# Etherchain | ||
|
||
The `etherchain` package contains an API for interacting with the Etherchain API. | ||
|
||
# Reproduce | ||
|
||
An example app is `reproduce.py` which can reproduce an on-chain transaction as a totally local event, and run it in the `evm`. | ||
|
||
The app takes a `txhash`, and | ||
|
||
1 Fetch the transaction data from an API. | ||
2 Mark (source, destination) as need-to-fetch | ||
3 Fetch balance and nonce at source, add to `genesis` | ||
4 Execute transaction on the `evm` | ||
5 If transaction has any externally reaching ops (BALANCE, EXTCODECOPY, CALL etc), | ||
* Add those accounts as need-to-fetch | ||
6. Go back to 3 until the execution does not result in any more accounts to be fetched. | ||
7. Save the transaction trace and genesis | ||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,282 @@ | ||
STOP = 0x00 # Pops: 0, Pushes: 0, Gas: 0], | ||
ADD = 0x01 # Pops: 2, Pushes: 1, Gas: 3], | ||
MUL = 0x02 # Pops: 2, Pushes: 1, Gas: 5], | ||
SUB = 0x03 # Pops: 2, Pushes: 1, Gas: 3], | ||
DIV = 0x04 # Pops: 2, Pushes: 1, Gas: 5], | ||
SDIV = 0x05 # Pops: 2, Pushes: 1, Gas: 5], | ||
MOD = 0x06 # Pops: 2, Pushes: 1, Gas: 5], | ||
SMOD = 0x07 # Pops: 2, Pushes: 1, Gas: 5], | ||
ADDMOD = 0x08 # Pops: 3, Pushes: 1, Gas: 8], | ||
MULMOD = 0x09 # Pops: 3, Pushes: 1, Gas: 8], | ||
EXP = 0x0a # Pops: 2, Pushes: 1, Gas: 10], | ||
SIGNEXTEND = 0x0b # Pops: 2, Pushes: 1, Gas: 5], | ||
LT = 0x10 # Pops: 2, Pushes: 1, Gas: 3], | ||
GT = 0x11 # Pops: 2, Pushes: 1, Gas: 3], | ||
SLT = 0x12 # Pops: 2, Pushes: 1, Gas: 3], | ||
SGT = 0x13 # Pops: 2, Pushes: 1, Gas: 3], | ||
EQ = 0x14 # Pops: 2, Pushes: 1, Gas: 3], | ||
ISZERO = 0x15 # Pops: 1, Pushes: 1, Gas: 3], | ||
AND = 0x16 # Pops: 2, Pushes: 1, Gas: 3], | ||
OR = 0x17 # Pops: 2, Pushes: 1, Gas: 3], | ||
XOR = 0x18 # Pops: 2, Pushes: 1, Gas: 3], | ||
NOT = 0x19 # Pops: 1, Pushes: 1, Gas: 3], | ||
BYTE = 0x1a # Pops: 2, Pushes: 1, Gas: 3], | ||
SHA3 = 0x20 # Pops: 2, Pushes: 1, Gas: 30], | ||
ADDRESS = 0x30 # Pops: 0, Pushes: 1, Gas: 2], | ||
BALANCE = 0x31 # Pops: 1, Pushes: 1, Gas: 20], | ||
ORIGIN = 0x32 # Pops: 0, Pushes: 1, Gas: 2], | ||
CALLER = 0x33 # Pops: 0, Pushes: 1, Gas: 2], | ||
CALLVALUE = 0x34 # Pops: 0, Pushes: 1, Gas: 2], | ||
CALLDATALOAD = 0x35 # Pops: 1, Pushes: 1, Gas: 3], | ||
CALLDATASIZE = 0x36 # Pops: 0, Pushes: 1, Gas: 2], | ||
CALLDATACOPY = 0x37 # Pops: 3, Pushes: 0, Gas: 3], | ||
CODESIZE = 0x38 # Pops: 0, Pushes: 1, Gas: 2], | ||
CODECOPY = 0x39 # Pops: 3, Pushes: 0, Gas: 3], | ||
GASPRICE = 0x3a # Pops: 0, Pushes: 1, Gas: 2], | ||
EXTCODESIZE = 0x3b # Pops: 1, Pushes: 1, Gas: 20], | ||
EXTCODECOPY = 0x3c # Pops: 4, Pushes: 0, Gas: 20], | ||
BLOCKHASH = 0x40 # Pops: 1, Pushes: 1, Gas: 20], | ||
COINBASE = 0x41 # Pops: 0, Pushes: 1, Gas: 2], | ||
TIMESTAMP = 0x42 # Pops: 0, Pushes: 1, Gas: 2], | ||
NUMBER = 0x43 # Pops: 0, Pushes: 1, Gas: 2], | ||
DIFFICULTY = 0x44 # Pops: 0, Pushes: 1, Gas: 2], | ||
GASLIMIT = 0x45 # Pops: 0, Pushes: 1, Gas: 2], | ||
POP = 0x50 # Pops: 1, Pushes: 0, Gas: 2], | ||
MLOAD = 0x51 # Pops: 1, Pushes: 1, Gas: 3], | ||
MSTORE = 0x52 # Pops: 2, Pushes: 0, Gas: 3], | ||
MSTORE8 = 0x53 # Pops: 2, Pushes: 0, Gas: 3], | ||
SLOAD = 0x54 # Pops: 1, Pushes: 1, Gas: 50], | ||
SSTORE = 0x55 # Pops: 2, Pushes: 0, Gas: 0], | ||
JUMP = 0x56 # Pops: 1, Pushes: 0, Gas: 8], | ||
JUMPI = 0x57 # Pops: 2, Pushes: 0, Gas: 10], | ||
PC = 0x58 # Pops: 0, Pushes: 1, Gas: 2], | ||
MSIZE = 0x59 # Pops: 0, Pushes: 1, Gas: 2], | ||
GAS = 0x5a # Pops: 0, Pushes: 1, Gas: 2], | ||
JUMPDEST = 0x5b # Pops: 0, Pushes: 0, Gas: 1], | ||
LOG0 = 0xa0 # Pops: 2, Pushes: 0, Gas: 375], | ||
LOG1 = 0xa1 # Pops: 3, Pushes: 0, Gas: 750], | ||
LOG2 = 0xa2 # Pops: 4, Pushes: 0, Gas: 1125], | ||
LOG3 = 0xa3 # Pops: 5, Pushes: 0, Gas: 1500], | ||
LOG4 = 0xa4 # Pops: 6, Pushes: 0, Gas: 1875], | ||
CREATE = 0xf0 # Pops: 3, Pushes: 1, Gas: 32000], | ||
CALL = 0xf1 # Pops: 7, Pushes: 1, Gas: 40], | ||
CALLCODE = 0xf2 # Pops: 7, Pushes: 1, Gas: 40], | ||
RETURN = 0xf3 # Pops: 2, Pushes: 0, Gas: 0], | ||
DELEGATECALL = 0xf4 # Pops: 6, Pushes: 0, Gas: 40], | ||
SUICIDE = 0xff # Pops: 1, Pushes: 0, Gas: 0], | ||
|
||
PUSH1 =0x60 | ||
PUSH32 =0x7f | ||
|
||
DUP1 = 0x80 | ||
DUP2 = 0x81 | ||
DUP3 = 0x82 | ||
DUP4 = 0x83 | ||
DUP5 = 0x84 | ||
DUP6 = 0x85 | ||
DUP7 = 0x86 | ||
DUP8 = 0x87 | ||
DUP9 = 0x88 | ||
DUP10 = 0x89 | ||
DUP11 = 0x8a | ||
DUP12 = 0x8b | ||
DUP13 = 0x8c | ||
DUP14 = 0x8d | ||
DUP15 = 0x8e | ||
DUP16 = 0x8f | ||
|
||
SWAP1 = 0x90 | ||
SWAP2 = 0x91 | ||
SWAP3 = 0x92 | ||
SWAP4 = 0x93 | ||
SWAP5 = 0x94 | ||
SWAP6 = 0x95 | ||
SWAP7 = 0x96 | ||
SWAP8 = 0x97 | ||
SWAP9 = 0x98 | ||
SWAP10 = 0x99 | ||
SWAP11 = 0x9a | ||
SWAP12 = 0x9b | ||
SWAP13 = 0x9c | ||
SWAP14 = 0x9d | ||
SWAP15 = 0x9e | ||
SWAP16 = 0x9f | ||
|
||
|
||
def bytecode(value): | ||
|
||
if type(value) == str or \ | ||
type(value) == unicode: | ||
if value[:2] == "0x": | ||
value = value[2:] | ||
|
||
if type(value) == float or \ | ||
type(value) == int or \ | ||
type(value) == long: | ||
value = format(value, '02x') | ||
|
||
value = ('0' * (len(value) % 2)) + value | ||
return value | ||
|
||
class Program(): | ||
|
||
def __init__(self): | ||
self.compiled = [] | ||
self.ops = [] | ||
self.mstore= lambda index,value: self.push(value).push(index).op(MSTORE) | ||
self.add = lambda x,y: self.push(y).push(x).op(ADD) | ||
self.sub = lambda x,y: self.push(y).push(x).op(SUB) | ||
self.mul = lambda x,y: self.push(y).push(x).op(MUL) | ||
self.div = lambda x,y: self.push(y).push(x).op(DIV) | ||
self.sdiv = lambda x,y: self.push(y).push(x).op(SDIV) | ||
self.mod = lambda x,y: self.push(y).push(x).op(MOD) | ||
self.smod = lambda x,y: self.push(y).push(x).op(SMOD) | ||
self.exp = lambda x,y: self.push(y).push(x).op(EXP) | ||
|
||
self.create = lambda v,p,s:self.push(s).push(p).push(v).op(CREATE) | ||
self.codecopy = lambda t,f,s:self.push(s).push(f).push(t).op(CODECOPY) | ||
self.extcodecopy = lambda a,t,f,s:self.push(s).push(f).push(t).push(a).op(CODECOPY) | ||
self.selfdestruct = lambda a: self.push(a).op(SUICIDE) | ||
|
||
#log without topics and data mem[p..(p+s)) | ||
self.log0 = lambda p, s: self.push(s).push(p).op(LOG0) | ||
#log with topic t1 and data mem[p..(p+s)) | ||
self.log1 = lambda p, s, t1: self.push(t1).push(s).push(p).op(LOG1) | ||
#log with topics t1, t2 and data mem[p..(p+s)) | ||
self.log2 = lambda p, s, t1, t2: self.push(t2).push(t1).push(s).push(p).op(LOG3) | ||
#log with topics t1, t2, t3 and data mem[p..(p+s)) | ||
self.log3 = lambda p, s, t1, t2, t3: self.push(t3).push(t2).push(t1).push(s).push(p).op(LOG3) | ||
#log with topics t1, t2, t3, t4 and data mem[p..(p+s)) | ||
self.log4 = lambda p, s, t1, t2, t3, t4: self.push(t4).push(t3).push(t2).push(t1).push(s).push(p).op(LOG3) | ||
self.jump = lambda label : self.push(label).op(JUMP) | ||
self.jumpi = lambda label,cond : self.push(cond).push(label).op(JUMPI) | ||
|
||
def _add(self, x): | ||
if x == None: | ||
return self | ||
|
||
if type(x) == str: | ||
self.compiled.append(x) | ||
else: | ||
self.compiled.append(bytecode(x)) | ||
|
||
return self | ||
|
||
def extend(self,program): | ||
self.compiled.extend(program.compiled) | ||
|
||
def _addOp(self,op,v = None): | ||
self._add(op) | ||
self._add(v) | ||
return self | ||
|
||
def op(self,x): | ||
self._add(x) | ||
return self | ||
|
||
def push(self,value): | ||
value = bytecode(value) | ||
length = len(value) / 2 | ||
|
||
assert length <=32 | ||
|
||
self._addOp(PUSH1+(length-1), bytecode(value)); | ||
return self | ||
|
||
def call(self,gas ,address,value = 0,instart = 0, insize = 0, out = 0, outsize = 0): | ||
self.push(outsize) | ||
self.push(out) | ||
self.push(insize) | ||
self.push(instart) | ||
self.push(value) | ||
self.push(address) | ||
if gas is not None: | ||
self.push(gas) | ||
else: | ||
self.op(GAS) | ||
self._addOp(CALL) | ||
return self | ||
|
||
def callcode(self,gas ,address,value = 0,instart = 0, insize = 0, out = 0, outsize = 0): | ||
self.push(outsize) | ||
self.push(out) | ||
self.push(insize) | ||
self.push(instart) | ||
self.push(value) | ||
self.push(address) | ||
self.push(gas) | ||
self._addOp(CALLCODE) | ||
return self | ||
|
||
def delegatecall(self,gas ,address,instart = 0, insize = 0, out = 0, outsize = 0): | ||
self.push(outsize) | ||
self.push(out) | ||
self.push(insize) | ||
self.push(instart) | ||
self.push(address) | ||
self.push(gas) | ||
self._addOp(DELEGATECALL) | ||
return self | ||
|
||
|
||
def rreturn(self, memStart=0, memSize=0): | ||
self.push(memSize) | ||
self.push(memStart) | ||
self._addOp(RETURN) | ||
return self | ||
|
||
def bytecode(self): | ||
return "".join(self.compiled) | ||
|
||
def label(self): | ||
return len(self.bytecode()) / 2 | ||
|
||
def jumpdest(self): | ||
here = self.label() | ||
self.op(JUMPDEST) | ||
return here | ||
|
||
|
||
def __str__(self): | ||
return ",".join(self.compiled) | ||
|
||
# def add(self, x,y): | ||
# self.push(x).push(y)._add(ADD) | ||
|
||
def test(): | ||
p = Program() | ||
p.mstore(0 ,0x38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e) | ||
#Stuff all kinds of stuff into the 'v'-value | ||
p.mstore(32 ,0xdeadbeef00000000cafebabe00000000cafebabe0000deadbeef00cadebabe1b) | ||
p.mstore(64 ,0x723841761d213b60ac1cbf063207cbeba6c2725bcaf7c189e63f13d93fc1dc07) | ||
p.mstore(96 ,0x789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02) | ||
p.call(0xfff,1,0,0,0x80,0x80,0x20) | ||
p.rreturn(140,20) | ||
|
||
old = "7f38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e6000527fdeadbeef00000000cafebabe00000000cafebabe0000deadbeef00cadebabe1b6020527f723841761d213b60ac1cbf063207cbeba6c2725bcaf7c189e63f13d93fc1dc076040527f789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02606052602060806080600060006001610ffff16014608cf3" | ||
new = p.bytecode() | ||
|
||
if new != old: | ||
print old | ||
print "DIFF!" | ||
else: | ||
print "OK" | ||
|
||
import subprocess | ||
print subprocess.check_output(["/home/martin/tmp/go-ethereum/build/bin/evm","--debug", "--code", new]) | ||
|
||
if __name__ == '__main__': | ||
test() | ||
|
||
|
||
|
||
|
||
#Insert evm executable here... | ||
|
||
# Correct address for ecdsa_recover with hash/rsv-values above, but v 0x1b: | ||
# 0x629defec4044715f5f43403bb88a7074a7a28f67 | ||
|
||
# We get that whatever we put into the upper bytes of 'v', any byte above the lowest two: | ||
|
||
#0x629defec4044715f5f43403bb88a7074a7a28f67 | ||
#0x629defec4044715f5f43403bb88a7074a7a28f67 |
Oops, something went wrong.