Skip to content

Commit

Permalink
core: deferred verification of stateRoot
Browse files Browse the repository at this point in the history
  • Loading branch information
buddh0 committed Jul 12, 2024
1 parent 7582407 commit 1eab820
Show file tree
Hide file tree
Showing 12 changed files with 336 additions and 101 deletions.
38 changes: 36 additions & 2 deletions consensus/parlia/parlia.go
Original file line number Diff line number Diff line change
Expand Up @@ -619,9 +619,43 @@ func (p *Parlia) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
if header.ParentBeaconRoot != nil {
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", header.ParentBeaconRoot)
}
if header.ParentRoot != nil {
return fmt.Errorf("invalid ParentRoot, have %#x, expected nil", header.ParentRoot)
}
if header.ParentReceiptHash != nil {
return fmt.Errorf("invalid ParentReceiptHash, have %#x, expected nil", header.ParentReceiptHash)
}
if header.ParentBloom != nil {
return fmt.Errorf("invalid ParentBloom, have %#x, expected nil", header.ParentBloom)
}
if header.ParentGasUsed != nil {
return fmt.Errorf("invalid ParentGasUsed, have %#x, expected nil", header.ParentGasUsed)
}
} else {
if header.ParentBeaconRoot == nil || *header.ParentBeaconRoot != (common.Hash{}) {
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected zero hash", header.ParentBeaconRoot)
if header.ParentBeaconRoot == nil {
return fmt.Errorf("invalid parentBeaconRoot, have nil, expected %s", common.Hash{})
} else if *header.ParentBeaconRoot != (common.Hash{}) {
return fmt.Errorf("invalid parentBeaconRoot, have %s, expected %s", *header.ParentBeaconRoot, common.Hash{})
}
if header.ParentRoot == nil {
return fmt.Errorf("invalid ParentRoot, have nil, expected %s", parent.Root)
} else if *header.ParentRoot != parent.Root {
return fmt.Errorf("invalid ParentRoot, have %s, expected %s", *header.ParentRoot, parent.Root)
}
if header.ParentReceiptHash == nil {
return fmt.Errorf("invalid ParentReceiptHash, have nil, expected %s", parent.ReceiptHash)
} else if *header.ParentRoot != parent.Root {
return fmt.Errorf("invalid ParentReceiptHash, have %s, expected %s", *header.ParentReceiptHash, parent.ReceiptHash)
}
if header.ParentBloom == nil {
return fmt.Errorf("invalid ParentBloom, have nil, expected %s", parent.Bloom)
} else if *header.ParentRoot != parent.Root {
return fmt.Errorf("invalid ParentBloom, have %s, expected %s", *header.ParentBloom, parent.Bloom)
}
if header.ParentGasUsed == nil {
return fmt.Errorf("invalid ParentGasUsed, have nil, expected %d", parent.GasUsed)
} else if *header.ParentGasUsed != parent.GasUsed {
return fmt.Errorf("invalid ParentGasUsed, have %d, expected %d", *header.ParentGasUsed, parent.GasUsed)
}
}

Expand Down
100 changes: 84 additions & 16 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import (
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/triedb"
"github.com/ethereum/go-ethereum/triedb/hashdb"
"github.com/ethereum/go-ethereum/triedb/pathdb"
Expand Down Expand Up @@ -259,17 +260,18 @@ type BlockChain struct {
triesInMemory uint64
txIndexer *txIndexer // Transaction indexer, might be nil if not enabled

hc *HeaderChain
rmLogsFeed event.Feed
chainFeed event.Feed
chainSideFeed event.Feed
chainHeadFeed event.Feed
chainBlockFeed event.Feed
logsFeed event.Feed
blockProcFeed event.Feed
finalizedHeaderFeed event.Feed
scope event.SubscriptionScope
genesisBlock *types.Block
hc *HeaderChain
rmLogsFeed event.Feed
chainFeed event.Feed
chainSideFeed event.Feed
chainHeadFeed event.Feed
chainBlockFeed event.Feed
logsFeed event.Feed
blockProcFeed event.Feed
finalizedHeaderFeed event.Feed
highestVerifiedHeaderFeed event.Feed
scope event.SubscriptionScope
genesisBlock *types.Block

// This mutex synchronizes chain write operations.
// Readers don't need to take it, they can just read the database.
Expand Down Expand Up @@ -1962,6 +1964,7 @@ func (bc *BlockChain) writeBlockAndSetHead(block *types.Block, receipts []*types
}
}
if emitHeadEvent {
bc.highestVerifiedHeaderFeed.Send(HighestVerifiedHeaderEvent{Header: block.Header()})
bc.chainHeadFeed.Send(ChainHeadEvent{Block: block})
if finalizedHeader != nil {
bc.finalizedHeaderFeed.Send(FinalizedHeaderEvent{finalizedHeader})
Expand Down Expand Up @@ -2266,11 +2269,21 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool) (int, error)

// Validate the state using the default validator
vstart := time.Now()
if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil {
log.Error("validate state failed", "error", err)
bc.reportBlock(block, receipts, err)
statedb.StopPrefetcher()
return it.index, err
bohr := bc.chainConfig.IsCancun(block.Number(), block.Time())
if !bohr {
if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil {
log.Error("validate state failed", "error", err)
bc.reportBlock(block, receipts, err)
statedb.StopPrefetcher()
return it.index, err
}
} else {
if err := bc.fillStateFields(block, statedb, receipts, usedGas); err != nil {
log.Error("fill state failed", "error", err)
bc.reportBlock(block, receipts, err)
statedb.StopPrefetcher()
return it.index, err
}
}
vtime := time.Since(vstart)
proctime := time.Since(start) // processing + validation
Expand Down Expand Up @@ -2391,6 +2404,7 @@ func (bc *BlockChain) updateHighestVerifiedHeader(header *types.Header) {
reorg, err := bc.forker.ReorgNeededWithFastFinality(currentBlock, header)
if err == nil && reorg {
bc.highestVerifiedHeader.Store(types.CopyHeader(header))
bc.highestVerifiedHeaderFeed.Send(HighestVerifiedHeaderEvent{Header: header})
log.Trace("updateHighestVerifiedHeader", "number", header.Number.Uint64(), "hash", header.Hash())
}
}
Expand Down Expand Up @@ -3253,3 +3267,57 @@ func (bc *BlockChain) SetTrieFlushInterval(interval time.Duration) {
func (bc *BlockChain) GetTrieFlushInterval() time.Duration {
return time.Duration(bc.flushInterval.Load())
}

func (bc *BlockChain) fillStateFields(block *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error {
header := block.Header()
if block.GasUsed() != usedGas {
log.Error("invalid gasUsed", "remote", block.GasUsed(), "local", usedGas)
header.GasUsed = usedGas
}
CheckAndFillFuns := []func() error{
func() error {
rbloom := types.CreateBloom(receipts)
if rbloom != header.Bloom {
log.Error("invalid logsBloom", "remote", header.Bloom, "local", rbloom)
header.Bloom = rbloom
}
return nil
},
func() error {
receiptSha := types.DeriveSha(receipts, trie.NewStackTrie(nil))
if receiptSha != header.ReceiptHash {
log.Error("invalid receiptsRoot", "remote", header.ReceiptHash, "local", receiptSha)
header.ReceiptHash = receiptSha
}
return nil
},
func() error {
if root := statedb.IntermediateRoot(bc.chainConfig.IsEIP158(header.Number)); header.Root != root {
if statedb.Error() != nil {
return fmt.Errorf("invalid stateRoott dberr: %w", statedb.Error())
} else {
log.Error("invalid stateRoot", "remote", header.Root, "local", root)
header.Root = root
}
}
return nil
},
}

CheckAndFillRes := make(chan error, len(CheckAndFillFuns))
for _, f := range CheckAndFillFuns {
tmpFunc := f
go func() {
CheckAndFillRes <- tmpFunc()
}()
}

var err error
for i := 0; i < len(CheckAndFillFuns); i++ {
r := <-CheckAndFillRes
if r != nil && err == nil {
err = r
}
}
return err
}
14 changes: 14 additions & 0 deletions core/blockchain_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ func (bc *BlockChain) GetHeader(hash common.Hash, number uint64) *types.Header {
return bc.hc.GetHeader(hash, number)
}

// GetVerifiedHeaderByHash retrieves a verified block header, it may be only in memory.
func (bc *BlockChain) GetVerifiedHeaderByHash(hash common.Hash) *types.Header {
highestVerifiedHeader := bc.highestVerifiedHeader.Load()
if highestVerifiedHeader != nil && highestVerifiedHeader.Hash() == hash {
return highestVerifiedHeader
}
return bc.hc.GetHeaderByHash(hash)
}

// GetHeaderByHash retrieves a block header from the database by hash, caching it if
// found.
func (bc *BlockChain) GetHeaderByHash(hash common.Hash) *types.Header {
Expand Down Expand Up @@ -486,6 +495,11 @@ func (bc *BlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Su
return bc.scope.Track(bc.chainHeadFeed.Subscribe(ch))
}

// SubscribeHighestVerifiedHeaderEvent registers a subscription of HighestVerifiedHeaderEvent.
func (bc *BlockChain) SubscribeHighestVerifiedHeaderEvent(ch chan<- HighestVerifiedHeaderEvent) event.Subscription {
return bc.scope.Track(bc.highestVerifiedHeaderFeed.Subscribe(ch))
}

// SubscribeChainBlockEvent registers a subscription of ChainBlockEvent.
func (bc *BlockChain) SubscribeChainBlockEvent(ch chan<- ChainHeadEvent) event.Subscription {
return bc.scope.Track(bc.chainBlockFeed.Subscribe(ch))
Expand Down
4 changes: 4 additions & 0 deletions core/chain_makers.go
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,10 @@ func (cm *chainMaker) makeHeader(parent *types.Block, state *state.StateDB, engi
}
if cm.config.Parlia == nil || cm.config.IsBohr(header.Number, header.Time) {
header.ParentBeaconRoot = new(common.Hash)
header.ParentRoot = &parent.Header().Root
header.ParentReceiptHash = &parent.Header().ReceiptHash
header.ParentBloom = &parent.Header().Bloom
header.ParentGasUsed = &parent.Header().GasUsed
}
}
return header
Expand Down
2 changes: 2 additions & 0 deletions core/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,5 @@ type ChainSideEvent struct {
}

type ChainHeadEvent struct{ Block *types.Block }

type HighestVerifiedHeaderEvent struct{ Header *types.Header }
4 changes: 4 additions & 0 deletions core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,10 @@ func (g *Genesis) ToBlock() *types.Block {
// by definition.
if conf.Parlia == nil || conf.IsBohr(num, g.Timestamp) {
head.ParentBeaconRoot = new(common.Hash)
head.ParentRoot = nil
head.ParentReceiptHash = nil
head.ParentBloom = nil
head.ParentGasUsed = nil
}

// EIP-4844 fields
Expand Down
50 changes: 49 additions & 1 deletion core/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
)

Expand Down Expand Up @@ -121,6 +122,11 @@ type Header struct {

// ParentBeaconRoot was added by EIP-4788 and is ignored in legacy headers.
ParentBeaconRoot *common.Hash `json:"parentBeaconBlockRoot" rlp:"optional"`

ParentRoot *common.Hash `json:"parentStateRoot" rlp:"optional"`
ParentReceiptHash *common.Hash `json:"parentReceiptsRoot" rlp:"optional"`
ParentBloom *Bloom `json:"parentLogsBloom" rlp:"optional"`
ParentGasUsed *uint64 `json:"parentGasUsed" rlp:"optional"`
}

// field type overrides for gencodec
Expand All @@ -140,7 +146,7 @@ type headerMarshaling struct {
// Hash returns the block hash of the header, which is simply the keccak256 hash of its
// RLP encoding.
func (h *Header) Hash() common.Hash {
return rlpHash(h)
return rlpForBlockHash(h)
}

var headerSize = common.StorageSize(reflect.TypeOf(Header{}).Size())
Expand Down Expand Up @@ -708,6 +714,10 @@ func EncodeSigHeader(w io.Writer, header *Header, chainId *big.Int) {
header.BlobGasUsed,
header.ExcessBlobGas,
header.ParentBeaconRoot,
header.ParentRoot,
header.ParentReceiptHash,
header.ParentBloom,
header.ParentGasUsed,
})
} else {
err = rlp.Encode(w, []interface{}{
Expand All @@ -733,3 +743,41 @@ func EncodeSigHeader(w io.Writer, header *Header, chainId *big.Int) {
panic("can't encode: " + err.Error())
}
}

func rlpForBlockHash(header *Header) (h common.Hash) {
if header.ParentBeaconRoot != nil && *header.ParentBeaconRoot == (common.Hash{}) {
sha := hasherPool.Get().(crypto.KeccakState)
defer hasherPool.Put(sha)
sha.Reset()
rlp.Encode(sha, []interface{}{
header.ParentHash,
header.UncleHash,
header.Coinbase,
// header.Root,
header.TxHash,
// header.ReceiptHash,
// header.Bloom,
header.Difficulty,
header.Number,
header.GasLimit,
header.GasUsed,
header.Time,
header.Extra,
header.MixDigest,
header.Nonce,
header.BaseFee,
header.WithdrawalsHash,
header.BlobGasUsed,
header.ExcessBlobGas,
header.ParentBeaconRoot,
header.ParentRoot,
header.ParentReceiptHash,
header.ParentBloom,
header.ParentGasUsed,
})
sha.Read(h[:])
return h
} else {
return rlpHash(header)
}
}
Loading

0 comments on commit 1eab820

Please sign in to comment.