Skip to content
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

Post-Shapella Replay #431

Draft
wants to merge 2 commits into
base: kaustinen-with-shapella
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -2157,8 +2157,30 @@ func MakeChain(ctx *cli.Context, stack *node.Node, readonly bool) (*core.BlockCh
}
vmcfg := vm.Config{EnablePreimageRecording: ctx.Bool(VMEnableDebugFlag.Name)}

// Override the chain config with provided settings.
var overrides core.ChainOverrides
if ctx.IsSet(OverrideCancun.Name) {
v := ctx.Uint64(OverrideCancun.Name)
overrides.OverrideCancun = &v
}
if ctx.IsSet(OverridePrague.Name) {
v := ctx.Uint64(OverridePrague.Name)
overrides.OverridePrague = &v
}
if ctx.IsSet(OverrideProofInBlock.Name) {
v := ctx.Bool(OverrideProofInBlock.Name)
overrides.OverrideProofInBlock = &v
}
if ctx.IsSet(OverrideOverlayStride.Name) {
v := ctx.Uint64(OverrideOverlayStride.Name)
overrides.OverrideOverlayStride = &v
}
if ctx.IsSet(ClearVerkleCosts.Name) {
params.ClearVerkleWitnessCosts()
}

// Disable transaction indexing/unindexing by default.
chain, err := core.NewBlockChain(chainDb, cache, gspec, nil, engine, vmcfg, nil, nil)
chain, err := core.NewBlockChain(chainDb, cache, gspec, &overrides, engine, vmcfg, nil, nil)
if err != nil {
Fatalf("Can't create BlockChain: %v", err)
}
Expand Down
34 changes: 17 additions & 17 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,13 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
return errors.New("data blobs present in block body")
}
}
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
return consensus.ErrUnknownAncestor
}
fmt.Println("failure here")
return consensus.ErrPrunedAncestor
}
// if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
// if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
// return consensus.ErrUnknownAncestor
// }
// fmt.Println("failure here")
// return consensus.ErrPrunedAncestor
// }
return nil
}

Expand All @@ -121,16 +121,16 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
if rbloom != header.Bloom {
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
}
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]]))
receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil))
if receiptSha != header.ReceiptHash {
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
}
// Validate the state root against the received state root and throw
// an error if they don't match.
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())
}
// // Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, Rn]]))
// receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil))
// if receiptSha != header.ReceiptHash {
// return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
// }
// // Validate the state root against the received state root and throw
// // an error if they don't match.
// 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())
// }
// Verify that the advertised root is correct before
// it can be used as an identifier for the conversion
// status.
Expand Down
42 changes: 5 additions & 37 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,11 @@
package core

import (
"bufio"
"errors"
"fmt"
"io"
"math"
"math/big"
"os"
"runtime"
"strconv"
"strings"
"sync"
"sync/atomic"
Expand Down Expand Up @@ -1535,30 +1531,6 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
return bc.insertChain(chain, true)
}

func findVerkleConversionBlock() (uint64, error) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was removed since we don't rely on the old .txt anymore but in timestamps.

if _, err := os.Stat("conversion.txt"); os.IsNotExist(err) {
return math.MaxUint64, nil
}

f, err := os.Open("conversion.txt")
if err != nil {
log.Error("Failed to open conversion.txt", "err", err)
return 0, err
}
defer f.Close()

scanner := bufio.NewScanner(f)
scanner.Scan()
conversionBlock, err := strconv.ParseUint(scanner.Text(), 10, 64)
if err != nil {
log.Error("Failed to parse conversionBlock", "err", err)
return 0, err
}
log.Info("Found conversion block info", "conversionBlock", conversionBlock)

return conversionBlock, nil
}

// insertChain is the internal implementation of InsertChain, which assumes that
// 1) chains are contiguous, and 2) The chain mutex is held.
//
Expand All @@ -1573,11 +1545,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
return 0, nil
}

conversionBlock, err := findVerkleConversionBlock()
if err != nil {
return 0, err
}

// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
SenderCacher.RecoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number(), chain[0].Time()), chain)

