-
Notifications
You must be signed in to change notification settings - Fork 686
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Redesign receipt/tx trie generation on block imports #565
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,12 +3,12 @@ | |
ABCMeta, | ||
abstractmethod | ||
) | ||
|
||
import contextlib | ||
import functools | ||
import logging | ||
import rlp | ||
from typing import List # noqa: F401 | ||
|
||
import functools | ||
import rlp | ||
|
||
from eth_utils import ( | ||
keccak, | ||
|
@@ -28,6 +28,7 @@ | |
from evm.rlp.headers import ( | ||
BlockHeader, | ||
) | ||
from evm.rlp.receipts import Receipt # noqa: F401 | ||
from evm.utils.datatypes import ( | ||
Configurable, | ||
) | ||
|
@@ -62,6 +63,7 @@ def __init__(self, header, chaindb): | |
self.chaindb = chaindb | ||
block_class = self.get_block_class() | ||
self.block = block_class.from_header(header=header, chaindb=self.chaindb) | ||
self.receipts = [] # type: List[Receipt] | ||
|
||
# | ||
# Logging | ||
|
@@ -77,14 +79,12 @@ def apply_transaction(self, transaction): | |
""" | ||
Apply the transaction to the vm in the current block. | ||
""" | ||
computation, block, trie_data_dict = self.state.apply_transaction( | ||
computation, block, receipt = self.state.apply_transaction( | ||
transaction, | ||
self.block, | ||
) | ||
self.block = block | ||
|
||
# Persist changed transaction and receipt key-values to self.chaindb. | ||
self.chaindb.persist_trie_data_dict(trie_data_dict) | ||
self.receipts.append(receipt) | ||
|
||
self.clear_journal() | ||
|
||
|
@@ -132,6 +132,7 @@ def execute_bytecode(self, | |
# Mining | ||
# | ||
def import_block(self, block): | ||
self.receipts = [] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This stood out to me. What if instead of tracking this state we instead returned the receipt from It looks like this change goes well with the changes within the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, but should we then pass There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Honestly not sure. Nothing specific in mind for exact implementation but starting with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, we need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That sounds viable. We can continue to iterate on this to I'd recommend just picking something that seems good enough. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We actually need to keep the receipts somewhere in order to generate the receipt trie |
||
self.configure_header( | ||
coinbase=block.header.coinbase, | ||
gas_limit=block.header.gas_limit, | ||
|
@@ -156,6 +157,13 @@ def mine_block(self, *args, **kwargs): | |
Mine the current block. Proxies to self.pack_block method. | ||
""" | ||
block = self.block | ||
tx_root_hash, tx_kv_nodes = make_trie_root_and_nodes(block.transactions) | ||
receipt_root_hash, receipt_kv_nodes = make_trie_root_and_nodes(self.receipts) | ||
self.chaindb.persist_trie_data_dict(tx_kv_nodes) | ||
self.chaindb.persist_trie_data_dict(receipt_kv_nodes) | ||
self.block.header.transaction_root = tx_root_hash | ||
self.block.header.receipt_root = receipt_root_hash | ||
|
||
self.pack_block(block, *args, **kwargs) | ||
|
||
if block.number == 0: | ||
|
@@ -211,7 +219,10 @@ def state_in_temp_block(self): | |
header = self.block.header | ||
temp_block = self.generate_block_from_parent_header_and_coinbase(header, header.coinbase) | ||
prev_hashes = (header.hash, ) + self.previous_hashes | ||
state = self.get_state(self.chaindb, temp_block, prev_hashes) | ||
gas_used = 0 | ||
if self.receipts: | ||
gas_used = self.receipts[-1].gas_used | ||
state = self.get_state(self.chaindb, temp_block, prev_hashes, gas_used) | ||
assert state.gas_used == 0, "There must not be any gas used in a fresh temporary block" | ||
|
||
snapshot = state.snapshot() | ||
|
@@ -266,6 +277,8 @@ def validate_block(self, block): | |
) | ||
) | ||
|
||
# FIXME: make_trie_root_and_nodes() is rather expensive, and we already run that once in | ||
# import_block(), so should refactor some of this code to avoid re-generating it here. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems like we could fix this with some caching on the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I guess #573 might still be useful! |
||
tx_root_hash, _ = make_trie_root_and_nodes(block.transactions) | ||
if tx_root_hash != block.header.transaction_root: | ||
raise ValidationError( | ||
|
@@ -435,7 +448,7 @@ def get_state_class(cls): | |
return cls._state_class | ||
|
||
@classmethod | ||
def get_state(cls, chaindb, block, prev_hashes): | ||
def get_state(cls, chaindb, block, prev_hashes, gas_used): | ||
""" | ||
Return state object | ||
|
||
|
@@ -445,21 +458,24 @@ def get_state(cls, chaindb, block, prev_hashes): | |
:return: state with root defined in block | ||
""" | ||
execution_context = block.header.create_execution_context(prev_hashes) | ||
receipts = block.get_receipts(chaindb) | ||
|
||
return cls.get_state_class()( | ||
chaindb, | ||
execution_context=execution_context, | ||
state_root=block.header.state_root, | ||
receipts=receipts, | ||
gas_used=gas_used, | ||
) | ||
|
||
@property | ||
def state(self): | ||
"""Return current state property | ||
""" | ||
gas_used = 0 | ||
if self.receipts: | ||
gas_used = self.receipts[-1].gas_used | ||
return self.get_state( | ||
chaindb=self.chaindb, | ||
block=self.block, | ||
prev_hashes=self.previous_hashes | ||
prev_hashes=self.previous_hashes, | ||
gas_used=gas_used, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reminder: the reason why
apply_transaction
returnedtrie_data_dict
is for the stateless client to generate witness, which was discussed here: #247 (comment)But since the sharding roadmap was (largely) changed, now the stateless client implementation won't happen in couple months, so I don't have a strong opinion about the change. Hope that we will find a solution to solve in both stateless/non-stateless clients when we reimplement stateless client. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could have the VM incrementally generate the trie data, I suppose? The problem was that it was re-generating it from scratch for every transaction applied
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Except it wouldn't make the VM class stateless, so I guess that wouldn't really buy us anything
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hwwhww we'll continue to move towards stateless clients but for now going to prioritze full sync and then we'll figure out how to make it work with stateless clients also.