Skip to content

Commit

Permalink
consortium: avoid reading statedb in block header verification path
Browse files Browse the repository at this point in the history
In snap sync, we only verify header without processing block transactions so the
statedb is not available. As a result, we need to avoid reading statedb in the
header verification path and must use the information from header instead.

This commit disables signer list verification in extra data of checkpoint block
header in consortium version 1 when snap/fast sync. In consortium version 2, at
the forked block, when getting the list of validators, we read this information
from header instead of contract, it is expected to behave the same as before.
This commit does not change the header verification process when full sync.
  • Loading branch information
minh-bq committed Dec 25, 2023
1 parent 5950f02 commit d23228e
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 13 deletions.
4 changes: 0 additions & 4 deletions cmd/ronin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,10 +325,6 @@ func prepare(ctx *cli.Context) {
case !ctx.GlobalIsSet(utils.NetworkIdFlag.Name):
log.Info("Starting Geth on Ethereum mainnet...")
}
// remove snap out of ronin
if ctx.GlobalString(utils.SyncModeFlag.Name) == "snap" {
ctx.GlobalSet(utils.SyncModeFlag.Name, "full")
}
// If we're a full node on mainnet without --cache specified, bump default cache allowance
if ctx.GlobalString(utils.SyncModeFlag.Name) != "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) && !ctx.GlobalIsSet(utils.NetworkIdFlag.Name) {
// Make sure we're not on any supported preconfigured testnet either
Expand Down
2 changes: 1 addition & 1 deletion consensus/consortium/common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ type BaseSnapshot struct {
// ConsortiumAdapter defines a small collection of methods needed to access the private
// methods between consensus engines
type ConsortiumAdapter interface {
GetSnapshot(chain consensus.ChainHeaderReader, number uint64, parents []*types.Header) *BaseSnapshot
GetSnapshot(chain consensus.ChainHeaderReader, number uint64, hash common.Hash, parents []*types.Header) *BaseSnapshot
}
21 changes: 16 additions & 5 deletions consensus/consortium/v1/consortium.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"io"
"math/big"
"math/rand"
"os"
"sync"
"time"

Expand Down Expand Up @@ -52,7 +53,8 @@ const (
inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory
inmemorySignatures = 4096 // Number of recent block signatures to keep in memory

wiggleTime = 1000 * time.Millisecond // Random delay (per signer) to allow concurrent signers
wiggleTime = 1000 * time.Millisecond // Random delay (per signer) to allow concurrent signers
FastSyncEnv = "FAST_SYNC"
)

// Consortium proof-of-authority protocol constants.
Expand Down Expand Up @@ -282,10 +284,15 @@ func (c *Consortium) verifyCascadingFields(chain consensus.ChainHeaderReader, he
return err
}

var fastSync bool
if os.Getenv(FastSyncEnv) == "1" {
fastSync = true
}

extraSuffix := len(header.Extra) - consortiumCommon.ExtraSeal
checkpointHeaders := consortiumCommon.ExtractAddressFromBytes(header.Extra[extraVanity:extraSuffix])
validSigners := consortiumCommon.CompareSignersLists(checkpointHeaders, signers)
if !validSigners {
if !fastSync && !validSigners {
log.Error("signers lists are different in checkpoint header and snapshot", "number", number, "signersHeader", checkpointHeaders, "signers", signers)
return consortiumCommon.ErrInvalidCheckpointSigners
}
Expand Down Expand Up @@ -716,9 +723,13 @@ func (c *Consortium) CalcDifficulty(chain consensus.ChainHeaderReader, time uint
return c.doCalcDifficulty(c.val, number, validators)
}

func (c *Consortium) GetSnapshot(chain consensus.ChainHeaderReader, number uint64, parents []*types.Header) *consortiumCommon.BaseSnapshot {
header := chain.GetHeaderByNumber(number)
snap, err := c.snapshot(chain, number, header.Hash(), parents)
func (c *Consortium) GetSnapshot(
chain consensus.ChainHeaderReader,
number uint64,
hash common.Hash,
parents []*types.Header,
) *consortiumCommon.BaseSnapshot {
snap, err := c.snapshot(chain, number, hash, parents)
if err != nil {
return nil
}
Expand Down
60 changes: 57 additions & 3 deletions consensus/consortium/v2/consortium.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,15 @@ func (c *Consortium) verifyCascadingFields(chain consensus.ChainHeaderReader, he
return err
}

// Retrieve the snapshot needed to verify this header and cache it
snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
var (
snap *Snapshot
err error
)
if number == c.forkedBlock {
snap, err = c.snapshotAtConsortiumFork(chain, number-1, header.ParentHash, header, parents)
} else {
snap, err = c.snapshot(chain, number-1, header.ParentHash, parents)
}
if err != nil {
return err
}
Expand All @@ -385,6 +392,53 @@ func (c *Consortium) verifyCascadingFields(chain consensus.ChainHeaderReader, he
return c.verifySeal(chain, header, parents, snap)
}

// snapshotAtConsortiumFork is expected to have to the same
// behavior as snapshot. However, the validator list is read from
// header instead of statedb to support snap sync. We try to keep
// the snapshot function unchanged to minimize the changing effect.
func (c *Consortium) snapshotAtConsortiumFork(
chain consensus.ChainHeaderReader,
number uint64,
hash common.Hash,
forkedHeader *types.Header,
parents []*types.Header,
) (*Snapshot, error) {
var validators []common.Address

snap, err := loadSnapshot(c.config, c.signatures, c.db, hash, c.ethAPI, c.chainConfig)
if err == nil {
log.Trace("Loaded snapshot from disk", "number", number, "hash", hash.Hex())
return snap, nil
}

extraData, err := finality.DecodeExtra(forkedHeader.Extra, false)
if err != nil {
return nil, err
}

for _, validator := range extraData.CheckpointValidators {
validators = append(validators, validator.Address)
}

snap = newSnapshot(c.chainConfig, c.config, c.signatures, number, hash, validators, nil, c.ethAPI)

// load v1 recent list to prevent recent producing-block-validators produce block again
snapV1 := c.v1.GetSnapshot(chain, number, hash, parents)
if snapV1 == nil {
return nil, errors.New("snapshot v1 is not available")
}

snap.Recents = consortiumCommon.RemoveOutdatedRecents(snapV1.Recents, number)

if err := snap.store(c.db); err != nil {
return nil, err
}
log.Info("Stored checkpoint snapshot to disk", "number", number, "hash", hash)
figure.NewColorFigure("Welcome to DPOS", "", "green", true).Print()

return snap, nil
}

// snapshot retrieves the authorization snapshot at a given point in time.
func (c *Consortium) snapshot(chain consensus.ChainHeaderReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) {
// Search for a snapshot in memory or on disk for checkpoints
Expand Down Expand Up @@ -436,7 +490,7 @@ func (c *Consortium) snapshot(chain consensus.ChainHeaderReader, number uint64,
snap = newSnapshot(c.chainConfig, c.config, c.signatures, number, hash, validators, nil, c.ethAPI)

// load v1 recent list to prevent recent producing-block-validators produce block again
snapV1 := c.v1.GetSnapshot(chain, number, parents)
snapV1 := c.v1.GetSnapshot(chain, number, hash, parents)

// NOTE(linh): In version 1, the snapshot is not used correctly, so we must clean up
// incorrect data in the recent list before going to version 2
Expand Down
4 changes: 4 additions & 0 deletions eth/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ import (
"errors"
"math"
"math/big"
"os"
"sync"
"sync/atomic"
"time"

"github.com/ethereum/go-ethereum/common"
v1 "github.com/ethereum/go-ethereum/consensus/consortium/v1"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/forkid"
"github.com/ethereum/go-ethereum/core/types"
Expand Down Expand Up @@ -167,6 +169,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
// In these cases however it's safe to reenable fast sync.
fullBlock, fastBlock := h.chain.CurrentBlock(), h.chain.CurrentFastBlock()
if fullBlock.NumberU64() == 0 && fastBlock.NumberU64() > 0 {
os.Setenv(v1.FastSyncEnv, "1")
h.fastSync = uint32(1)
log.Warn("Switch sync mode from full sync to fast sync")
}
Expand All @@ -176,6 +179,7 @@ func newHandler(config *handlerConfig) (*handler, error) {
log.Warn("Switch sync mode from fast sync to full sync")
} else {
// If fast sync was requested and our database is empty, grant it
os.Setenv(v1.FastSyncEnv, "1")
h.fastSync = uint32(1)
if config.Sync == downloader.SnapSync {
h.snapSync = uint32(1)
Expand Down

0 comments on commit d23228e

Please sign in to comment.