titanoboa icon indicating copy to clipboard operation
titanoboa copied to clipboard

Feature Request -- Export / Save Snapshot to reload boa.env in prior state

Open pyrex41 opened this issue 2 years ago • 2 comments

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

image

ENV 2 Loads state, but fails

Screenshot 2023-01-18 at 2 28 58 PM ``` --------------------------------------------------------------------------- InsufficientDataBytes Traceback (most recent call last) Cell In[4], line 1 ----> 1 MBT_connect.symbol()

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..outer..inner(*args, **kwargs) 43 @functools.wraps(fn) 44 def inner(*args, **kwargs) -> T: # type: ignore ---> 45 return callback(fn(*args, **kwargs))

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

pyrex41 avatar Jan 18 '23 22:01 pyrex41

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.

pyrex41 avatar Jan 19 '23 23:01 pyrex41

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.

charles-cooper avatar May 06 '23 16:05 charles-cooper

I also think that it's easier to run the code again instead of exporting evm data. Can we close this?

DanielSchiavini avatar Jun 03 '24 11:06 DanielSchiavini

Due to the lack of responses I'm closing this issue

DanielSchiavini avatar Jun 26 '24 07:06 DanielSchiavini