titanoboa
titanoboa copied to clipboard
Feature Request -- Export / Save Snapshot to reload boa.env in prior state
Want to be able to export state of boa.env in a file that can be loaded by a different instantiation of boa.env. Currently working on this as follows:
under class VMPatcher: ~87
def export_state(self):
snap = {}
for s, _ in self._patchables:
for attr in s:
snap[attr] = getattr(self, attr)
snap['prev_hashes'] = list(snap['prev_hashes']) # can't pickle a generator
return snap
def load_state(self, snap: dict):
snap['prev_hashes'] = (x for x in snap['prev_hashes']) # makes it a generator but not the same class as before -- issue?
for s, _ in self._patchables:
for attr in s:
setattr(self, attr, snap[attr])
Under class ENV: ~399
def export_state(self, file_name: str = "boa_env_state.pickle"):
snap = self.vm.patch.export_state()
out_file = "{}/{}".format(os.getcwd(), file_name)
with open(out_file, "wb") as file:
pickle.dump(snap, file)
def load_state(self, file_name: str):
with open(file_name, "rb") as file:
snap = pickle.load(file)
self.vm.patch.load_state(snap)
It all seems to work but the new env doesn't have the same compiled bytecode when load_partial(contract_source_code.vy).at(contract_address_in_first_env) is invoked:
warnings.warn(f"casted bytecode does not match compiled bytecode at {self}")
.
Obv. the second env isn't loading the bytecode from the first. I'm not familiar enough with py-evm to understand why or where the issue is -- welcome to suggestions.
ENV 1 Saves state
ENV 2 Loads state, but fails

File ~/Library/Caches/pypoetry/virtualenvs/peg-boa-livPFXpy-py3.10/lib/python3.10/site-packages/boa/vyper/contract.py:895, in VyperFunction.call(self, value, gas, sender, *args, **kwargs) 885 computation = self.env.execute_code( 886 to_address=self.contract.address, 887 bytecode=self._bytecode, (...) 891 gas=gas, 892 ) 894 typ = self.fn_signature.return_type --> 895 return self.contract.marshal_to_python(computation, typ)
File ~/Library/Caches/pypoetry/virtualenvs/peg-boa-livPFXpy-py3.10/lib/python3.10/site-packages/boa/vyper/contract.py:627, in VyperContract.marshal_to_python(self, computation, vyper_typ) 624 return None 626 return_typ = calculate_type_for_external_return(vyper_typ) --> 627 ret = abi_decode(return_typ.abi_type.selector_name(), computation.output) 629 # unwrap the tuple if needed 630 if not isinstance(vyper_typ, TupleType):
File ~/Library/Caches/pypoetry/virtualenvs/peg-boa-livPFXpy-py3.10/lib/python3.10/site-packages/eth_abi/codec.py:180, in ABIDecoder.decode_single(self, typ, data) 177 decoder = self._registry.get_decoder(typ) 178 stream = self.stream_class(data) --> 180 return decoder(stream)
File ~/Library/Caches/pypoetry/virtualenvs/peg-boa-livPFXpy-py3.10/lib/python3.10/site-packages/eth_abi/decoding.py:129, in BaseDecoder.call(self, stream) 128 def call(self, stream: ContextFramesBytesIO) -> Any: --> 129 return self.decode(stream)
File ~/Library/Caches/pypoetry/virtualenvs/peg-boa-livPFXpy-py3.10/lib/python3.10/site-packages/eth_utils/functional.py:45, in apply_to_return_value.
File ~/Library/Caches/pypoetry/virtualenvs/peg-boa-livPFXpy-py3.10/lib/python3.10/site-packages/eth_abi/decoding.py:175, in TupleDecoder.decode(self, stream) 172 @to_tuple 173 def decode(self, stream): 174 for decoder in self.decoders: --> 175 yield decoder(stream)
File ~/Library/Caches/pypoetry/virtualenvs/peg-boa-livPFXpy-py3.10/lib/python3.10/site-packages/eth_abi/decoding.py:129, in BaseDecoder.call(self, stream) 128 def call(self, stream: ContextFramesBytesIO) -> Any: --> 129 return self.decode(stream)
File ~/Library/Caches/pypoetry/virtualenvs/peg-boa-livPFXpy-py3.10/lib/python3.10/site-packages/eth_abi/decoding.py:144, in HeadTailDecoder.decode(self, stream) 143 def decode(self, stream): --> 144 start_pos = decode_uint_256(stream) 146 stream.push_frame(start_pos) 147 value = self.tail_decoder(stream)
File ~/Library/Caches/pypoetry/virtualenvs/peg-boa-livPFXpy-py3.10/lib/python3.10/site-packages/eth_abi/decoding.py:129, in BaseDecoder.call(self, stream) 128 def call(self, stream: ContextFramesBytesIO) -> Any: --> 129 return self.decode(stream)
File ~/Library/Caches/pypoetry/virtualenvs/peg-boa-livPFXpy-py3.10/lib/python3.10/site-packages/eth_abi/decoding.py:199, in SingleDecoder.decode(self, stream) 198 def decode(self, stream): --> 199 raw_data = self.read_data_from_stream(stream) 200 data, padding_bytes = self.split_data_and_padding(raw_data) 201 value = self.decoder_fn(data)
File ~/Library/Caches/pypoetry/virtualenvs/peg-boa-livPFXpy-py3.10/lib/python3.10/site-packages/eth_abi/decoding.py:306, in FixedByteSizeDecoder.read_data_from_stream(self, stream) 303 data = stream.read(self.data_byte_size) 305 if len(data) != self.data_byte_size: --> 306 raise InsufficientDataBytes( 307 "Tried to read {0} bytes. Only got {1} bytes".format( 308 self.data_byte_size, 309 len(data), 310 ) 311 ) 313 return data
InsufficientDataBytes: Tried to read 32 bytes. Only got 0 bytes
Looks like boa.env.vm.build_state(atomic_db, headers, chain_context, prev_hashes)
should do what i was trying to do here, but it still doesn't have they bytecode deployed at the same address across different environments.
one thing i need to be convinced of before implementing this is - that there is a use case where there is actually a significant performance difference for loading state from a file rather than just recreating it on the fly.
I also think that it's easier to run the code again instead of exporting evm data. Can we close this?
Due to the lack of responses I'm closing this issue