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-genesis transition #304

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ad3281f
post-genesis transition
gballet Nov 8, 2023
b7381c0
quell linter issue
gballet Nov 8, 2023
d49cbd6
support Transition post tree in conversion
gballet Nov 8, 2023
a7bc86f
Refactor transition post genesis (#311)
gballet Nov 30, 2023
0582870
fix OpenStorageTrie: return an error instead of panicking if the acco…
gballet Dec 5, 2023
3e5b4b7
add a switch to force proof in blocks (#322)
gballet Dec 5, 2023
8bfaaa0
add switch to override the stride of the overlay conversion (#323)
gballet Dec 5, 2023
677e66e
add a few traces for relaunch
gballet Dec 5, 2023
28eee60
add missing step in the chain of setting the override for overlay stride
gballet Dec 6, 2023
3cbf601
fix: save and load transition state for block processing (#324)
gballet Dec 8, 2023
7e86b80
fix: ensure StorageProcessed is properly recovered
gballet Dec 10, 2023
6a255d0
add traces for gas pool overflow
gballet Dec 11, 2023
9b54f29
cmd/{geth, utils}: add a command to clear verkle costs (#326)
gballet Jan 26, 2024
6841063
fix: check for nil override in NewBlockChain (#357)
gballet Feb 3, 2024
a4c556d
fix rebase issue
gballet Feb 8, 2024
a774d16
fix a few rebase issues
gballet Feb 8, 2024
1784337
add debug traces
gballet Feb 9, 2024
ff36168
fix: assume that having to create a new transaction state mean it has…
gballet Feb 9, 2024
a641947
add logs to debug shadowfork
gballet Feb 13, 2024
7a3bb67
persist conversion state to db and use an LRU cache for active transi…
gballet Feb 22, 2024
afc364b
eth: add debug_conversionStatus RPC call (#392)
gballet Mar 2, 2024
bb3a39d
fix post-verge sync (#404)
gballet Mar 13, 2024
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
75 changes: 75 additions & 0 deletions .github/workflows/conversion.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: Overlay conversion

on:
push:
branches: [ master, transition-post-genesis, store-transition-state-in-db ]
pull_request:
branches: [ master, kaustinen-with-shapella, transition-post-genesis, store-transition-state-in-db, lock-overlay-transition ]
workflow_dispatch:

jobs:
build:
runs-on: self-hosted
steps:
- uses: actions/checkout@v2
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.21.1

- name: Cleanup from previous runs
run: |
rm -f log.txt
rm -rf .shadowfork
rm -f genesis.json

- name: Download genesis file
run: wget https://gist.githubusercontent.com/gballet/0b02a025428aa0e7b67941864d54716c/raw/bfb4e158bca5217b356a19b2ec55c4a45a7b2bad/genesis.json

- name: Init data
run: go run ./cmd/geth --dev --cache.preimages init genesis.json

- name: Run geth in devmode
run: go run ./cmd/geth --dev --dev.period=5 --cache.preimages --http --datadir=.shadowfork --override.overlay-stride=10 --override.prague=$(($(date +%s) + 45)) > log.txt &

- name: Wait for the transition to start
run: |
start_time=$(date +%s)
while true; do
sleep 5
current_time=$(date +%s)
elapsed_time=$((current_time - start_time))

# 2 minute timeout
if [ $elapsed_time -ge 120 ]; then
kill -9 $(pgrep -f geth)
exit 1
fi

# Check for signs that the conversion has started
if grep -q "Processing verkle conversion starting at" log.txt; then
break
fi
done

- name: Wait for the transition to end
run: |
start_time=$(date +%s)
while true; do
sleep 5
current_time=$(date +%s)
elapsed_time=$((current_time - start_time))

# 10 minute timeout
if [ $elapsed_time -ge 300 ]; then
cat log.txt
kill -9 $(pgrep -f geth)
exit 1
fi

# Check for signs that the conversion has started
if egrep -q "at block.*performing transition\? false" log.txt; then
kill -9 $(pgrep -f geth)
break
fi
done
11 changes: 11 additions & 0 deletions cmd/geth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,17 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
v := ctx.Uint64(utils.OverridePrague.Name)
cfg.Eth.OverridePrague = &v
}
if ctx.IsSet(utils.OverrideProofInBlock.Name) {
v := ctx.Bool(utils.OverrideProofInBlock.Name)
cfg.Eth.OverrideProofInBlock = &v
}
if ctx.IsSet(utils.OverrideOverlayStride.Name) {
v := ctx.Uint64(utils.OverrideOverlayStride.Name)
cfg.Eth.OverrideOverlayStride = &v
}
if ctx.IsSet(utils.ClearVerkleCosts.Name) {
params.ClearVerkleWitnessCosts()
}
backend, eth := utils.RegisterEthService(stack, &cfg.Eth)

// Configure log filter RPC API.
Expand Down
3 changes: 3 additions & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,11 @@ var (
utils.NoUSBFlag,
utils.USBFlag,
utils.SmartCardDaemonPathFlag,
utils.OverrideOverlayStride,
utils.OverrideCancun,
utils.OverridePrague,
utils.OverrideProofInBlock,
utils.ClearVerkleCosts,
utils.EnablePersonal,
utils.TxPoolLocalsFlag,
utils.TxPoolNoLocalsFlag,
Expand Down
16 changes: 16 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,12 @@ var (
Value: 2048,
Category: flags.EthCategory,
}
OverrideOverlayStride = &cli.Uint64Flag{
Name: "override.overlay-stride",
Usage: "Manually specify the stride of the overlay transition, overriding the bundled setting",
Value: 10000,
Category: flags.EthCategory,
}
OverrideCancun = &cli.Uint64Flag{
Name: "override.cancun",
Usage: "Manually specify the Cancun fork timestamp, overriding the bundled setting",
Expand All @@ -273,6 +279,16 @@ var (
Usage: "Manually specify the Verkle fork timestamp, overriding the bundled setting",
Category: flags.EthCategory,
}
OverrideProofInBlock = &cli.BoolFlag{
Name: "override.blockproof",
Usage: "Manually specify the proof-in-block setting",
Category: flags.EthCategory,
}
ClearVerkleCosts = &cli.BoolFlag{
Name: "clear.verkle.costs",
Usage: "Clear verkle costs (for shadow forks)",
Category: flags.EthCategory,
}
// Light server and client settings
LightServeFlag = &cli.IntFlag{
Name: "light.serve",
Expand Down
93 changes: 61 additions & 32 deletions consensus/beacon/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ import (
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
"github.com/ethereum/go-ethereum/core/overlay"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie"
Expand Down Expand Up @@ -363,6 +365,14 @@ func (beacon *Beacon) Finalize(chain consensus.ChainHeaderReader, header *types.
state.Witness().TouchAddressOnWriteAndComputeGas(w.Address[:], uint256.Int{}, utils.CodeKeccakLeafKey)
state.Witness().TouchAddressOnWriteAndComputeGas(w.Address[:], uint256.Int{}, utils.CodeSizeLeafKey)
}

if chain.Config().IsPrague(header.Number, header.Time) {
fmt.Println("at block", header.Number, "performing transition?", state.Database().InTransition())
parent := chain.GetHeaderByHash(header.ParentHash)
if err := overlay.OverlayVerkleTransition(state, parent.Root, chain.Config().OverlayStride); err != nil {
log.Error("error performing the transition", "err", err)
}
}
}

// FinalizeAndAssemble implements consensus.Engine, setting the final state and
Expand All @@ -387,51 +397,70 @@ func (beacon *Beacon) FinalizeAndAssemble(chain consensus.ChainHeaderReader, hea

// Assign the final state root to header.
header.Root = state.IntermediateRoot(true)
state.Database().SaveTransitionState(header.Root)

var (
p *verkle.VerkleProof
k verkle.StateDiff
keys = state.Witness().Keys()
)
if chain.Config().IsPrague(header.Number, header.Time) && chain.Config().ProofInBlocks {
if chain.Config().IsPrague(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)
}

preTrie, err := state.Database().OpenTrie(parent.Root)
if err != nil {
return nil, fmt.Errorf("error opening pre-state tree root: %w", err)
}
// Load transition state at beginning of block, because
// OpenTrie needs to know what the conversion status is.
state.Database().LoadTransitionState(parent.Root)

var okpre, okpost bool
var vtrpre, vtrpost *trie.VerkleTrie
switch pre := preTrie.(type) {
case *trie.VerkleTrie:
vtrpre, okpre = preTrie.(*trie.VerkleTrie)
vtrpost, okpost = state.GetTrie().(*trie.VerkleTrie)
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)
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)
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions core/block_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ 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())
}
statedb.Database().SaveTransitionState(header.Root)
return nil
}

Expand Down
41 changes: 35 additions & 6 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ const (
txLookupCacheLimit = 1024
maxFutureBlocks = 256
maxTimeFutureBlocks = 30
TriesInMemory = 128
TriesInMemory = 8192

// BlockChainVersion ensures that an incompatible database forces a resync from scratch.
//
Expand Down Expand Up @@ -251,6 +251,14 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis
if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
return nil, genesisErr
}
if overrides != nil {
if overrides.OverrideProofInBlock != nil {
chainConfig.ProofInBlocks = *overrides.OverrideProofInBlock
}
if overrides.OverrideOverlayStride != nil {
chainConfig.OverlayStride = *overrides.OverrideOverlayStride
}
}
log.Info("")
log.Info(strings.Repeat("-", 153))
for _, line := range strings.Split(chainConfig.Description(), "\n") {
Expand Down Expand Up @@ -313,6 +321,12 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, genesis *Genesis

// Declare the end of the verkle transition if need be
if bc.chainConfig.Rules(head.Number, false /* XXX */, head.Time).IsPrague {
// TODO this only works when resuming a chain that has already gone
// through the conversion. All pointers should be saved to the DB
// for it to be able to recover if interrupted during the transition
// but that's left out to a later PR since there's not really a need
// right now.
bc.stateCache.InitTransitionStatus(true, true)
bc.stateCache.EndVerkleTransition()
}

Expand Down Expand Up @@ -1746,8 +1760,22 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
}

if bc.Config().IsPrague(block.Number(), block.Time()) {
bc.stateCache.LoadTransitionState(parent.Root)

// pragueTime has been reached. If the transition isn't active, it means this
// 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)
}
} 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)
bc.StartVerkleTransition(parent.Root, emptyVerkleRoot, bc.Config(), &parent.Time, parent.Root)
bc.stateCache.SetLastMerkleRoot(parent.Root)
}
statedb, err := state.New(parent.Root, bc.stateCache, bc.snaps)
Expand Down Expand Up @@ -1989,7 +2017,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
parent = bc.GetHeader(parent.ParentHash, parent.Number.Uint64()-1)
}
if parent == nil {
return it.index, errors.New("missing parent")
return it.index, fmt.Errorf("missing parent: hash=%x, number=%d", current.Hash(), current.Number)
}
// Import all the pruned blocks to make the state available
var (
Expand Down Expand Up @@ -2050,7 +2078,7 @@ func (bc *BlockChain) recoverAncestors(block *types.Block) (common.Hash, error)
}
}
if parent == nil {
return common.Hash{}, errors.New("missing parent")
return common.Hash{}, fmt.Errorf("missing parent during ancestor recovery: hash=%x, number=%d", block.ParentHash(), block.Number())
}
// Import all the pruned blocks to make the state available
for i := len(hashes) - 1; i >= 0; i-- {
Expand Down Expand Up @@ -2288,6 +2316,7 @@ func (bc *BlockChain) SetCanonical(head *types.Block) (common.Hash, error) {
defer bc.chainmu.Unlock()

// Re-execute the reorged chain in case the head state is missing.
log.Trace("looking for state", "root", head.Root(), "has state", bc.HasState(head.Root()))
if !bc.HasState(head.Root()) {
if latestValidHash, err := bc.recoverAncestors(head); err != nil {
return latestValidHash, err
Expand Down Expand Up @@ -2533,8 +2562,8 @@ func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
return time.Duration(bc.flushInterval.Load())
}

func (bc *BlockChain) StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, pragueTime *uint64) {
bc.stateCache.StartVerkleTransition(originalRoot, translatedRoot, chainConfig, pragueTime)
func (bc *BlockChain) StartVerkleTransition(originalRoot, translatedRoot common.Hash, chainConfig *params.ChainConfig, pragueTime *uint64, root common.Hash) {
bc.stateCache.StartVerkleTransition(originalRoot, translatedRoot, chainConfig, pragueTime, root)
}
func (bc *BlockChain) ReorgThroughVerkleTransition() {
bc.stateCache.ReorgThroughVerkleTransition()
Expand Down
11 changes: 7 additions & 4 deletions core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int,
return db, blocks, receipts
}

func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts, []*verkle.VerkleProof, []verkle.StateDiff) {
func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine consensus.Engine, diskdb ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts, []*verkle.VerkleProof, []verkle.StateDiff) {
if config == nil {
config = params.TestChainConfig
}
Expand Down Expand Up @@ -434,13 +434,16 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine
return nil, nil
}
var snaps *snapshot.Tree
triedb := state.NewDatabaseWithConfig(db, nil)
triedb.EndVerkleTransition()
db := state.NewDatabaseWithConfig(diskdb, nil)
db.StartVerkleTransition(common.Hash{}, common.Hash{}, config, config.PragueTime, common.Hash{})
db.EndVerkleTransition()
db.SaveTransitionState(parent.Root())
for i := 0; i < n; i++ {
statedb, err := state.New(parent.Root(), triedb, snaps)
statedb, err := state.New(parent.Root(), db, snaps)
if err != nil {
panic(fmt.Sprintf("could not find state for block %d: err=%v, parent root=%x", i, err, parent.Root()))
}
statedb.NewAccessWitness()
block, receipt := genblock(i, parent, statedb)
blocks[i] = block
receipts[i] = receipt
Expand Down
Loading
Loading