-
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
Changes from all commits
9beccc2
86c9305
7ee97cc
20fa694
b0521df
7ee8902
47e17d1
fd59830
0406d61
a588006
b08b06b
dcaffc6
3ca9415
c781d4b
034a01c
b738092
c229a56
551b0c8
606e682
4afce0a
f4a98cf
8461f91
e9f1c83
bf7a55b
8790b40
3a17341
1ac865c
ff97fbe
d134ccf
f0cfa2e
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 |
---|---|---|
|
@@ -8,7 +8,7 @@ on: | |
workflow_dispatch: | ||
|
||
env: | ||
FIXTURES_TAG: "[email protected].6" | ||
FIXTURES_TAG: "[email protected].7-alpha-4" | ||
|
||
jobs: | ||
setup: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -69,6 +69,7 @@ type ExecutionResult struct { | |
// Verkle witness | ||
VerkleProof *verkle.VerkleProof `json:"verkleProof,omitempty"` | ||
StateDiff verkle.StateDiff `json:"stateDiff,omitempty"` | ||
ParentRoot common.Hash `json:"parentRoot,omitempty"` | ||
|
||
// Values to test the verkle conversion | ||
CurrentAccountAddress *common.Address `json:"currentConversionAddress,omitempty" gencodec:"optional"` | ||
|
@@ -163,17 +164,18 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, | |
return h | ||
} | ||
var ( | ||
statedb = MakePreState(rawdb.NewMemoryDatabase(), chainConfig, pre, chainConfig.IsVerkle(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp)) | ||
vtrpre *trie.VerkleTrie | ||
signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) | ||
gaspool = new(core.GasPool) | ||
blockHash = common.Hash{0x13, 0x37} | ||
rejectedTxs []*rejectedTx | ||
includedTxs types.Transactions | ||
gasUsed = uint64(0) | ||
receipts = make(types.Receipts, 0) | ||
txIndex = 0 | ||
parentStateRoot, statedb = MakePreState(rawdb.NewMemoryDatabase(), chainConfig, pre, chainConfig.IsVerkle(big.NewInt(int64(pre.Env.Number)), pre.Env.Timestamp)) | ||
vtrpre *trie.VerkleTrie | ||
signer = types.MakeSigner(chainConfig, new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) | ||
gaspool = new(core.GasPool) | ||
blockHash = common.Hash{0x13, 0x37} | ||
rejectedTxs []*rejectedTx | ||
includedTxs types.Transactions | ||
gasUsed = uint64(0) | ||
receipts = make(types.Receipts, 0) | ||
txIndex = 0 | ||
) | ||
|
||
gaspool.AddGas(pre.Env.GasLimit) | ||
vmContext := vm.BlockContext{ | ||
CanTransfer: core.CanTransfer, | ||
|
@@ -191,8 +193,6 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, | |
switch tr := statedb.GetTrie().(type) { | ||
case *trie.VerkleTrie: | ||
vtrpre = tr.Copy() | ||
case *trie.TransitionTrie: | ||
vtrpre = tr.Overlay().Copy() | ||
Comment on lines
-194
to
-195
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 stop doing witness generation for Overlay Tree. |
||
} | ||
|
||
// If currentBaseFee is defined, add it to the vmContext. | ||
|
@@ -391,6 +391,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, | |
BaseFee: (*math.HexOrDecimal256)(vmContext.BaseFee), | ||
VerkleProof: vktProof, | ||
StateDiff: vktStateDiff, | ||
ParentRoot: parentStateRoot, | ||
} | ||
if pre.Env.Withdrawals != nil { | ||
h := types.DeriveSha(types.Withdrawals(pre.Env.Withdrawals), trie.NewStackTrie(nil)) | ||
|
@@ -425,7 +426,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, | |
return statedb, execRs, nil | ||
} | ||
|
||
func MakePreState(db ethdb.Database, chainConfig *params.ChainConfig, pre *Prestate, verkle bool) *state.StateDB { | ||
func MakePreState(db ethdb.Database, chainConfig *params.ChainConfig, pre *Prestate, verkle bool) (common.Hash, *state.StateDB) { | ||
// Start with generating the MPT DB, which should be empty if it's post-verkle transition | ||
sdb := state.NewDatabaseWithConfig(db, &trie.Config{Preimages: true, Verkle: false}) | ||
|
||
|
@@ -451,9 +452,12 @@ func MakePreState(db ethdb.Database, chainConfig *params.ChainConfig, pre *Prest | |
codeHash := crypto.Keccak256Hash(acc.Code) | ||
rawdb.WriteCode(codeWriter, codeHash, acc.Code) | ||
} | ||
statedb.Commit(0, false) | ||
root, err := statedb.Commit(0, false) | ||
if err != nil { | ||
panic(err) | ||
} | ||
Comment on lines
+455
to
+458
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. Apart from collecting the |
||
|
||
return statedb | ||
return root, statedb | ||
} | ||
|
||
// MPT pre is the same as the pre state for first conversion block | ||
|
@@ -473,12 +477,13 @@ func MakePreState(db ethdb.Database, chainConfig *params.ChainConfig, pre *Prest | |
panic(err) | ||
} | ||
|
||
parentRoot := mptRoot | ||
// If verkle mode started, establish the conversion | ||
if verkle { | ||
// If the current tree is a VerkleTrie, it means the state conversion has ended. | ||
// We don't need to continue with conversion setups and can return early. | ||
if _, ok := statedb.GetTrie().(*trie.VerkleTrie); ok { | ||
return statedb | ||
return parentRoot, statedb | ||
} | ||
|
||
rawdb.WritePreimages(sdb.DiskDB(), statedb.Preimages()) | ||
|
@@ -548,6 +553,12 @@ func MakePreState(db ethdb.Database, chainConfig *params.ChainConfig, pre *Prest | |
} | ||
|
||
root, _ := statedb.Commit(0, false) | ||
// 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 | ||
} | ||
Comment on lines
+556
to
+561
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. I added a comment since this But, this method doesn't know about this decision and should return the "parent" root for all cases. |
||
|
||
// recreate the verkle db with the tree root, but this time with the mpt snapshot, | ||
// so that the conversion can proceed. | ||
|
@@ -565,7 +576,7 @@ func MakePreState(db ethdb.Database, chainConfig *params.ChainConfig, pre *Prest | |
if statedb.Database().InTransition() || statedb.Database().Transitioned() { | ||
log.Info("at end of makestate", "in transition", statedb.Database().InTransition(), "during", statedb.Database().Transitioned(), "account hash", statedb.Database().GetCurrentAccountHash()) | ||
} | ||
return statedb | ||
return parentRoot, statedb | ||
} | ||
|
||
func rlpHash(x interface{}) (h common.Hash) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -400,82 +400,92 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea | |
state.Database().SaveTransitionState(header.Root) | ||
|
||
var ( | ||
p *verkle.VerkleProof | ||
k verkle.StateDiff | ||
keys = state.Witness().Keys() | ||
proot common.Hash | ||
proof *verkle.VerkleProof | ||
stateDiff verkle.StateDiff | ||
proot common.Hash | ||
) | ||
if chain.Config().IsVerkle(header.Number, header.Time) { | ||
// Open the pre-tree to prove the pre-state against | ||
parent := chain.GetHeaderByNumber(header.Number.Uint64() - 1) | ||
if parent == nil { | ||
return nil, fmt.Errorf("nil parent header for block %d", header.Number) | ||
} | ||
proot = parent.Root | ||
|
||
// Load transition state at beginning of block, because | ||
// OpenTrie needs to know what the conversion status is. | ||
state.Database().LoadTransitionState(parent.Root) | ||
|
||
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) | ||
} | ||
} | ||
} | ||
Comment on lines
-420
to
-467
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 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 commentThe 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. |
||
var err error | ||
stateDiff, proof, err = BuildVerkleProof(header, state, parent.Root) | ||
if err != nil { | ||
return nil, fmt.Errorf("error building verkle proof: %w", err) | ||
} | ||
proot = parent.Root | ||
} | ||
|
||
// Assemble and return the final block. | ||
block := types.NewBlockWithWithdrawals(header, txs, uncles, receipts, withdrawals, trie.NewStackTrie(nil)) | ||
if chain.Config().IsVerkle(header.Number, header.Time) && chain.Config().ProofInBlocks { | ||
block.SetVerkleProof(p, k, proot) | ||
block.SetVerkleProof(proof, stateDiff, proot) | ||
} | ||
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 commentThe reason will be displayed to describe this comment to others. Learn more. This method is just the old code. No code lines where changed. |
||
var ( | ||
proof *verkle.VerkleProof | ||
stateDiff verkle.StateDiff | ||
) | ||
|
||
preTrie, err := state.Database().OpenTrie(parentRoot) | ||
if err != nil { | ||
return nil, 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 { | ||
keys := state.Witness().Keys() | ||
if len(keys) > 0 { | ||
proof, stateDiff, err = trie.ProveAndSerialize(vtrpre, vtrpost, keys, vtrpre.FlatdbNodeResolver) | ||
if err != nil { | ||
return nil, nil, fmt.Errorf("error generating verkle proof for block %d: %w", header.Number, err) | ||
} | ||
} | ||
} | ||
return stateDiff, proof, nil | ||
} | ||
|
||
// Seal generates a new sealing request for the given input block and pushes | ||
// the result into the given channel. | ||
// | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,7 @@ import ( | |
"fmt" | ||
|
||
"github.com/ethereum/go-ethereum/consensus" | ||
"github.com/ethereum/go-ethereum/consensus/beacon" | ||
"github.com/ethereum/go-ethereum/core/state" | ||
"github.com/ethereum/go-ethereum/core/types" | ||
"github.com/ethereum/go-ethereum/params" | ||
|
@@ -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 commentThe reason will be displayed to describe this comment to others. Learn more. Finally, we reached the main goal of the PR. Now I added logic to also check the execution witness (if the proposed block has one). |
||
parent := v.bc.GetHeaderByNumber(header.Number.Uint64() - 1) | ||
if parent == nil { | ||
return fmt.Errorf("nil parent header for block %d", header.Number) | ||
} | ||
stateDiff, proof, err := beacon.BuildVerkleProof(header, statedb, parent.Root) | ||
if err != nil { | ||
return fmt.Errorf("error building verkle proof: %w", err) | ||
} | ||
ew := types.ExecutionWitness{ | ||
StateDiff: stateDiff, | ||
VerkleProof: proof, | ||
ParentStateRoot: parent.Root, | ||
} | ||
if err := ew.Equal(blockEw); err != nil { | ||
return fmt.Errorf("invalid execution witness: %v", err) | ||
} | ||
} | ||
// Verify that the advertised root is correct before | ||
// it can be used as an identifier for the conversion | ||
// status. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -432,7 +432,7 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine | |
proots = append(proots, parent.Root()) | ||
|
||
// quick check that we are self-consistent | ||
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 commentThe reason will be displayed to describe this comment to others. Learn more. Sideffects of upgrading |
||
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.
Whenever we merge this PR to
kaustinen-with-shapella
, I'd be able to create the finalv0.0.7
. That is becauseexecution-spec-tests
targetskaustinen-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.