-
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
Incremental block construction #598
Incremental block construction #598
Conversation
16f372f
to
cda3fee
Compare
@@ -79,16 +78,14 @@ def apply_transaction(self, transaction): | |||
""" | |||
Apply the transaction to the vm in the current block. | |||
""" | |||
computation, block, receipt = self.state.apply_transaction( | |||
block, receipt, computation = self.state.apply_transaction( |
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.
FYI: this ordering is structured from highest to lowest level elements.
@@ -14,7 +14,8 @@ def vm_logger(): | |||
|
|||
# level = TRACE_LEVEL_NUM | |||
# level = logging.DEBUG | |||
level = logging.INFO | |||
# level = logging.INFO | |||
level = logging.ERROR |
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.
FYI: This came from me ending up with a terminal full of INFO
level logging messages long enough to push the actual test failure off the history of my terminal.
assert chain.estimate_gas(next_pending_tx, chain.header) == 722760 | ||
|
||
|
||
@pytest.mark.xfail(reason="modification to initial allocation made the block fixture invalid") |
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.
I broke these tests by modifying the genesis state for the fixture (by increasing the initial ether allotment to the funded_address
. I decided not to fix it because I'll be completely reworking this test suite as part of #599
tests/core/conftest.py
Outdated
|
||
@pytest.fixture | ||
def funded_address_initial_balance(): | ||
return to_wei(1000, 'ether') |
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.
FYI: This is the change that broke the block import fixture
c095a4f
to
bd4ba13
Compare
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.
I think this change highlights the need for #599. Ultimately most of these comments here are forward-looking to that change, just pointing out places that I think need love.
Apart from style tweaks, the main thing that I think could improve this PR is a couple extra assertions in one of the tests. (commented inline)
|
||
def test_building_block_incrementally_with_single_transaction(chain, | ||
funded_address, | ||
funded_address_private_key): |
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.
style nitpick
def long_signature(
thing_a,
thing_b,
thing_c=None,
thing_d=None):
pass
_, _, computation = chain.apply_transaction(tx) | ||
assert computation.is_success | ||
|
||
mined_block = chain.mine_block() |
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.
Before mining, there might be other things we want to assert. Perhaps:
- that the transaction is present in the
pending
block - that it is not present in the
latest
block
mined_block = chain.mine_block() | ||
assert len(mined_block.transactions) == 3 | ||
|
||
for left, right in zip(txns, mined_block.transactions): |
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.
Maybe:
-left, right
+expected, actual
evm/chains/base.py
Outdated
""" | ||
vm = self.get_vm() | ||
block, receipt, computation = vm.apply_transaction(transaction) | ||
receipts = block.get_receipts(self.chaindb) + [receipt] |
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.
This feels a little weird. We're getting back the block after the transaction, but it doesn't have the receipts that were also produced as a part of the transaction and some of the final header fields.
I think I get where you're eventually going: that we shouldn't return a block at all, but the building pieces that are needed to form the block. It is just hard to track which of those changes are still going in the block and which aren't. Maybe that's fine for this PR, where you're just trying to enable this one feature of incremental building, and come back for a reorg later.
A TransactionDiff
is continuing to look like a nice encapsulation, which would include all the things that change in the block, plus the receipt, and the computation.
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.
Agreed, though I'm going to push fixing this out a bit as I'm about to make a number of incremental changes. Good spot as I hadn't quite connected that logic (should not return a block unless the block is actually complete).
evm/vm/base.py
Outdated
transaction, | ||
self.block, | ||
) | ||
self.block = block | ||
self.receipts.append(receipt) | ||
self.chaindb.persist() |
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.
Yeah, it's gross to have db writes sprinkled about. But... I think we'll have to come back to that one in a future PR.
self.apply_transaction(transaction) | ||
for transaction | ||
in block.transactions | ||
] |
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.
Something about generating a list this way makes it less obvious that apply_transaction()
is also mutating the state in the VM. I guess if we become super consistent about apply
=> "mutation", then it's not a problem.
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.
Agreed. Fixing in subsequent refactoring steps.
evm/vm/base.py
Outdated
if self.receipts: | ||
gas_used = self.receipts[-1].gas_used | ||
state = self.get_state(self.chaindb, temp_block, prev_hashes, gas_used) | ||
state = self.get_state(self.chaindb, temp_block, prev_hashes, gas_used=0) |
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.
Hah, yay for unrelated bugfixes (gas_used
should always start at 0 in a temp 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(receipts) | ||
self.chaindb.persist_trie_data_dict(tx_kv_nodes) | ||
self.chaindb.persist_trie_data_dict(receipt_kv_nodes) |
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.
- Just wondering, since now there's no "clean up function", after calling
Chain.apply_transaction(transaction)
(Maybe it's miner to select transactions or eth-tester), it seems the transactions and receipts will be in the database permanently...? - Those four lines appear twice, maybe add a simple function to deal with it?
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.
+1 though I'm going to be fixing this in a subsequent PR.
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.
Maybe add a big warning to the docstring here stating that this will generate/persist partial block state and that has a big performance hit?
tx_root_hash, tx_kv_nodes = make_trie_root_and_nodes(block.transactions) | ||
receipt_root_hash, receipt_kv_nodes = make_trie_root_and_nodes(receipts) | ||
self.chaindb.persist_trie_data_dict(tx_kv_nodes) | ||
self.chaindb.persist_trie_data_dict(receipt_kv_nodes) |
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.
Maybe add a big warning to the docstring here stating that this will generate/persist partial block state and that has a big performance hit?
self.header = block.header.copy( | ||
transaction_root=tx_root_hash, | ||
receipt_root=receipt_root_hash, | ||
) |
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.
Shouldn't we update gas_used
here as well?
return self.get_state( | ||
chaindb=self.chaindb, | ||
block=self.block, | ||
prev_hashes=self.previous_hashes, | ||
gas_used=gas_used, | ||
gas_used=self.block.header.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.
IIUC, before this change, on every call to VM.apply_transaction()
, the VM.state
would include the gas_used
up to the last applied tx, but now it won't as self.block.header.gas_used
is only updated at the end of VM.import_block()
. Is that ok or can it be a problem when applying transactions that would exceed the block's gas limit?
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.
I'll add a test to check.
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.
This is currently handled. State.apply_transaction
calls State.add_transaction
which updates the block header's gas_used
. And VM.apply_transaction
updates self.block
on each call.
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.
Then in import_block() above we don't need to update gas_used
when we do self.block.copy()?
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.
+1
I'll get this cleaned up in a subsequent PR. adding a comment.
Fixes #579
What was wrong?
The way in which
eth-tester
uses py-evm requires the ability to incrementally apply transactions to thetip
block and then to mine the block when ready.Care is being taken to ensure that
VM.import_block
is still able to operate with a low amount of overhead when importing the block.How was it fixed?
Implemented
Chain.add_transaction
.Cute Animal Picture