1
1
from ethereum .utils import sha3 , privtoaddr , int_to_addr , to_string , big_endian_to_int , checksum_encode , int_to_big_endian , encode_hex
2
2
from ethereum .genesis_helpers import mk_basic_state
3
- from ethereum .pow import chain
3
+ from ethereum .pow import chain as pow_chain
4
4
from ethereum .transactions import Transaction
5
5
from ethereum .consensus_strategy import get_consensus_strategy
6
6
from ethereum .config import config_homestead , config_tangerine , config_spurious , config_metropolis , default_config , Env
@@ -63,9 +63,10 @@ class TransactionFailed(Exception):
63
63
config_string = ':info'
64
64
# configure_logging(config_string=config_string)
65
65
66
+
66
67
class ABIContract (object ): # pylint: disable=too-few-public-methods
67
68
68
- def __init__ (self , _chain , _abi , address ):
69
+ def __init__ (self , _tester , _abi , address ):
69
70
self .address = address
70
71
71
72
if isinstance (_abi , ContractTranslator ):
@@ -76,25 +77,29 @@ def __init__(self, _chain, _abi, address):
76
77
self .translator = abi_translator
77
78
78
79
for function_name in self .translator .function_data :
79
- function = self .method_factory (_chain , function_name )
80
+ if self .translator .function_data [function_name ]['is_constant' ]:
81
+ function = self .method_factory (_tester .call , function_name )
82
+ else :
83
+ function = self .method_factory (_tester .tx , function_name )
80
84
method = types .MethodType (function , self )
81
85
setattr (self , function_name , method )
82
86
83
87
@staticmethod
84
- def method_factory (test_chain , function_name ):
88
+ def method_factory (tx_or_call , function_name ):
85
89
""" Return a proxy for calling a contract method with automatic encoding of
86
90
argument and decoding of results.
87
91
"""
88
92
89
93
def kall (self , * args , ** kwargs ):
90
94
key = kwargs .get ('sender' , k0 )
91
95
92
- result = test_chain . tx ( # pylint: disable=protected-access
96
+ result = tx_or_call ( # pylint: disable=protected-access
93
97
sender = key ,
94
98
to = self .address ,
95
99
value = kwargs .get ('value' , 0 ),
96
100
data = self .translator .encode (function_name , args ),
97
- startgas = kwargs .get ('startgas' , STARTGAS )
101
+ startgas = kwargs .get ('startgas' , STARTGAS ),
102
+ gasprice = kwargs .get ('gasprice' , GASPRICE )
98
103
)
99
104
100
105
if result is False :
@@ -117,14 +122,36 @@ def get_env(env):
117
122
return env if isinstance (env , Env ) else Env (config = d [env ])
118
123
119
124
125
+ class State (object ):
126
+ def __init__ (self , genesis ):
127
+ self .state = genesis
128
+
129
+ def tx (self , sender = k0 , to = b'\x00 ' * 20 , value = 0 , data = b'' , startgas = STARTGAS , gasprice = GASPRICE ):
130
+ sender_addr = privtoaddr (sender )
131
+ transaction = Transaction (self .state .get_nonce (sender_addr ), gasprice , startgas ,
132
+ to , value , data ).sign (sender )
133
+ success , output = apply_transaction (self .state , transaction )
134
+ if not success :
135
+ raise TransactionFailed ()
136
+ return output
137
+
138
+ def call (self , sender = k0 , to = b'\x00 ' * 20 , value = 0 , data = b'' , startgas = STARTGAS , gasprice = GASPRICE ):
139
+ state = self .state
140
+ self .state = self .state .ephemeral_clone ()
141
+ try :
142
+ output = self .tx (sender , to , value , data , startgas , gasprice )
143
+ self .state = state
144
+ return output
145
+ except Exception as e :
146
+ self .state = state
147
+ raise e
148
+
120
149
class Chain (object ):
121
- def __init__ (self , alloc = None , env = None ):
122
- self .chain = chain .Chain (
123
- genesis = mk_basic_state (base_alloc if alloc is None else alloc ,
124
- None ,
125
- get_env (env )),
126
- reset_genesis = True
127
- )
150
+ def __init__ (self , alloc = base_alloc , env = None , genesis = None ):
151
+ if genesis :
152
+ self .chain = pow_chain .Chain (genesis , reset_genesis = True )
153
+ else :
154
+ self .chain = pow_chain .Chain (mk_basic_state (alloc , None , get_env (env )), reset_genesis = True )
128
155
self .cs = get_consensus_strategy (self .chain .env .config )
129
156
self .block = mk_block_from_prevstate (self .chain , timestamp = self .chain .state .timestamp + 1 )
130
157
self .head_state = self .chain .state .ephemeral_clone ()
@@ -144,9 +171,19 @@ def tx(self, sender=k0, to=b'\x00' * 20, value=0, data=b'', startgas=STARTGAS, g
144
171
sender_addr = privtoaddr (sender )
145
172
transaction = Transaction (self .head_state .get_nonce (sender_addr ), gasprice , startgas ,
146
173
to , value , data ).sign (sender )
147
- o = self .direct_tx (transaction )
174
+ output = self .direct_tx (transaction )
148
175
self .last_sender = sender
149
- return o
176
+ return output
177
+
178
+ def call (self , sender = k0 , to = b'\x00 ' * 20 , value = 0 , data = b'' , startgas = STARTGAS , gasprice = GASPRICE ):
179
+ snapshot = self .snapshot ()
180
+ try :
181
+ output = self .tx (sender , to , value , data , startgas , gasprice )
182
+ self .revert (snapshot )
183
+ return output
184
+ except Exception as e :
185
+ self .revert (snapshot )
186
+ raise e
150
187
151
188
def contract (self , sourcecode , args = [], sender = k0 , value = 0 , language = 'evm' , startgas = STARTGAS , gasprice = GASPRICE ):
152
189
if language == 'evm' :
@@ -165,13 +202,17 @@ def mine(self, number_of_blocks=1, coinbase=a0):
165
202
set_execution_results (self .head_state , self .block )
166
203
self .block = Miner (self .block ).mine (rounds = 100 , start_nonce = 0 )
167
204
assert self .chain .add_block (self .block )
168
- assert self . head_state . trie . root_hash == self .chain . state . trie . root_hash
205
+ b = self .block
169
206
for i in range (1 , number_of_blocks ):
170
- b , _ = make_head_candidate (self .chain , timestamp = self .chain .state .timestamp + 14 )
207
+ b , _ = make_head_candidate (self .chain , parent = b , timestamp = self .chain .state .timestamp + 14 , coinbase = coinbase )
171
208
b = Miner (b ).mine (rounds = 100 , start_nonce = 0 )
172
209
assert self .chain .add_block (b )
173
- self .block = mk_block_from_prevstate (self .chain , timestamp = self .chain .state .timestamp + 14 )
174
- self .head_state = self .chain .state .ephemeral_clone ()
210
+ self .change_head (b .header .hash , coinbase )
211
+ return b
212
+
213
+ def change_head (self , parent , coinbase = a0 ):
214
+ self .head_state = self .chain .mk_poststate_of_blockhash (parent ).ephemeral_clone ()
215
+ self .block = mk_block_from_prevstate (self .chain , self .head_state , timestamp = self .chain .state .timestamp , coinbase = coinbase )
175
216
self .cs .initialize (self .head_state , self .block )
176
217
177
218
def snapshot (self ):
@@ -190,7 +231,7 @@ def int_to_0x_hex(v):
190
231
return '0x' + o [1 :]
191
232
else :
192
233
return '0x' + o
193
-
234
+
194
235
195
236
def mk_state_test_prefill (c ):
196
237
env = {
0 commit comments