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
7
7
from ethereum .pow .ethpow import Miner
8
- from ethereum .messages import apply_transaction
8
+ from ethereum .messages import apply_transaction , apply_message
9
9
from ethereum .common import verify_execution_results , mk_block_from_prevstate , set_execution_results
10
10
from ethereum .meta import make_head_candidate
11
11
from ethereum .abi import ContractTranslator
@@ -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,32 @@ 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
+ sender_addr = privtoaddr (sender )
140
+ result = apply_message (self .state .ephemeral_clone (), sender = sender_addr , to = to , value = value , data = data , gas = startgas )
141
+ if result is None :
142
+ raise TransactionFailed ()
143
+ return result
144
+
120
145
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
- )
146
+ def __init__ (self , alloc = base_alloc , env = None , genesis = None ):
147
+ if genesis :
148
+ self .chain = pow_chain .Chain (genesis , reset_genesis = True )
149
+ else :
150
+ self .chain = pow_chain .Chain (mk_basic_state (alloc , None , get_env (env )), reset_genesis = True )
128
151
self .cs = get_consensus_strategy (self .chain .env .config )
129
152
self .block = mk_block_from_prevstate (self .chain , timestamp = self .chain .state .timestamp + 1 )
130
153
self .head_state = self .chain .state .ephemeral_clone ()
@@ -144,9 +167,16 @@ def tx(self, sender=k0, to=b'\x00' * 20, value=0, data=b'', startgas=STARTGAS, g
144
167
sender_addr = privtoaddr (sender )
145
168
transaction = Transaction (self .head_state .get_nonce (sender_addr ), gasprice , startgas ,
146
169
to , value , data ).sign (sender )
147
- o = self .direct_tx (transaction )
170
+ output = self .direct_tx (transaction )
148
171
self .last_sender = sender
149
- return o
172
+ return output
173
+
174
+ def call (self , sender = k0 , to = b'\x00 ' * 20 , value = 0 , data = b'' , startgas = STARTGAS , gasprice = GASPRICE ):
175
+ sender_addr = privtoaddr (sender )
176
+ result = apply_message (self .head_state .ephemeral_clone (), sender = sender_addr , to = to , value = value , data = data , gas = startgas )
177
+ if result is None :
178
+ raise TransactionFailed ()
179
+ return result
150
180
151
181
def last_gas_used (self , with_tx = False ):
152
182
if len (self .head_state .receipts ) == 1 :
@@ -174,13 +204,17 @@ def mine(self, number_of_blocks=1, coinbase=a0):
174
204
set_execution_results (self .head_state , self .block )
175
205
self .block = Miner (self .block ).mine (rounds = 100 , start_nonce = 0 )
176
206
assert self .chain .add_block (self .block )
177
- assert self . head_state . trie . root_hash == self .chain . state . trie . root_hash
207
+ b = self .block
178
208
for i in range (1 , number_of_blocks ):
179
- b , _ = make_head_candidate (self .chain , timestamp = self .chain .state .timestamp + 14 )
209
+ b , _ = make_head_candidate (self .chain , parent = b , timestamp = self .chain .state .timestamp + 14 , coinbase = coinbase )
180
210
b = Miner (b ).mine (rounds = 100 , start_nonce = 0 )
181
211
assert self .chain .add_block (b )
182
- self .block = mk_block_from_prevstate (self .chain , timestamp = self .chain .state .timestamp + 14 )
183
- self .head_state = self .chain .state .ephemeral_clone ()
212
+ self .change_head (b .header .hash , coinbase )
213
+ return b
214
+
215
+ def change_head (self , parent , coinbase = a0 ):
216
+ self .head_state = self .chain .mk_poststate_of_blockhash (parent ).ephemeral_clone ()
217
+ self .block = mk_block_from_prevstate (self .chain , self .head_state , timestamp = self .chain .state .timestamp , coinbase = coinbase )
184
218
self .cs .initialize (self .head_state , self .block )
185
219
186
220
def snapshot (self ):
@@ -199,7 +233,7 @@ def int_to_0x_hex(v):
199
233
return '0x' + o [1 :]
200
234
else :
201
235
return '0x' + o
202
-
236
+
203
237
204
238
def mk_state_test_prefill (c ):
205
239
env = {
0 commit comments