-
Notifications
You must be signed in to change notification settings - Fork 13
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
blockchain: add execution witness validation #515
blockchain: add execution witness validation #515
Conversation
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
260d1b1
to
043792d
Compare
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
043792d
to
fd59830
Compare
go 1.19 | ||
go 1.22 | ||
|
||
toolchain go1.22.1 |
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 was forced by the Go compiler since we updated to the lastest go-verkle
, see its go.mod.
5fe94aa
to
813f9ea
Compare
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
813f9ea
to
a588006
Compare
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
9ca31e6
to
e9f1c83
Compare
case *trie.TransitionTrie: | ||
vtrpre = tr.Overlay().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.
We stop doing witness generation for Overlay Tree.
root, err := statedb.Commit(0, false) | ||
if err != nil { | ||
panic(err) | ||
} |
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.
Apart from collecting the root
, we were also ignoring a potential error.
// If the VKT prestate is empty, this means that the "parent" root is the MPT. | ||
// If we don't do this, we'd consider the "parent" to be an empty VKT which isn't | ||
// correct. | ||
if pre.VKT != nil { | ||
parentRoot = root | ||
} |
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 added a comment since this if
might not be entirely obvious. Practically, the if
is irrelevant since we don't create witnesses now for Overlay Tree.
But, this method doesn't know about this decision and should return the "parent" root for all cases.
if chain.Config().ProofInBlocks { | ||
preTrie, err := state.Database().OpenTrie(parent.Root) | ||
if err != nil { | ||
return nil, fmt.Errorf("error opening pre-state tree root: %w", err) | ||
} | ||
|
||
var okpre, okpost bool | ||
var vtrpre, vtrpost *trie.VerkleTrie | ||
switch pre := preTrie.(type) { | ||
case *trie.VerkleTrie: | ||
vtrpre, okpre = preTrie.(*trie.VerkleTrie) | ||
switch tr := state.GetTrie().(type) { | ||
case *trie.VerkleTrie: | ||
vtrpost = tr | ||
okpost = true | ||
// This is to handle a situation right at the start of the conversion: | ||
// the post trie is a transition tree when the pre tree is an empty | ||
// verkle tree. | ||
case *trie.TransitionTrie: | ||
vtrpost = tr.Overlay() | ||
okpost = true | ||
default: | ||
okpost = false | ||
} | ||
case *trie.TransitionTrie: | ||
vtrpre = pre.Overlay() | ||
okpre = true | ||
post, _ := state.GetTrie().(*trie.TransitionTrie) | ||
vtrpost = post.Overlay() | ||
okpost = true | ||
default: | ||
// This should only happen for the first block of the | ||
// conversion, when the previous tree is a merkle tree. | ||
// Logically, the "previous" verkle tree is an empty tree. | ||
okpre = true | ||
vtrpre = trie.NewVerkleTrie(verkle.New(), state.Database().TrieDB(), utils.NewPointCache(), false) | ||
post := state.GetTrie().(*trie.TransitionTrie) | ||
vtrpost = post.Overlay() | ||
okpost = true | ||
} | ||
if okpre && okpost { | ||
if len(keys) > 0 { | ||
p, k, err = trie.ProveAndSerialize(vtrpre, vtrpost, keys, vtrpre.FlatdbNodeResolver) | ||
if err != nil { | ||
return nil, fmt.Errorf("error generating verkle proof for block %d: %w", header.Number, err) | ||
} | ||
} | ||
} |
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 deleted block chunk is only extracted to a new method below. I did this because now apart from creating witnesses in block generation, we also create the witness in block processing... validating the witness is now part of the block validation (more on that below).
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.
If you look at 517, this function will be split back into smaller elements. I suggest not doing that.
} | ||
return block, nil | ||
} | ||
|
||
func BuildVerkleProof(header *types.Header, state *state.StateDB, parentRoot common.Hash) (verkle.StateDiff, *verkle.VerkleProof, 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.
This method is just the old code. No code lines where changed.
@@ -131,6 +132,24 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD | |||
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { | |||
return fmt.Errorf("invalid merkle root (remote: %x local: %x) dberr: %w", header.Root, root, statedb.Error()) | |||
} | |||
if blockEw := block.ExecutionWitness(); blockEw != 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.
Finally, we reached the main goal of the PR. ValidateState
is an existing method that has the usual stateRoot and gasUsage checks when validating blocks.
Now I added logic to also check the execution witness (if the proposed block has one).
err = trie.DeserializeAndVerifyVerkleProof(block.ExecutionWitness().VerkleProof, block.ExecutionWitness().ParentStateRoot[:], block.Root().Bytes(), block.ExecutionWitness().StateDiff) | ||
err = verkle.Verify(block.ExecutionWitness().VerkleProof, block.ExecutionWitness().ParentStateRoot[:], block.Root().Bytes(), block.ExecutionWitness().StateDiff) |
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.
Sideffects of upgrading go-verkle
.
@@ -300,60 +300,6 @@ func ProveAndSerialize(pretrie, posttrie *VerkleTrie, keys [][]byte, resolver ve | |||
return p, kvps, nil | |||
} | |||
|
|||
func DeserializeAndVerifyVerkleProof(vp *verkle.VerkleProof, preStateRoot []byte, postStateRoot []byte, statediff verkle.StateDiff) 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.
This is gone, since was moved to go-verkle
.
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.
actually it's coming back in #517 but yeah let's sort this out when we get there.
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
FIXTURES_TAG: "verkle@v0.0.6" | ||
FIXTURES_TAG: "verkle@v0.0.7-alpha-4" |
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.
Whenever we merge this PR to kaustinen-with-shapella
, I'd be able to create the final v0.0.7
. That is because execution-spec-tests
targets kaustinen-with-shapella
by default, and I need this PR changes to be there first.
Kind of the usual chicken and egg problems. I can do a later quick PR to change this to v0.0.7
when that exists.
@@ -300,60 +300,6 @@ func ProveAndSerialize(pretrie, posttrie *VerkleTrie, keys [][]byte, resolver ve | |||
return p, kvps, nil | |||
} | |||
|
|||
func DeserializeAndVerifyVerkleProof(vp *verkle.VerkleProof, preStateRoot []byte, postStateRoot []byte, statediff verkle.StateDiff) 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.
actually it's coming back in #517 but yeah let's sort this out when we get there.
if chain.Config().ProofInBlocks { | ||
preTrie, err := state.Database().OpenTrie(parent.Root) | ||
if err != nil { | ||
return nil, fmt.Errorf("error opening pre-state tree root: %w", err) | ||
} | ||
|
||
var okpre, okpost bool | ||
var vtrpre, vtrpost *trie.VerkleTrie | ||
switch pre := preTrie.(type) { | ||
case *trie.VerkleTrie: | ||
vtrpre, okpre = preTrie.(*trie.VerkleTrie) | ||
switch tr := state.GetTrie().(type) { | ||
case *trie.VerkleTrie: | ||
vtrpost = tr | ||
okpost = true | ||
// This is to handle a situation right at the start of the conversion: | ||
// the post trie is a transition tree when the pre tree is an empty | ||
// verkle tree. | ||
case *trie.TransitionTrie: | ||
vtrpost = tr.Overlay() | ||
okpost = true | ||
default: | ||
okpost = false | ||
} | ||
case *trie.TransitionTrie: | ||
vtrpre = pre.Overlay() | ||
okpre = true | ||
post, _ := state.GetTrie().(*trie.TransitionTrie) | ||
vtrpost = post.Overlay() | ||
okpost = true | ||
default: | ||
// This should only happen for the first block of the | ||
// conversion, when the previous tree is a merkle tree. | ||
// Logically, the "previous" verkle tree is an empty tree. | ||
okpre = true | ||
vtrpre = trie.NewVerkleTrie(verkle.New(), state.Database().TrieDB(), utils.NewPointCache(), false) | ||
post := state.GetTrie().(*trie.TransitionTrie) | ||
vtrpost = post.Overlay() | ||
okpost = true | ||
} | ||
if okpre && okpost { | ||
if len(keys) > 0 { | ||
p, k, err = trie.ProveAndSerialize(vtrpre, vtrpost, keys, vtrpre.FlatdbNodeResolver) | ||
if err != nil { | ||
return nil, fmt.Errorf("error generating verkle proof for block %d: %w", header.Number, err) | ||
} | ||
} | ||
} |
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.
If you look at 517, this function will be split back into smaller elements. I suggest not doing that.
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
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.
LGTM. Maybe wait for the testnet relaunch before merging, but otherwise it's good to go.
This PR adds the required logic to add an extra validation when processing a block. We must verify that the execution witness generated while re-executing the block matches the one present in the block. This is a similar check as checking the stateRoot and usedGas.
It also adds the parentRoot to the witness when filling.