Expand Down Expand Up @@ -1767,18 +1734,19 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
// is the fork block and that the conversion needs to be marked at started.
if !bc.stateCache.InTransition() && !bc.stateCache.Transitioned() {
bc.stateCache.StartVerkleTransition(parent.Root, emptyVerkleRoot, bc.Config(), bc.Config().PragueTime, parent.Root)
bc.stateCache.SetLastMerkleRoot(parent.Root)
}
} else {
// If the verkle activation time hasn't started, declare it as "not started".
// This is so that if the miner activates the conversion, the insertion happens
// in the correct mode.
bc.stateCache.InitTransitionStatus(false, false)
}
if parent.Number.Uint64() == conversionBlock {
bc.StartVerkleTransition(parent.Root, emptyVerkleRoot, bc.Config(), &parent.Time, parent.Root)
bc.stateCache.SetLastMerkleRoot(parent.Root)
stateRoot := parent.Root
if block.Header().Number.Uint64() == 4702178 {
stateRoot = common.HexToHash("0x00")
}
Comment on lines +1745 to 1748
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this was a very ugly rabbit hole due to a bug in the way our previous replay data was exported.

In the previous setup the first block to be replayed was 4702177 and unfortunately that block had a snapshot in the state. We need to force any second (i.e: previously 4702178) or further block to always read from the tree and not use snapshots.

Maybe the new replay data wasn't corrupted in that way and we can remove this. But it's worth checking that state.New(stateRoot, ...) in the line below never finds a snapshot for the second-and-further replayed blocks, it must always read from the tree. If that isn't the case, we might be replaying using snapshots prepopulated by the original execution that created the exported chain and not the replayed blocks.

statedb, err := state.New(parent.Root, bc.stateCache, bc.snaps)
statedb, err := state.New(stateRoot, bc.stateCache, bc.snaps)
if err != nil {
return it.index, err
}
Expand Down
1 change: 1 addition & 0 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ func init() {
func ActivePrecompiles(rules params.Rules) []common.Address {
switch {
case rules.IsPrague:
// Note: this config should be correctly set depending on replay starting point.
return PrecompiledAddressesBerlin
Comment on lines +154 to 155
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In our previous replay branch, we had to override this to be PrecompiledAddressByzantium. I think now the current configuration since Shanghai precompiles are the same as Berlin, correct?

Left a comment here to remember to keep updating this as we keep rebasing Verkle further down the forks. I can remove it though, since maybe from now forward the default branch and replay data will always match.

case rules.IsCancun:
return PrecompiledAddressesCancun
Expand Down
4 changes: 2 additions & 2 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ type (
func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
var precompiles map[common.Address]PrecompiledContract
switch {
case evm.chainRules.IsPrague:
precompiles = PrecompiledContractsBerlin
case evm.chainRules.IsCancun:
precompiles = PrecompiledContractsCancun
case evm.chainRules.IsPrague:
precompiles = PrecompiledContractsBerlin
Comment on lines -44 to +47
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to express the correct order since we're rebased on top of shapella, not cancun.

case evm.chainRules.IsBerlin:
precompiles = PrecompiledContractsBerlin
case evm.chainRules.IsIstanbul:
Expand Down
6 changes: 3 additions & 3 deletions core/vm/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ func NewEVMInterpreter(evm *EVM) *EVMInterpreter {
// If jump table was not initialised we set the default one.
var table *JumpTable
switch {
case evm.chainRules.IsPrague:
// TODO replace with prooper instruction set when fork is specified
table = &pragueInstructionSet
case evm.chainRules.IsCancun:
table = &cancunInstructionSet
case evm.chainRules.IsPrague:
// TODO replace with proper instruction set when fork is specified
table = &shanghaiInstructionSet
Comment on lines -59 to +63
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an important change for replay since we're replaying history without many gas changes for Verkle.

Before potentially merging this branch into kaustinen-with-shapella we should find a reasonable way to configure this depending if the branch is used for Kaustinen or replay.

case evm.chainRules.IsShanghai:
table = &shanghaiInstructionSet
case evm.chainRules.IsMerge:
Expand Down
2 changes: 1 addition & 1 deletion params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ func (c *ChainConfig) IsCancun(num *big.Int, time uint64) bool {

// IsPrague returns whether num is either equal to the Prague fork time or greater.
func (c *ChainConfig) IsPrague(num *big.Int, time uint64) bool {
return c.IsLondon(num) && isTimestampForked(c.PragueTime, time)
return c.IsShanghai(num, time) && isTimestampForked(c.PragueTime, time)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we might need to do a similar change in kaustinen-with-shapella since I Think using IsLondon isn't entirely correct?

}

// CheckCompatible checks whether scheduled fork transitions have been imported
Expand Down
18 changes: 18 additions & 0 deletions trie/verkle.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,24 @@ func (trie *VerkleTrie) UpdateStorage(address common.Address, key, value []byte)
}

func (t *VerkleTrie) DeleteAccount(addr common.Address) error {
var (
err error
values = make([][]byte, verkle.NodeWidth)
stem = t.pointCache.GetTreeKeyVersionCached(addr[:])
)

for i := 0; i < verkle.NodeWidth; i++ {
values[i] = zero[:]
}
switch root := t.root.(type) {
case *verkle.InternalNode:
err = root.InsertValuesAtStem(stem, values, t.FlatdbNodeResolver)
default:
return errInvalidRootType
}
if err != nil {
return fmt.Errorf("DeleteAccount (%x) error: %v", addr, err)
}
Comment on lines +226 to +243
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another thing that we should make dynamic if we merge replay in kaustinen-with-shapella. For running Kaustinen this should be skipped, for replay it should be enabled.

return nil
}

Expand Down
Loading