vyperlang / titanoboa

a vyper interpreter
https://titanoboa.readthedocs.io
Other
257 stars 51 forks source link

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

Closed pyrex41 closed 5 months ago

pyrex41 commented 1 year ago

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.<locals>.outer.<locals>.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 commented 1 year ago

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.

charles-cooper commented 1 year ago

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.

DanielSchiavini commented 5 months ago

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

DanielSchiavini commented 5 months ago

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