-
Notifications
You must be signed in to change notification settings - Fork 159
Atomic block extension #981
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
Conversation
| if atomicState, err := b.vm.atomicBackend.GetVerifiedAtomicState(b.ethBlock.Hash()); err == nil { | ||
| _ = atomicState.Reject() // ignore this error so we can return the original error instead. | ||
| } | ||
| if b.extension != nil && (err != nil || !writes) { |
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 a comment here about what might be cleaned up would be helpful.
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.
The same outcome can be achieved with a slight refactor, which IMO is preferable to a comment. Comments should be reserved for why, not how nor what. "How" and "what" can be communicated in code, and if they're not then it suggests that the code needs to be changed. "Why" can't be communicated in code, so it requires a comment.
if noWrite := err != nil || !writes; noWrite && b.extension != nil {
b.extension.CleanupVerified()
}| return err | ||
| } | ||
|
|
||
| // semanticVerify verifies that a *Block is internally consistent. |
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 section could still go in another file. It's relatively tangential to the operations here.
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.
semanticVerify does not do much other than calling the extension. I find it a bit weird to split a type's functions into different files unless it leads to a gigantic file (like VM)
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.
Weren't these just moved from block_verification.go into here and the methods renamed? Or were they changed beyond that? The move adds a significant amount of unnecessary review.
2ddb618 to
d477ea2
Compare
|
This was merged while I was still reviewing (sorry it's taken so long) as I'm trying to get my head around the ultimate goal of block extensions. Are there use cases beyond adding atomic transactions? Abstractions add complexity (through indirection) and a requirement to maintain 2x the amount of code (implementation and interface definition), which I think are only justified when there is a need for more than one implementation. EDIT: to clarify, I like the abstraction of the atomic functionality as standalone methods, and think it simplifies things for SAE integration. What I'm still concerned about is possibly premature abstraction and YAGNI. |
|
|
||
| // Block implements the snowman.Block interface | ||
| type Block struct { | ||
| // wrappedBlock implements the snowman.wrappedBlock interface |
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.
snowman.Block interface
| // Block implements the snowman.Block interface | ||
| type Block struct { | ||
| // wrappedBlock implements the snowman.wrappedBlock interface | ||
| type wrappedBlock struct { |
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.
What makes this a wrapped block? If I understand correctly it's actually wrapping something (the extension), not being wrapped. What am I misunderstanding?
| vm: vm, | ||
| atomicTxs: atomicTxs, | ||
| }, nil | ||
| if vm.extensionConfig.BlockExtender != nil { |
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.
(subjective, readability) I would return early if it is nil, signalling to the reader what they need to remember and concentrate on:
extender := vm.extensionConfig.BlockExtender
if extender == nil {
return b, nil
}
ext, err := extender.NewBlockExtension(b)
if err != nil {
return nil, ...
}
b.extension = ext
return b, nilExtensive nesting adds complexity that adds cognitive load. Effectively our brains have to keep a stack for which every level of indentation is a push. The shorter the stack, the easier it is for the reader.
| if atomicState, err := b.vm.atomicBackend.GetVerifiedAtomicState(b.ethBlock.Hash()); err == nil { | ||
| _ = atomicState.Reject() // ignore this error so we can return the original error instead. | ||
| } | ||
| if b.extension != nil && (err != nil || !writes) { |
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.
The same outcome can be achieved with a slight refactor, which IMO is preferable to a comment. Comments should be reserved for why, not how nor what. "How" and "what" can be communicated in code, and if they're not then it suggests that the code needs to be changed. "Why" can't be communicated in code, so it requires a comment.
if noWrite := err != nil || !writes; noWrite && b.extension != nil {
b.extension.CleanupVerified()
}| return err | ||
| } | ||
|
|
||
| // semanticVerify verifies that a *Block is internally consistent. |
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.
Weren't these just moved from block_verification.go into here and the methods renamed? Or were they changed beyond that? The move adds a significant amount of unnecessary review.
| blockExtension, ok := wrappedBlock.GetBlockExtension().(atomic.AtomicBlockContext) | ||
| assert.True(ok, "unknown block extension type") | ||
|
|
||
| assert.Equal(txID, blockExtension.AtomicTxs()[0].ID(), "block does not include expected 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.
Why only check the first one?
| extDataHashes = fujiExtDataHashes | ||
| } | ||
| vm.syntacticBlockValidator = NewBlockValidator(extDataHashes) | ||
| vm.extensionConfig.BlockExtender = newBlockExtender(extDataHashes, vm) |
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.
Hard-coding the specific concrete implementation of an abstraction suggests the premature abstraction I was concerned about in my overall PR comment.
|
|
||
| // Note: the status of block is set by ChainState | ||
| blk, err := vm.newBlock(block) | ||
| blk, err := wrapBlock(block, vm) |
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.
Why was this changed from a method to a function that accepts the original method's receiver value anyway?
| return nil, err | ||
| } | ||
|
|
||
| return blk.(*wrappedBlock), nil |
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 isn't safe in production code as it could panic.
It's also another instance of a hard-coded concrete implementation of the abstraction.
| if lastAcceptedBlock == nil { | ||
| return nil | ||
| } | ||
| return lastAcceptedBlock.(*wrappedBlock) |
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.
As above.
Why this should be merged
Seperates atomic block verification, accept, reject from block by introducing a general block extender/extension interface
How this works
This pull request introduces significant changes to the EVM plugin to support block extensions, refactor block verification, and improve atomic transaction handling. The most notable updates include replacing the
snowman.Blockinterface with a newextension.ExtendedBlock, introducing a newblockExtensionfor handling block extensions. These changes aim to enhance modularity, maintainability, and support for future upgrades.blockExtensionimplementation inplugin/evm/atomic_block_extension.goto handle block extensions, including atomic transaction extraction, syntactic/semantic verification, and UTXO validation.These changes pave the way for a more extensible and robust EVM implementation, aligning with upcoming protocol upgrades and improving the handling of atomic transactions.
How this was tested
Existing UTs/e2e tests
Need to be documented?
no
Need to update RELEASES.md?
No