mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-25 06:06:44 +00:00
Revert "all: nuke total difficulty (#30744)"
This reverts commit 39638c81c5.
This commit is contained in:
parent
b215cd470f
commit
6805024cad
42 changed files with 637 additions and 132 deletions
|
|
@ -143,7 +143,11 @@ func (c *Chain) ForkID() forkid.ID {
|
||||||
// TD calculates the total difficulty of the chain at the
|
// TD calculates the total difficulty of the chain at the
|
||||||
// chain head.
|
// chain head.
|
||||||
func (c *Chain) TD() *big.Int {
|
func (c *Chain) TD() *big.Int {
|
||||||
return new(big.Int)
|
sum := new(big.Int)
|
||||||
|
for _, block := range c.blocks[:c.Len()] {
|
||||||
|
sum.Add(sum, block.Difficulty())
|
||||||
|
}
|
||||||
|
return sum
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlock returns the block at the specified number.
|
// GetBlock returns the block at the specified number.
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -77,6 +78,7 @@ func (s *Suite) EthTests() []utesting.Test {
|
||||||
{Name: "GetBlockBodies", Fn: s.TestGetBlockBodies},
|
{Name: "GetBlockBodies", Fn: s.TestGetBlockBodies},
|
||||||
// // malicious handshakes + status
|
// // malicious handshakes + status
|
||||||
{Name: "MaliciousHandshake", Fn: s.TestMaliciousHandshake},
|
{Name: "MaliciousHandshake", Fn: s.TestMaliciousHandshake},
|
||||||
|
{Name: "MaliciousStatus", Fn: s.TestMaliciousStatus},
|
||||||
// test transactions
|
// test transactions
|
||||||
{Name: "LargeTxRequest", Fn: s.TestLargeTxRequest, Slow: true},
|
{Name: "LargeTxRequest", Fn: s.TestLargeTxRequest, Slow: true},
|
||||||
{Name: "Transaction", Fn: s.TestTransaction},
|
{Name: "Transaction", Fn: s.TestTransaction},
|
||||||
|
|
@ -469,6 +471,42 @@ func (s *Suite) TestMaliciousHandshake(t *utesting.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Suite) TestMaliciousStatus(t *utesting.T) {
|
||||||
|
t.Log(`This test sends a malicious eth Status message to the node and expects a disconnect.`)
|
||||||
|
|
||||||
|
conn, err := s.dial()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("dial failed: %v", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
if err := conn.handshake(); err != nil {
|
||||||
|
t.Fatalf("handshake failed: %v", err)
|
||||||
|
}
|
||||||
|
// Create status with large total difficulty.
|
||||||
|
status := ð.StatusPacket{
|
||||||
|
ProtocolVersion: uint32(conn.negotiatedProtoVersion),
|
||||||
|
NetworkID: s.chain.config.ChainID.Uint64(),
|
||||||
|
TD: new(big.Int).SetBytes(randBuf(2048)),
|
||||||
|
Head: s.chain.Head().Hash(),
|
||||||
|
Genesis: s.chain.GetBlock(0).Hash(),
|
||||||
|
ForkID: s.chain.ForkID(),
|
||||||
|
}
|
||||||
|
if err := conn.statusExchange(s.chain, status); err != nil {
|
||||||
|
t.Fatalf("status exchange failed: %v", err)
|
||||||
|
}
|
||||||
|
// Wait for disconnect.
|
||||||
|
code, _, err := conn.Read()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error reading from connection: %v", err)
|
||||||
|
}
|
||||||
|
switch code {
|
||||||
|
case discMsg:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
t.Fatalf("expected disconnect, got: %d", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Suite) TestTransaction(t *utesting.T) {
|
func (s *Suite) TestTransaction(t *utesting.T) {
|
||||||
t.Log(`This test sends a valid transaction to the node and checks if the
|
t.Log(`This test sends a valid transaction to the node and checks if the
|
||||||
transaction gets propagated.`)
|
transaction gets propagated.`)
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -455,10 +454,6 @@ func ExportHistory(bc *core.BlockChain, dir string, first, last, step uint64) er
|
||||||
buf = bytes.NewBuffer(nil)
|
buf = bytes.NewBuffer(nil)
|
||||||
checksums []string
|
checksums []string
|
||||||
)
|
)
|
||||||
td := new(big.Int)
|
|
||||||
for i := uint64(0); i < first; i++ {
|
|
||||||
td.Add(td, bc.GetHeaderByNumber(i).Difficulty)
|
|
||||||
}
|
|
||||||
for i := first; i <= last; i += step {
|
for i := first; i <= last; i += step {
|
||||||
err := func() error {
|
err := func() error {
|
||||||
filename := filepath.Join(dir, era.Filename(network, int(i/step), common.Hash{}))
|
filename := filepath.Join(dir, era.Filename(network, int(i/step), common.Hash{}))
|
||||||
|
|
@ -481,8 +476,11 @@ func ExportHistory(bc *core.BlockChain, dir string, first, last, step uint64) er
|
||||||
if receipts == nil {
|
if receipts == nil {
|
||||||
return fmt.Errorf("export failed on #%d: receipts not found", n)
|
return fmt.Errorf("export failed on #%d: receipts not found", n)
|
||||||
}
|
}
|
||||||
td.Add(td, block.Difficulty())
|
td := bc.GetTd(block.Hash(), block.NumberU64())
|
||||||
if err := w.Add(block, receipts, new(big.Int).Set(td)); err != nil {
|
if td == nil {
|
||||||
|
return fmt.Errorf("export failed on #%d: total difficulty not found", n)
|
||||||
|
}
|
||||||
|
if err := w.Add(block, receipts, td); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -93,55 +93,67 @@ func (beacon *Beacon) Author(header *types.Header) (common.Address, error) {
|
||||||
// VerifyHeader checks whether a header conforms to the consensus rules of the
|
// VerifyHeader checks whether a header conforms to the consensus rules of the
|
||||||
// stock Ethereum consensus engine.
|
// stock Ethereum consensus engine.
|
||||||
func (beacon *Beacon) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error {
|
func (beacon *Beacon) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error {
|
||||||
// During the live merge transition, the consensus engine used the terminal
|
reached, err := IsTTDReached(chain, header.ParentHash, header.Number.Uint64()-1)
|
||||||
// total difficulty to detect when PoW (PoA) switched to PoS. Maintaining the
|
if err != nil {
|
||||||
// total difficulty values however require applying all the blocks from the
|
return err
|
||||||
// genesis to build up the TD. This stops being a possibility if the tail of
|
}
|
||||||
// the chain is pruned already during sync.
|
if !reached {
|
||||||
//
|
return beacon.ethone.VerifyHeader(chain, header)
|
||||||
// One heuristic that can be used to distinguish pre-merge and post-merge
|
}
|
||||||
// blocks is whether their *difficulty* is >0 or ==0 respectively. This of
|
// Short circuit if the parent is not known
|
||||||
// course would mean that we cannot prove anymore for a past chain that it
|
|
||||||
// truly transitioned at the correct TTD, but if we consider that ancient
|
|
||||||
// point in time finalized a long time ago, there should be no attempt from
|
|
||||||
// the consensus client to rewrite very old history.
|
|
||||||
//
|
|
||||||
// One thing that's probably not needed but which we can add to make this
|
|
||||||
// verification even stricter is to enforce that the chain can switch from
|
|
||||||
// >0 to ==0 TD only once by forbidding an ==0 to be followed by a >0.
|
|
||||||
|
|
||||||
// Verify that we're not reverting to pre-merge from post-merge
|
|
||||||
parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
|
parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
return consensus.ErrUnknownAncestor
|
return consensus.ErrUnknownAncestor
|
||||||
}
|
}
|
||||||
if parent.Difficulty.Sign() == 0 && header.Difficulty.Sign() > 0 {
|
// Sanity checks passed, do a proper verification
|
||||||
return consensus.ErrInvalidTerminalBlock
|
|
||||||
}
|
|
||||||
// Check >0 TDs with pre-merge, --0 TDs with post-merge rules
|
|
||||||
if header.Difficulty.Sign() > 0 {
|
|
||||||
return beacon.ethone.VerifyHeader(chain, header)
|
|
||||||
}
|
|
||||||
return beacon.verifyHeader(chain, header, parent)
|
return beacon.verifyHeader(chain, header, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// errOut constructs an error channel with prefilled errors inside.
|
||||||
|
func errOut(n int, err error) chan error {
|
||||||
|
errs := make(chan error, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
errs <- err
|
||||||
|
}
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
// splitHeaders splits the provided header batch into two parts according to
|
// splitHeaders splits the provided header batch into two parts according to
|
||||||
// the difficulty field.
|
// the configured ttd. It requires the parent of header batch along with its
|
||||||
//
|
// td are stored correctly in chain. If ttd is not configured yet, all headers
|
||||||
|
// will be treated legacy PoW headers.
|
||||||
// Note, this function will not verify the header validity but just split them.
|
// Note, this function will not verify the header validity but just split them.
|
||||||
func (beacon *Beacon) splitHeaders(headers []*types.Header) ([]*types.Header, []*types.Header) {
|
func (beacon *Beacon) splitHeaders(chain consensus.ChainHeaderReader, headers []*types.Header) ([]*types.Header, []*types.Header, error) {
|
||||||
|
// TTD is not defined yet, all headers should be in legacy format.
|
||||||
|
ttd := chain.Config().TerminalTotalDifficulty
|
||||||
|
ptd := chain.GetTd(headers[0].ParentHash, headers[0].Number.Uint64()-1)
|
||||||
|
if ptd == nil {
|
||||||
|
return nil, nil, consensus.ErrUnknownAncestor
|
||||||
|
}
|
||||||
|
// The entire header batch already crosses the transition.
|
||||||
|
if ptd.Cmp(ttd) >= 0 {
|
||||||
|
return nil, headers, nil
|
||||||
|
}
|
||||||
var (
|
var (
|
||||||
preHeaders = headers
|
preHeaders = headers
|
||||||
postHeaders []*types.Header
|
postHeaders []*types.Header
|
||||||
|
td = new(big.Int).Set(ptd)
|
||||||
|
tdPassed bool
|
||||||
)
|
)
|
||||||
for i, header := range headers {
|
for i, header := range headers {
|
||||||
if header.Difficulty.Sign() == 0 {
|
if tdPassed {
|
||||||
preHeaders = headers[:i]
|
preHeaders = headers[:i]
|
||||||
postHeaders = headers[i:]
|
postHeaders = headers[i:]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
td = td.Add(td, header.Difficulty)
|
||||||
|
if td.Cmp(ttd) >= 0 {
|
||||||
|
// This is the last PoW header, it still belongs to
|
||||||
|
// the preHeaders, so we cannot split+break yet.
|
||||||
|
tdPassed = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return preHeaders, postHeaders
|
return preHeaders, postHeaders, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
|
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
|
||||||
|
|
@ -149,7 +161,10 @@ func (beacon *Beacon) splitHeaders(headers []*types.Header) ([]*types.Header, []
|
||||||
// a results channel to retrieve the async verifications.
|
// a results channel to retrieve the async verifications.
|
||||||
// VerifyHeaders expect the headers to be ordered and continuous.
|
// VerifyHeaders expect the headers to be ordered and continuous.
|
||||||
func (beacon *Beacon) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header) (chan<- struct{}, <-chan error) {
|
func (beacon *Beacon) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header) (chan<- struct{}, <-chan error) {
|
||||||
preHeaders, postHeaders := beacon.splitHeaders(headers)
|
preHeaders, postHeaders, err := beacon.splitHeaders(chain, headers)
|
||||||
|
if err != nil {
|
||||||
|
return make(chan struct{}), errOut(len(headers), err)
|
||||||
|
}
|
||||||
if len(postHeaders) == 0 {
|
if len(postHeaders) == 0 {
|
||||||
return beacon.ethone.VerifyHeaders(chain, headers)
|
return beacon.ethone.VerifyHeaders(chain, headers)
|
||||||
}
|
}
|
||||||
|
|
@ -329,7 +344,12 @@ func (beacon *Beacon) verifyHeaders(chain consensus.ChainHeaderReader, headers [
|
||||||
// Prepare implements consensus.Engine, initializing the difficulty field of a
|
// Prepare implements consensus.Engine, initializing the difficulty field of a
|
||||||
// header to conform to the beacon protocol. The changes are done inline.
|
// header to conform to the beacon protocol. The changes are done inline.
|
||||||
func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
|
func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
|
||||||
if !isPostMerge(chain.Config(), header.Number.Uint64(), header.Time) {
|
// Transition isn't triggered yet, use the legacy rules for preparation.
|
||||||
|
reached, err := IsTTDReached(chain, header.ParentHash, header.Number.Uint64()-1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !reached {
|
||||||
return beacon.ethone.Prepare(chain, header)
|
return beacon.ethone.Prepare(chain, header)
|
||||||
}
|
}
|
||||||
header.Difficulty = beaconDifficulty
|
header.Difficulty = beaconDifficulty
|
||||||
|
|
@ -440,7 +460,8 @@ func (beacon *Beacon) SealHash(header *types.Header) common.Hash {
|
||||||
// the difficulty that a new block should have when created at time
|
// the difficulty that a new block should have when created at time
|
||||||
// given the parent block's time and difficulty.
|
// given the parent block's time and difficulty.
|
||||||
func (beacon *Beacon) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
|
func (beacon *Beacon) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
|
||||||
if !isPostMerge(chain.Config(), parent.Number.Uint64()+1, time) {
|
// Transition isn't triggered yet, use the legacy rules for calculation
|
||||||
|
if reached, _ := IsTTDReached(chain, parent.Hash(), parent.Number.Uint64()); !reached {
|
||||||
return beacon.ethone.CalcDifficulty(chain, time, parent)
|
return beacon.ethone.CalcDifficulty(chain, time, parent)
|
||||||
}
|
}
|
||||||
return beaconDifficulty
|
return beaconDifficulty
|
||||||
|
|
@ -481,3 +502,14 @@ func (beacon *Beacon) SetThreads(threads int) {
|
||||||
th.SetThreads(threads)
|
th.SetThreads(threads)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsTTDReached checks if the TotalTerminalDifficulty has been surpassed on the `parentHash` block.
|
||||||
|
// It depends on the parentHash already being stored in the database.
|
||||||
|
// If the parentHash is not stored in the database a UnknownAncestor error is returned.
|
||||||
|
func IsTTDReached(chain consensus.ChainHeaderReader, parentHash common.Hash, parentNumber uint64) (bool, error) {
|
||||||
|
td := chain.GetTd(parentHash, parentNumber)
|
||||||
|
if td == nil {
|
||||||
|
return false, consensus.ErrUnknownAncestor
|
||||||
|
}
|
||||||
|
return td.Cmp(chain.Config().TerminalTotalDifficulty) >= 0, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
41
consensus/beacon/faker.go
Normal file
41
consensus/beacon/faker.go
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright 2023 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package beacon
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewFaker creates a fake consensus engine for testing.
|
||||||
|
// The fake engine simulates a merged network.
|
||||||
|
// It can not be used to test the merge transition.
|
||||||
|
// This type is needed since the fakeChainReader can not be used with
|
||||||
|
// a normal beacon consensus engine.
|
||||||
|
func NewFaker() consensus.Engine {
|
||||||
|
return new(faker)
|
||||||
|
}
|
||||||
|
|
||||||
|
type faker struct {
|
||||||
|
Beacon
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *faker) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
|
||||||
|
return beaconDifficulty
|
||||||
|
}
|
||||||
|
|
@ -45,6 +45,9 @@ type ChainHeaderReader interface {
|
||||||
|
|
||||||
// GetHeaderByHash retrieves a block header from the database by its hash.
|
// GetHeaderByHash retrieves a block header from the database by its hash.
|
||||||
GetHeaderByHash(hash common.Hash) *types.Header
|
GetHeaderByHash(hash common.Hash) *types.Header
|
||||||
|
|
||||||
|
// GetTd retrieves the total difficulty from the database by hash and number.
|
||||||
|
GetTd(hash common.Hash, number uint64) *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChainReader defines a small collection of methods needed to access the local
|
// ChainReader defines a small collection of methods needed to access the local
|
||||||
|
|
|
||||||
|
|
@ -295,6 +295,7 @@ func makeChainForBench(db ethdb.Database, genesis *Genesis, full bool, count uin
|
||||||
|
|
||||||
rawdb.WriteHeader(db, header)
|
rawdb.WriteHeader(db, header)
|
||||||
rawdb.WriteCanonicalHash(db, hash, n)
|
rawdb.WriteCanonicalHash(db, hash, n)
|
||||||
|
rawdb.WriteTd(db, hash, n, big.NewInt(int64(n+1)))
|
||||||
|
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
rawdb.WriteChainConfig(db, hash, genesis.Config)
|
rawdb.WriteChainConfig(db, hash, genesis.Config)
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ const (
|
||||||
//
|
//
|
||||||
// - Version 9
|
// - Version 9
|
||||||
// The following incompatible database changes were added:
|
// The following incompatible database changes were added:
|
||||||
// * Total difficulty has been removed from both the key-value store and the ancient store.
|
// * (not applicable for bor) Total difficulty has been removed from both the key-value store and the ancient store.
|
||||||
// * The metadata structure of freezer is changed by adding 'flushOffset'
|
// * The metadata structure of freezer is changed by adding 'flushOffset'
|
||||||
BlockChainVersion uint64 = 9
|
BlockChainVersion uint64 = 9
|
||||||
)
|
)
|
||||||
|
|
@ -787,18 +787,23 @@ func (bc *BlockChain) loadLastState() error {
|
||||||
var (
|
var (
|
||||||
currentSnapBlock = bc.CurrentSnapBlock()
|
currentSnapBlock = bc.CurrentSnapBlock()
|
||||||
currentFinalBlock = bc.CurrentFinalBlock()
|
currentFinalBlock = bc.CurrentFinalBlock()
|
||||||
|
|
||||||
|
headerTd = bc.GetTd(headHeader.Hash(), headHeader.Number.Uint64())
|
||||||
|
blockTd = bc.GetTd(headBlock.Hash(), headBlock.NumberU64())
|
||||||
)
|
)
|
||||||
|
|
||||||
if headHeader.Hash() != headBlock.Hash() {
|
if headHeader.Hash() != headBlock.Hash() {
|
||||||
log.Info("Loaded most recent local header", "number", headHeader.Number, "hash", headHeader.Hash(), "age", common.PrettyAge(time.Unix(int64(headHeader.Time), 0)))
|
log.Info("Loaded most recent local header", "number", headHeader.Number, "hash", headHeader.Hash(), "td", headerTd, "age", common.PrettyAge(time.Unix(int64(headHeader.Time), 0)))
|
||||||
}
|
}
|
||||||
log.Info("Loaded most recent local block", "number", headBlock.Number(), "hash", headBlock.Hash(), "age", common.PrettyAge(time.Unix(int64(headBlock.Time()), 0)))
|
log.Info("Loaded most recent local block", "number", headBlock.Number(), "hash", headBlock.Hash(), "td", blockTd, "age", common.PrettyAge(time.Unix(int64(headBlock.Time()), 0)))
|
||||||
if headBlock.Hash() != currentSnapBlock.Hash() {
|
if headBlock.Hash() != currentSnapBlock.Hash() {
|
||||||
log.Info("Loaded most recent local snap block", "number", currentSnapBlock.Number, "hash", currentSnapBlock.Hash(), "age", common.PrettyAge(time.Unix(int64(currentSnapBlock.Time), 0)))
|
snapTd := bc.GetTd(currentSnapBlock.Hash(), currentSnapBlock.Number.Uint64())
|
||||||
|
log.Info("Loaded most recent local snap block", "number", currentSnapBlock.Number, "hash", currentSnapBlock.Hash(), "td", snapTd, "age", common.PrettyAge(time.Unix(int64(currentSnapBlock.Time), 0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if currentFinalBlock != nil {
|
if currentFinalBlock != nil {
|
||||||
log.Info("Loaded most recent local finalized block", "number", currentFinalBlock.Number, "hash", currentFinalBlock.Hash(), "age", common.PrettyAge(time.Unix(int64(currentFinalBlock.Time), 0)))
|
finalTd := bc.GetTd(currentFinalBlock.Hash(), currentFinalBlock.Number.Uint64())
|
||||||
|
log.Info("Loaded most recent local finalized block", "number", currentFinalBlock.Number, "hash", currentFinalBlock.Hash(), "td", finalTd, "age", common.PrettyAge(time.Unix(int64(currentFinalBlock.Time), 0)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if pivot := rawdb.ReadLastPivotNumber(bc.db); pivot != nil {
|
if pivot := rawdb.ReadLastPivotNumber(bc.db); pivot != nil {
|
||||||
|
|
@ -1248,6 +1253,7 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
|
||||||
|
|
||||||
// Prepare the genesis block and reinitialise the chain
|
// Prepare the genesis block and reinitialise the chain
|
||||||
batch := bc.db.NewBatch()
|
batch := bc.db.NewBatch()
|
||||||
|
rawdb.WriteTd(batch, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty())
|
||||||
rawdb.WriteBlock(batch, genesis)
|
rawdb.WriteBlock(batch, genesis)
|
||||||
|
|
||||||
if err := batch.Write(); err != nil {
|
if err := batch.Write(); err != nil {
|
||||||
|
|
@ -1550,7 +1556,8 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
// Ensure genesis is in ancients.
|
// Ensure genesis is in ancients.
|
||||||
if first.NumberU64() == 1 {
|
if first.NumberU64() == 1 {
|
||||||
if frozen, _ := bc.db.Ancients(); frozen == 0 {
|
if frozen, _ := bc.db.Ancients(); frozen == 0 {
|
||||||
writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []types.Receipts{nil})
|
td := bc.genesisBlock.Difficulty()
|
||||||
|
writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{bc.genesisBlock}, []types.Receipts{nil}, td)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error writing genesis to ancients", "err", err)
|
log.Error("Error writing genesis to ancients", "err", err)
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
@ -1579,7 +1586,9 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
headers = append(headers, block.Header())
|
headers = append(headers, block.Header())
|
||||||
}
|
}
|
||||||
|
|
||||||
writeSize, err := rawdb.WriteAncientBlocks(bc.db, blockChain, receiptChain)
|
// Write all chain data to ancients.
|
||||||
|
td := bc.GetTd(first.Hash(), first.NumberU64())
|
||||||
|
writeSize, err := rawdb.WriteAncientBlocks(bc.db, blockChain, receiptChain, td)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error importing chain data to ancients", "err", err)
|
log.Error("Error importing chain data to ancients", "err", err)
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|
@ -1776,11 +1785,12 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
|
||||||
// writeBlockWithoutState writes only the block and its metadata to the database,
|
// writeBlockWithoutState writes only the block and its metadata to the database,
|
||||||
// but does not write any state. This is used to construct competing side forks
|
// but does not write any state. This is used to construct competing side forks
|
||||||
// up to the point where they exceed the canonical total difficulty.
|
// up to the point where they exceed the canonical total difficulty.
|
||||||
func (bc *BlockChain) writeBlockWithoutState(block *types.Block) (err error) {
|
func (bc *BlockChain) writeBlockWithoutState(block *types.Block, td *big.Int) (err error) {
|
||||||
if bc.insertStopped() {
|
if bc.insertStopped() {
|
||||||
return errInsertionInterrupted
|
return errInsertionInterrupted
|
||||||
}
|
}
|
||||||
batch := bc.db.NewBatch()
|
batch := bc.db.NewBatch()
|
||||||
|
rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), td)
|
||||||
rawdb.WriteBlock(batch, block)
|
rawdb.WriteBlock(batch, block)
|
||||||
|
|
||||||
if err := batch.Write(); err != nil {
|
if err := batch.Write(); err != nil {
|
||||||
|
|
@ -1808,14 +1818,20 @@ func (bc *BlockChain) writeKnownBlock(block *types.Block) error {
|
||||||
// writeBlockWithState writes block, metadata and corresponding state data to the
|
// writeBlockWithState writes block, metadata and corresponding state data to the
|
||||||
// database.
|
// database.
|
||||||
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, statedb *state.StateDB) ([]*types.Log, error) {
|
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, statedb *state.StateDB) ([]*types.Log, error) {
|
||||||
if !bc.HasHeader(block.ParentHash(), block.NumberU64()-1) {
|
// Calculate the total difficulty of the block
|
||||||
|
ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
|
||||||
|
if ptd == nil {
|
||||||
return consensus.ErrUnknownAncestor
|
return consensus.ErrUnknownAncestor
|
||||||
}
|
}
|
||||||
|
// Make sure no inconsistent state is leaked during insertion
|
||||||
|
externTd := new(big.Int).Add(block.Difficulty(), ptd)
|
||||||
|
|
||||||
// Irrelevant of the canonical status, write the block itself to the database.
|
// Irrelevant of the canonical status, write the block itself to the database.
|
||||||
//
|
//
|
||||||
// Note all the components of block(hash->number map, header, body, receipts)
|
// Note all the components of block(td, hash->number map, header, body, receipts)
|
||||||
// should be written atomically. BlockBatch is used for containing all components.
|
// should be written atomically. BlockBatch is used for containing all components.
|
||||||
blockBatch := bc.db.NewBatch()
|
blockBatch := bc.db.NewBatch()
|
||||||
|
rawdb.WriteTd(blockBatch, block.Hash(), block.NumberU64(), externTd)
|
||||||
rawdb.WriteBlock(blockBatch, block)
|
rawdb.WriteBlock(blockBatch, block)
|
||||||
rawdb.WriteReceipts(blockBatch, block.Hash(), block.NumberU64(), receipts)
|
rawdb.WriteReceipts(blockBatch, block.Hash(), block.NumberU64(), receipts)
|
||||||
|
|
||||||
|
|
@ -2312,6 +2328,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, setHead bool, makeWitness
|
||||||
if bc.logger != nil && bc.logger.OnSkippedBlock != nil {
|
if bc.logger != nil && bc.logger.OnSkippedBlock != nil {
|
||||||
bc.logger.OnSkippedBlock(tracing.BlockEvent{
|
bc.logger.OnSkippedBlock(tracing.BlockEvent{
|
||||||
Block: block,
|
Block: block,
|
||||||
|
TD: bc.GetTd(block.ParentHash(), block.NumberU64()-1),
|
||||||
Finalized: bc.CurrentFinalBlock(),
|
Finalized: bc.CurrentFinalBlock(),
|
||||||
Safe: bc.CurrentSafeBlock(),
|
Safe: bc.CurrentSafeBlock(),
|
||||||
})
|
})
|
||||||
|
|
@ -2547,8 +2564,10 @@ type blockProcessingResult struct {
|
||||||
// nolint : unused
|
// nolint : unused
|
||||||
func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, start time.Time, setHead bool) (_ *blockProcessingResult, blockEndErr error) {
|
func (bc *BlockChain) processBlock(block *types.Block, statedb *state.StateDB, start time.Time, setHead bool) (_ *blockProcessingResult, blockEndErr error) {
|
||||||
if bc.logger != nil && bc.logger.OnBlockStart != nil {
|
if bc.logger != nil && bc.logger.OnBlockStart != nil {
|
||||||
|
td := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
|
||||||
bc.logger.OnBlockStart(tracing.BlockEvent{
|
bc.logger.OnBlockStart(tracing.BlockEvent{
|
||||||
Block: block,
|
Block: block,
|
||||||
|
TD: td,
|
||||||
Finalized: bc.CurrentFinalBlock(),
|
Finalized: bc.CurrentFinalBlock(),
|
||||||
Safe: bc.CurrentSafeBlock(),
|
Safe: bc.CurrentSafeBlock(),
|
||||||
})
|
})
|
||||||
|
|
@ -2662,6 +2681,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
|
||||||
lastBlock = block
|
lastBlock = block
|
||||||
current = bc.CurrentBlock()
|
current = bc.CurrentBlock()
|
||||||
headers []*types.Header
|
headers []*types.Header
|
||||||
|
externTd *big.Int
|
||||||
)
|
)
|
||||||
|
|
||||||
// The first sidechain block error is already verified to be ErrPrunedAncestor.
|
// The first sidechain block error is already verified to be ErrPrunedAncestor.
|
||||||
|
|
@ -2676,6 +2696,11 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
|
||||||
canonical := bc.GetBlockByNumber(number)
|
canonical := bc.GetBlockByNumber(number)
|
||||||
if canonical != nil && canonical.Hash() == block.Hash() {
|
if canonical != nil && canonical.Hash() == block.Hash() {
|
||||||
// Not a sidechain block, this is a re-import of a canon block which has it's state pruned
|
// Not a sidechain block, this is a re-import of a canon block which has it's state pruned
|
||||||
|
|
||||||
|
// Collect the TD of the block. Since we know it's a canon one,
|
||||||
|
// we can get it directly, and not (like further below) use
|
||||||
|
// the parent and then add the block on top
|
||||||
|
externTd = bc.GetTd(block.Hash(), block.NumberU64())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2695,9 +2720,14 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator, ma
|
||||||
return nil, it.index, errors.New("sidechain ghost-state attack")
|
return nil, it.index, errors.New("sidechain ghost-state attack")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if externTd == nil {
|
||||||
|
externTd = bc.GetTd(block.ParentHash(), block.NumberU64()-1)
|
||||||
|
}
|
||||||
|
externTd = new(big.Int).Add(externTd, block.Difficulty())
|
||||||
|
|
||||||
if !bc.HasBlock(block.Hash(), block.NumberU64()) {
|
if !bc.HasBlock(block.Hash(), block.NumberU64()) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
if err := bc.writeBlockWithoutState(block); err != nil {
|
if err := bc.writeBlockWithoutState(block, externTd); err != nil {
|
||||||
return nil, it.index, err
|
return nil, it.index, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/consensus"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
|
|
@ -341,6 +342,12 @@ func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLoo
|
||||||
return lookup, tx, nil
|
return lookup, tx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTd retrieves a block's total difficulty in the canonical chain from the
|
||||||
|
// database by hash and number, caching it if found.
|
||||||
|
func (bc *BlockChain) GetTd(hash common.Hash, number uint64) *big.Int {
|
||||||
|
return bc.hc.GetTd(hash, number)
|
||||||
|
}
|
||||||
|
|
||||||
// HasState checks if state trie is fully present in the database or not.
|
// HasState checks if state trie is fully present in the database or not.
|
||||||
func (bc *BlockChain) HasState(hash common.Hash) bool {
|
func (bc *BlockChain) HasState(hash common.Hash) bool {
|
||||||
_, err := bc.statedb.OpenTrie(hash)
|
_, err := bc.statedb.OpenTrie(hash)
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ func newGwei(n int64) *big.Int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test fork of length N starting from block i
|
// Test fork of length N starting from block i
|
||||||
func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, scheme string) {
|
func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, comparator func(td1, td2 *big.Int), scheme string) {
|
||||||
// Copy old chain up to #i into a new db
|
// Copy old chain up to #i into a new db
|
||||||
genDb, _, blockchain2, err := newCanonical(ethash.NewFaker(), i, full, scheme)
|
genDb, _, blockchain2, err := newCanonical(ethash.NewFaker(), i, full, scheme)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -130,15 +130,27 @@ func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, scheme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Sanity check that the forked chain can be imported into the original
|
// Sanity check that the forked chain can be imported into the original
|
||||||
|
var tdPre, tdPost *big.Int
|
||||||
|
|
||||||
if full {
|
if full {
|
||||||
|
cur := blockchain.CurrentBlock()
|
||||||
|
tdPre = blockchain.GetTd(cur.Hash(), cur.Number.Uint64())
|
||||||
if err := testBlockChainImport(blockChainB, blockchain); err != nil {
|
if err := testBlockChainImport(blockChainB, blockchain); err != nil {
|
||||||
t.Fatalf("failed to import forked block chain: %v", err)
|
t.Fatalf("failed to import forked block chain: %v", err)
|
||||||
}
|
}
|
||||||
|
last := blockChainB[len(blockChainB)-1]
|
||||||
|
tdPost = blockchain.GetTd(last.Hash(), last.NumberU64())
|
||||||
} else {
|
} else {
|
||||||
|
cur := blockchain.CurrentHeader()
|
||||||
|
tdPre = blockchain.GetTd(cur.Hash(), cur.Number.Uint64())
|
||||||
if err := testHeaderChainImport(headerChainB, blockchain); err != nil {
|
if err := testHeaderChainImport(headerChainB, blockchain); err != nil {
|
||||||
t.Fatalf("failed to import forked header chain: %v", err)
|
t.Fatalf("failed to import forked header chain: %v", err)
|
||||||
}
|
}
|
||||||
|
last := headerChainB[len(headerChainB)-1]
|
||||||
|
tdPost = blockchain.GetTd(last.Hash(), last.Number.Uint64())
|
||||||
}
|
}
|
||||||
|
// Compare the total difficulties of the chains
|
||||||
|
comparator(tdPre, tdPost)
|
||||||
}
|
}
|
||||||
|
|
||||||
// testBlockChainImport tries to process a chain of blocks, writing them into
|
// testBlockChainImport tries to process a chain of blocks, writing them into
|
||||||
|
|
@ -179,6 +191,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
blockchain.chainmu.MustLock()
|
blockchain.chainmu.MustLock()
|
||||||
|
rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTd(block.ParentHash(), block.NumberU64()-1)))
|
||||||
rawdb.WriteBlock(blockchain.db, block)
|
rawdb.WriteBlock(blockchain.db, block)
|
||||||
statedb.Commit(block.NumberU64(), false, false)
|
statedb.Commit(block.NumberU64(), false, false)
|
||||||
blockchain.chainmu.Unlock()
|
blockchain.chainmu.Unlock()
|
||||||
|
|
@ -296,6 +309,7 @@ func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error
|
||||||
}
|
}
|
||||||
// Manually insert the header into the database, but don't reorganise (allows subsequent testing)
|
// Manually insert the header into the database, but don't reorganise (allows subsequent testing)
|
||||||
blockchain.chainmu.MustLock()
|
blockchain.chainmu.MustLock()
|
||||||
|
rawdb.WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTd(header.ParentHash, header.Number.Uint64()-1)))
|
||||||
rawdb.WriteHeader(blockchain.db, header)
|
rawdb.WriteHeader(blockchain.db, header)
|
||||||
blockchain.chainmu.Unlock()
|
blockchain.chainmu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
@ -399,11 +413,17 @@ func testExtendCanonical(t *testing.T, full bool, scheme string) {
|
||||||
}
|
}
|
||||||
defer processor.Stop()
|
defer processor.Stop()
|
||||||
|
|
||||||
|
// Define the difficulty comparator
|
||||||
|
better := func(td1, td2 *big.Int) {
|
||||||
|
if td2.Cmp(td1) <= 0 {
|
||||||
|
t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1)
|
||||||
|
}
|
||||||
|
}
|
||||||
// Start fork from current height
|
// Start fork from current height
|
||||||
testFork(t, processor, length, 1, full, scheme)
|
testFork(t, processor, length, 1, full, better, scheme)
|
||||||
testFork(t, processor, length, 2, full, scheme)
|
testFork(t, processor, length, 2, full, better, scheme)
|
||||||
testFork(t, processor, length, 5, full, scheme)
|
testFork(t, processor, length, 5, full, better, scheme)
|
||||||
testFork(t, processor, length, 10, full, scheme)
|
testFork(t, processor, length, 10, full, better, scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that given a starting canonical chain of a given size, it can be extended
|
// Tests that given a starting canonical chain of a given size, it can be extended
|
||||||
|
|
@ -452,13 +472,19 @@ func testShorterFork(t *testing.T, full bool, scheme string) {
|
||||||
}
|
}
|
||||||
defer processor.Stop()
|
defer processor.Stop()
|
||||||
|
|
||||||
|
// Define the difficulty comparator
|
||||||
|
worse := func(td1, td2 *big.Int) {
|
||||||
|
if td2.Cmp(td1) >= 0 {
|
||||||
|
t.Errorf("total difficulty mismatch: have %v, expected less than %v", td2, td1)
|
||||||
|
}
|
||||||
|
}
|
||||||
// Sum of numbers must be less than `length` for this to be a shorter fork
|
// Sum of numbers must be less than `length` for this to be a shorter fork
|
||||||
testFork(t, processor, 0, 3, full, scheme)
|
testFork(t, processor, 0, 3, full, worse, scheme)
|
||||||
testFork(t, processor, 0, 7, full, scheme)
|
testFork(t, processor, 0, 7, full, worse, scheme)
|
||||||
testFork(t, processor, 1, 1, full, scheme)
|
testFork(t, processor, 1, 1, full, worse, scheme)
|
||||||
testFork(t, processor, 1, 7, full, scheme)
|
testFork(t, processor, 1, 7, full, worse, scheme)
|
||||||
testFork(t, processor, 5, 3, full, scheme)
|
testFork(t, processor, 5, 3, full, worse, scheme)
|
||||||
testFork(t, processor, 5, 4, full, scheme)
|
testFork(t, processor, 5, 4, full, worse, scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that given a starting canonical chain of a given size, creating shorter
|
// Tests that given a starting canonical chain of a given size, creating shorter
|
||||||
|
|
@ -569,13 +595,19 @@ func testEqualFork(t *testing.T, full bool, scheme string) {
|
||||||
}
|
}
|
||||||
defer processor.Stop()
|
defer processor.Stop()
|
||||||
|
|
||||||
|
// Define the difficulty comparator
|
||||||
|
equal := func(td1, td2 *big.Int) {
|
||||||
|
if td2.Cmp(td1) != 0 {
|
||||||
|
t.Errorf("total difficulty mismatch: have %v, want %v", td2, td1)
|
||||||
|
}
|
||||||
|
}
|
||||||
// Sum of numbers must be equal to `length` for this to be an equal fork
|
// Sum of numbers must be equal to `length` for this to be an equal fork
|
||||||
testFork(t, processor, 0, 10, full, scheme)
|
testFork(t, processor, 0, 10, full, equal, scheme)
|
||||||
testFork(t, processor, 1, 9, full, scheme)
|
testFork(t, processor, 1, 9, full, equal, scheme)
|
||||||
testFork(t, processor, 2, 8, full, scheme)
|
testFork(t, processor, 2, 8, full, equal, scheme)
|
||||||
testFork(t, processor, 5, 5, full, scheme)
|
testFork(t, processor, 5, 5, full, equal, scheme)
|
||||||
testFork(t, processor, 6, 4, full, scheme)
|
testFork(t, processor, 6, 4, full, equal, scheme)
|
||||||
testFork(t, processor, 9, 1, full, scheme)
|
testFork(t, processor, 9, 1, full, equal, scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that given a starting canonical chain of a given size, creating equal
|
// Tests that given a starting canonical chain of a given size, creating equal
|
||||||
|
|
@ -738,6 +770,19 @@ func testReorg(t *testing.T, first, second []int64, td int64, full bool, scheme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Make sure the chain total difficulty is the correct one
|
||||||
|
want := new(big.Int).Add(blockchain.genesisBlock.Difficulty(), big.NewInt(td))
|
||||||
|
if full {
|
||||||
|
cur := blockchain.CurrentBlock()
|
||||||
|
if have := blockchain.GetTd(cur.Hash(), cur.Number.Uint64()); have.Cmp(want) != 0 {
|
||||||
|
t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cur := blockchain.CurrentHeader()
|
||||||
|
if have := blockchain.GetTd(cur.Hash(), cur.Number.Uint64()); have.Cmp(want) != 0 {
|
||||||
|
t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests chain insertions in the face of one entity containing an invalid nonce.
|
// Tests chain insertions in the face of one entity containing an invalid nonce.
|
||||||
|
|
@ -893,6 +938,12 @@ func testFastVsFullChains(t *testing.T, scheme string) {
|
||||||
for i := 0; i < len(blocks); i++ {
|
for i := 0; i < len(blocks); i++ {
|
||||||
num, hash, time := blocks[i].NumberU64(), blocks[i].Hash(), blocks[i].Time()
|
num, hash, time := blocks[i].NumberU64(), blocks[i].Hash(), blocks[i].Time()
|
||||||
|
|
||||||
|
if ftd, atd := fast.GetTd(hash, num), archive.GetTd(hash, num); ftd.Cmp(atd) != 0 {
|
||||||
|
t.Errorf("block #%d [%x]: td mismatch: fastdb %v, archivedb %v", num, hash, ftd, atd)
|
||||||
|
}
|
||||||
|
if antd, artd := ancient.GetTd(hash, num), archive.GetTd(hash, num); antd.Cmp(artd) != 0 {
|
||||||
|
t.Errorf("block #%d [%x]: td mismatch: ancientdb %v, archivedb %v", num, hash, antd, artd)
|
||||||
|
}
|
||||||
if fheader, aheader := fast.GetHeaderByHash(hash), archive.GetHeaderByHash(hash); fheader.Hash() != aheader.Hash() {
|
if fheader, aheader := fast.GetHeaderByHash(hash), archive.GetHeaderByHash(hash); fheader.Hash() != aheader.Hash() {
|
||||||
t.Errorf("block #%d [%x]: header mismatch: fastdb %v, archivedb %v", num, hash, fheader, aheader)
|
t.Errorf("block #%d [%x]: header mismatch: fastdb %v, archivedb %v", num, hash, fheader, aheader)
|
||||||
}
|
}
|
||||||
|
|
@ -4465,7 +4516,7 @@ func TestEIP3651(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa")
|
||||||
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
bb = common.HexToAddress("0x000000000000000000000000000000000000bbbb")
|
||||||
engine = beacon.New(ethash.NewFaker())
|
engine = beacon.NewFaker()
|
||||||
|
|
||||||
// A sender who makes transactions, has some funds
|
// A sender who makes transactions, has some funds
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
|
@ -4585,7 +4636,7 @@ func TestPragueRequests(t *testing.T) {
|
||||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
config = *params.MergedTestChainConfig
|
config = *params.MergedTestChainConfig
|
||||||
signer = types.LatestSigner(&config)
|
signer = types.LatestSigner(&config)
|
||||||
engine = beacon.New(ethash.NewFaker())
|
engine = beacon.NewFaker()
|
||||||
)
|
)
|
||||||
gspec := &Genesis{
|
gspec := &Genesis{
|
||||||
Config: &config,
|
Config: &config,
|
||||||
|
|
@ -4663,7 +4714,7 @@ func TestEIP7702(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
config = *params.MergedTestChainConfig
|
config = *params.MergedTestChainConfig
|
||||||
signer = types.LatestSigner(&config)
|
signer = types.LatestSigner(&config)
|
||||||
engine = beacon.New(ethash.NewFaker())
|
engine = beacon.NewFaker()
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
|
|
|
||||||
|
|
@ -764,3 +764,7 @@ func (cm *chainMaker) GetHeader(hash common.Hash, number uint64) *types.Header {
|
||||||
func (cm *chainMaker) GetBlock(hash common.Hash, number uint64) *types.Block {
|
func (cm *chainMaker) GetBlock(hash common.Hash, number uint64) *types.Block {
|
||||||
return cm.blockByNumber(number)
|
return cm.blockByNumber(number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cm *chainMaker) GetTd(hash common.Hash, number uint64) *big.Int {
|
||||||
|
return nil // not supported
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,9 +76,8 @@ func TestGeneratePOSChain(t *testing.T) {
|
||||||
Code: common.Hex2Bytes("600154600354"),
|
Code: common.Hex2Bytes("600154600354"),
|
||||||
}
|
}
|
||||||
genesis := gspec.MustCommit(gendb, triedb.NewDatabase(gendb, triedb.HashDefaults))
|
genesis := gspec.MustCommit(gendb, triedb.NewDatabase(gendb, triedb.HashDefaults))
|
||||||
engine := beacon.New(ethash.NewFaker())
|
|
||||||
|
|
||||||
genchain, genreceipts := GenerateChain(gspec.Config, genesis, engine, gendb, 4, func(i int, gen *BlockGen) {
|
genchain, genreceipts := GenerateChain(gspec.Config, genesis, beacon.NewFaker(), gendb, 4, func(i int, gen *BlockGen) {
|
||||||
gen.SetParentBeaconRoot(common.Hash{byte(i + 1)})
|
gen.SetParentBeaconRoot(common.Hash{byte(i + 1)})
|
||||||
|
|
||||||
// Add value transfer tx.
|
// Add value transfer tx.
|
||||||
|
|
@ -121,7 +120,7 @@ func TestGeneratePOSChain(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Import the chain. This runs all block validation rules.
|
// Import the chain. This runs all block validation rules.
|
||||||
blockchain, _ := NewBlockChain(db, nil, gspec, nil, engine, vm.Config{}, nil, nil, nil)
|
blockchain, _ := NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
defer blockchain.Stop()
|
defer blockchain.Stop()
|
||||||
|
|
||||||
if i, err := blockchain.InsertChain(genchain); err != nil {
|
if i, err := blockchain.InsertChain(genchain); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -576,6 +576,7 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Blo
|
||||||
}
|
}
|
||||||
batch := db.NewBatch()
|
batch := db.NewBatch()
|
||||||
rawdb.WriteGenesisStateSpec(batch, block.Hash(), blob)
|
rawdb.WriteGenesisStateSpec(batch, block.Hash(), blob)
|
||||||
|
rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), block.Difficulty())
|
||||||
rawdb.WriteBlock(batch, block)
|
rawdb.WriteBlock(batch, block)
|
||||||
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), nil)
|
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), nil)
|
||||||
rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64())
|
rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64())
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ func TestGenesisHashes(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenesisCommit(t *testing.T) {
|
func TestGenesis_Commit(t *testing.T) {
|
||||||
genesis := &Genesis{
|
genesis := &Genesis{
|
||||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||||
Config: params.TestChainConfig,
|
Config: params.TestChainConfig,
|
||||||
|
|
@ -221,6 +221,13 @@ func TestGenesisCommit(t *testing.T) {
|
||||||
if genesisBlock.Difficulty().Cmp(params.GenesisDifficulty) != 0 {
|
if genesisBlock.Difficulty().Cmp(params.GenesisDifficulty) != 0 {
|
||||||
t.Errorf("assumption wrong: want: %d, got: %v", params.GenesisDifficulty, genesisBlock.Difficulty())
|
t.Errorf("assumption wrong: want: %d, got: %v", params.GenesisDifficulty, genesisBlock.Difficulty())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expect the stored total difficulty to be the difficulty of the genesis block.
|
||||||
|
stored := rawdb.ReadTd(db, genesisBlock.Hash(), genesisBlock.NumberU64())
|
||||||
|
|
||||||
|
if stored.Cmp(genesisBlock.Difficulty()) != 0 {
|
||||||
|
t.Errorf("inequal difficulty; stored: %v, genesisBlock: %v", stored, genesisBlock.Difficulty())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReadWriteGenesisAlloc(t *testing.T) {
|
func TestReadWriteGenesisAlloc(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package core
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -35,6 +36,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
headerCacheLimit = 512
|
headerCacheLimit = 512
|
||||||
|
tdCacheLimit = 1024
|
||||||
numberCacheLimit = 2048
|
numberCacheLimit = 2048
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -63,7 +65,8 @@ type HeaderChain struct {
|
||||||
currentHeaderHash common.Hash // Hash of the current head of the header chain (prevent recomputing all the time)
|
currentHeaderHash common.Hash // Hash of the current head of the header chain (prevent recomputing all the time)
|
||||||
|
|
||||||
headerCache *lru.Cache[common.Hash, *types.Header]
|
headerCache *lru.Cache[common.Hash, *types.Header]
|
||||||
numberCache *lru.Cache[common.Hash, uint64] // most recent block numbers
|
tdCache *lru.Cache[common.Hash, *big.Int] // most recent total difficulties
|
||||||
|
numberCache *lru.Cache[common.Hash, uint64] // most recent block numbers
|
||||||
|
|
||||||
procInterrupt func() bool
|
procInterrupt func() bool
|
||||||
engine consensus.Engine
|
engine consensus.Engine
|
||||||
|
|
@ -76,6 +79,7 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c
|
||||||
config: config,
|
config: config,
|
||||||
chainDb: chainDb,
|
chainDb: chainDb,
|
||||||
headerCache: lru.NewCache[common.Hash, *types.Header](headerCacheLimit),
|
headerCache: lru.NewCache[common.Hash, *types.Header](headerCacheLimit),
|
||||||
|
tdCache: lru.NewCache[common.Hash, *big.Int](tdCacheLimit),
|
||||||
numberCache: lru.NewCache[common.Hash, uint64](numberCacheLimit),
|
numberCache: lru.NewCache[common.Hash, uint64](numberCacheLimit),
|
||||||
procInterrupt: procInterrupt,
|
procInterrupt: procInterrupt,
|
||||||
engine: engine,
|
engine: engine,
|
||||||
|
|
@ -210,13 +214,15 @@ func (hc *HeaderChain) WriteHeaders(headers []*types.Header) (int, error) {
|
||||||
if len(headers) == 0 {
|
if len(headers) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
if !hc.HasHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1) {
|
ptd := hc.GetTd(headers[0].ParentHash, headers[0].Number.Uint64()-1)
|
||||||
|
if ptd == nil {
|
||||||
return 0, consensus.ErrUnknownAncestor
|
return 0, consensus.ErrUnknownAncestor
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
inserted []rawdb.NumberHash // Ephemeral lookup of number/hash for the chain
|
newTD = new(big.Int).Set(ptd) // Total difficulty of inserted chain
|
||||||
parentKnown = true // Set to true to force hc.HasHeader check the first iteration
|
inserted []rawdb.NumberHash // Ephemeral lookup of number/hash for the chain
|
||||||
|
parentKnown = true // Set to true to force hc.HasHeader check the first iteration
|
||||||
batch = hc.chainDb.NewBatch()
|
batch = hc.chainDb.NewBatch()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -232,11 +238,16 @@ func (hc *HeaderChain) WriteHeaders(headers []*types.Header) (int, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
number := header.Number.Uint64()
|
number := header.Number.Uint64()
|
||||||
|
newTD.Add(newTD, header.Difficulty)
|
||||||
|
|
||||||
// If the parent was not present, store it
|
// If the parent was not present, store it
|
||||||
// If the header is already known, skip it, otherwise store
|
// If the header is already known, skip it, otherwise store
|
||||||
alreadyKnown := parentKnown && hc.HasHeader(hash, number)
|
alreadyKnown := parentKnown && hc.HasHeader(hash, number)
|
||||||
if !alreadyKnown {
|
if !alreadyKnown {
|
||||||
|
// Irrelevant of the canonical status, write the TD and header to the database.
|
||||||
|
rawdb.WriteTd(batch, hash, number, newTD)
|
||||||
|
hc.tdCache.Add(hash, new(big.Int).Set(newTD))
|
||||||
|
|
||||||
rawdb.WriteHeader(batch, header)
|
rawdb.WriteHeader(batch, header)
|
||||||
|
|
||||||
inserted = append(inserted, rawdb.NumberHash{Number: number, Hash: hash})
|
inserted = append(inserted, rawdb.NumberHash{Number: number, Hash: hash})
|
||||||
|
|
@ -442,6 +453,22 @@ func (hc *HeaderChain) GetAncestor(hash common.Hash, number, ancestor uint64, ma
|
||||||
return hash, number
|
return hash, number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTd retrieves a block's total difficulty in the canonical chain from the
|
||||||
|
// database by hash and number, caching it if found.
|
||||||
|
func (hc *HeaderChain) GetTd(hash common.Hash, number uint64) *big.Int {
|
||||||
|
// Short circuit if the td's already in the cache, retrieve otherwise
|
||||||
|
if cached, ok := hc.tdCache.Get(hash); ok {
|
||||||
|
return cached
|
||||||
|
}
|
||||||
|
td := rawdb.ReadTd(hc.chainDb, hash, number)
|
||||||
|
if td == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Cache the found body for next time and return
|
||||||
|
hc.tdCache.Add(hash, td)
|
||||||
|
return td
|
||||||
|
}
|
||||||
|
|
||||||
// GetHeader retrieves a block header from the database by hash and number,
|
// GetHeader retrieves a block header from the database by hash and number,
|
||||||
// caching it if found.
|
// caching it if found.
|
||||||
func (hc *HeaderChain) GetHeader(hash common.Hash, number uint64) *types.Header {
|
func (hc *HeaderChain) GetHeader(hash common.Hash, number uint64) *types.Header {
|
||||||
|
|
@ -677,6 +704,7 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat
|
||||||
}
|
}
|
||||||
|
|
||||||
rawdb.DeleteHeader(batch, hash, num)
|
rawdb.DeleteHeader(batch, hash, num)
|
||||||
|
rawdb.DeleteTd(batch, hash, num)
|
||||||
}
|
}
|
||||||
|
|
||||||
rawdb.DeleteCanonicalHash(batch, num)
|
rawdb.DeleteCanonicalHash(batch, num)
|
||||||
|
|
@ -688,6 +716,7 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat
|
||||||
}
|
}
|
||||||
// Clear out any stale content from the caches
|
// Clear out any stale content from the caches
|
||||||
hc.headerCache.Purge()
|
hc.headerCache.Purge()
|
||||||
|
hc.tdCache.Purge()
|
||||||
hc.numberCache.Purge()
|
hc.numberCache.Purge()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,10 @@ func verifyUnbrokenCanonchain(hc *HeaderChain) error {
|
||||||
return fmt.Errorf("Canon hash chain broken, block %d got %x, expected %x",
|
return fmt.Errorf("Canon hash chain broken, block %d got %x, expected %x",
|
||||||
h.Number, canonHash[:8], exp[:8])
|
h.Number, canonHash[:8], exp[:8])
|
||||||
}
|
}
|
||||||
|
// Verify that we have the TD
|
||||||
|
if td := rawdb.ReadTd(hc.chainDb, canonHash, h.Number.Uint64()); td == nil {
|
||||||
|
return fmt.Errorf("Canon TD missing at block %d", h.Number)
|
||||||
|
}
|
||||||
if h.Number.Uint64() == 0 {
|
if h.Number.Uint64() == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -601,6 +601,54 @@ func DeleteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadTdRLP retrieves a block's total difficulty corresponding to the hash in RLP encoding.
|
||||||
|
func ReadTdRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||||
|
var data []byte
|
||||||
|
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||||
|
// Check if the data is in ancients
|
||||||
|
if isCanon(reader, number, hash) {
|
||||||
|
data, _ = reader.Ancient(ChainFreezerDifficultyTable, number)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// If not, try reading from leveldb
|
||||||
|
data, _ = db.Get(headerTDKey(number, hash))
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadTd retrieves a block's total difficulty corresponding to the hash.
|
||||||
|
func ReadTd(db ethdb.Reader, hash common.Hash, number uint64) *big.Int {
|
||||||
|
data := ReadTdRLP(db, hash, number)
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
td := new(big.Int)
|
||||||
|
if err := rlp.DecodeBytes(data, td); err != nil {
|
||||||
|
log.Error("Invalid block total difficulty RLP", "hash", hash, "err", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return td
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTd stores the total difficulty of a block into the database.
|
||||||
|
func WriteTd(db ethdb.KeyValueWriter, hash common.Hash, number uint64, td *big.Int) {
|
||||||
|
data, err := rlp.EncodeToBytes(td)
|
||||||
|
if err != nil {
|
||||||
|
log.Crit("Failed to RLP encode block total difficulty", "err", err)
|
||||||
|
}
|
||||||
|
if err := db.Put(headerTDKey(number, hash), data); err != nil {
|
||||||
|
log.Crit("Failed to store block total difficulty", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteTd removes all block total difficulty data associated with a hash.
|
||||||
|
func DeleteTd(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||||
|
if err := db.Delete(headerTDKey(number, hash)); err != nil {
|
||||||
|
log.Crit("Failed to delete block total difficulty", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// HasReceipts verifies the existence of all the transaction receipts belonging
|
// HasReceipts verifies the existence of all the transaction receipts belonging
|
||||||
// to a block.
|
// to a block.
|
||||||
func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||||
|
|
@ -801,8 +849,9 @@ func WriteBlock(db ethdb.KeyValueWriter, block *types.Block) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WriteAncientBlocks writes entire block data into ancient store and returns the total written size.
|
// WriteAncientBlocks writes entire block data into ancient store and returns the total written size.
|
||||||
func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts []types.Receipts, borReceipts []types.Receipts) (int64, error) {
|
func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts []types.Receipts, borReceipts []types.Receipts, td *big.Int) (int64, error) {
|
||||||
var (
|
var (
|
||||||
|
tdSum = new(big.Int).Set(td)
|
||||||
stReceipts []*types.ReceiptForStorage
|
stReceipts []*types.ReceiptForStorage
|
||||||
borStReceipts []*types.ReceiptForStorage
|
borStReceipts []*types.ReceiptForStorage
|
||||||
)
|
)
|
||||||
|
|
@ -823,7 +872,10 @@ func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts
|
||||||
|
|
||||||
header := block.Header()
|
header := block.Header()
|
||||||
|
|
||||||
if err := writeAncientBlock(op, block, header, stReceipts, borStReceipts); err != nil {
|
if i > 0 {
|
||||||
|
tdSum.Add(tdSum, header.Difficulty)
|
||||||
|
}
|
||||||
|
if err := writeAncientBlock(op, block, header, stReceipts, borStReceipts, tdSum); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -832,7 +884,7 @@ func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *types.Header, receipts []*types.ReceiptForStorage, borReceipts []*types.ReceiptForStorage) error {
|
func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *types.Header, receipts []*types.ReceiptForStorage, borReceipts []*types.ReceiptForStorage, td *big.Int) error {
|
||||||
num := block.NumberU64()
|
num := block.NumberU64()
|
||||||
if err := op.AppendRaw(ChainFreezerHashTable, num, block.Hash().Bytes()); err != nil {
|
if err := op.AppendRaw(ChainFreezerHashTable, num, block.Hash().Bytes()); err != nil {
|
||||||
return fmt.Errorf("can't add block %d hash: %v", num, err)
|
return fmt.Errorf("can't add block %d hash: %v", num, err)
|
||||||
|
|
@ -854,6 +906,9 @@ func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *type
|
||||||
return fmt.Errorf("can't append block %d borReceipts: %v", num, err)
|
return fmt.Errorf("can't append block %d borReceipts: %v", num, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := op.Append(ChainFreezerDifficultyTable, num, td); err != nil {
|
||||||
|
return fmt.Errorf("can't append block %d total difficulty: %v", num, err)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -862,6 +917,7 @@ func DeleteBlock(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||||
DeleteReceipts(db, hash, number)
|
DeleteReceipts(db, hash, number)
|
||||||
DeleteHeader(db, hash, number)
|
DeleteHeader(db, hash, number)
|
||||||
DeleteBody(db, hash, number)
|
DeleteBody(db, hash, number)
|
||||||
|
DeleteTd(db, hash, number)
|
||||||
|
|
||||||
// delete bor receipt
|
// delete bor receipt
|
||||||
DeleteBorReceipt(db, hash, number)
|
DeleteBorReceipt(db, hash, number)
|
||||||
|
|
@ -873,6 +929,7 @@ func DeleteBlockWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number
|
||||||
DeleteReceipts(db, hash, number)
|
DeleteReceipts(db, hash, number)
|
||||||
deleteHeaderWithoutNumber(db, hash, number)
|
deleteHeaderWithoutNumber(db, hash, number)
|
||||||
DeleteBody(db, hash, number)
|
DeleteBody(db, hash, number)
|
||||||
|
DeleteTd(db, hash, number)
|
||||||
|
|
||||||
// delete bor receipt
|
// delete bor receipt
|
||||||
DeleteBorReceipt(db, hash, number)
|
DeleteBorReceipt(db, hash, number)
|
||||||
|
|
|
||||||
|
|
@ -281,6 +281,29 @@ func TestBadBlockStorage(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests block total difficulty storage and retrieval operations.
|
||||||
|
func TestTdStorage(t *testing.T) {
|
||||||
|
db := NewMemoryDatabase()
|
||||||
|
|
||||||
|
// Create a test TD to move around the database and make sure it's really new
|
||||||
|
hash, td := common.Hash{}, big.NewInt(314)
|
||||||
|
if entry := ReadTd(db, hash, 0); entry != nil {
|
||||||
|
t.Fatalf("Non existent TD returned: %v", entry)
|
||||||
|
}
|
||||||
|
// Write and verify the TD in the database
|
||||||
|
WriteTd(db, hash, 0, td)
|
||||||
|
if entry := ReadTd(db, hash, 0); entry == nil {
|
||||||
|
t.Fatalf("Stored TD not found")
|
||||||
|
} else if entry.Cmp(td) != 0 {
|
||||||
|
t.Fatalf("Retrieved TD mismatch: have %v, want %v", entry, td)
|
||||||
|
}
|
||||||
|
// Delete the TD and verify the execution
|
||||||
|
DeleteTd(db, hash, 0)
|
||||||
|
if entry := ReadTd(db, hash, 0); entry != nil {
|
||||||
|
t.Fatalf("Deleted TD returned: %v", entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Tests that canonical numbers can be mapped to hashes and retrieved.
|
// Tests that canonical numbers can be mapped to hashes and retrieved.
|
||||||
func TestCanonicalMappingStorage(t *testing.T) {
|
func TestCanonicalMappingStorage(t *testing.T) {
|
||||||
db := NewMemoryDatabase()
|
db := NewMemoryDatabase()
|
||||||
|
|
@ -472,9 +495,12 @@ func TestAncientStorage(t *testing.T) {
|
||||||
if blob := ReadReceiptsRLP(db, hash, number); len(blob) > 0 {
|
if blob := ReadReceiptsRLP(db, hash, number); len(blob) > 0 {
|
||||||
t.Fatalf("non existent receipts returned")
|
t.Fatalf("non existent receipts returned")
|
||||||
}
|
}
|
||||||
|
if blob := ReadTdRLP(db, hash, number); len(blob) > 0 {
|
||||||
|
t.Fatalf("non existent td returned")
|
||||||
|
}
|
||||||
|
|
||||||
// Write and verify the header in the database
|
// Write and verify the header in the database
|
||||||
WriteAncientBlocks(db, []*types.Block{block}, []types.Receipts{nil}, []types.Receipts{nil})
|
WriteAncientBlocks(db, []*types.Block{block}, []types.Receipts{nil}, []types.Receipts{nil}, big.NewInt(100))
|
||||||
|
|
||||||
if blob := ReadHeaderRLP(db, hash, number); len(blob) == 0 {
|
if blob := ReadHeaderRLP(db, hash, number); len(blob) == 0 {
|
||||||
t.Fatalf("no header returned")
|
t.Fatalf("no header returned")
|
||||||
|
|
@ -487,6 +513,9 @@ func TestAncientStorage(t *testing.T) {
|
||||||
if blob := ReadReceiptsRLP(db, hash, number); len(blob) == 0 {
|
if blob := ReadReceiptsRLP(db, hash, number); len(blob) == 0 {
|
||||||
t.Fatalf("no receipts returned")
|
t.Fatalf("no receipts returned")
|
||||||
}
|
}
|
||||||
|
if blob := ReadTdRLP(db, hash, number); len(blob) == 0 {
|
||||||
|
t.Fatalf("no td returned")
|
||||||
|
}
|
||||||
|
|
||||||
// Use a fake hash for data retrieval, nothing should be returned.
|
// Use a fake hash for data retrieval, nothing should be returned.
|
||||||
fakeHash := common.BytesToHash([]byte{0x01, 0x02, 0x03})
|
fakeHash := common.BytesToHash([]byte{0x01, 0x02, 0x03})
|
||||||
|
|
@ -501,6 +530,9 @@ func TestAncientStorage(t *testing.T) {
|
||||||
if blob := ReadReceiptsRLP(db, fakeHash, number); len(blob) != 0 {
|
if blob := ReadReceiptsRLP(db, fakeHash, number); len(blob) != 0 {
|
||||||
t.Fatalf("invalid receipts returned")
|
t.Fatalf("invalid receipts returned")
|
||||||
}
|
}
|
||||||
|
if blob := ReadTdRLP(db, fakeHash, number); len(blob) != 0 {
|
||||||
|
t.Fatalf("invalid td returned")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCanonicalHashIteration(t *testing.T) {
|
func TestCanonicalHashIteration(t *testing.T) {
|
||||||
|
|
@ -526,6 +558,7 @@ func TestCanonicalHashIteration(t *testing.T) {
|
||||||
// Fill database with testing data.
|
// Fill database with testing data.
|
||||||
for i := uint64(1); i <= 8; i++ {
|
for i := uint64(1); i <= 8; i++ {
|
||||||
WriteCanonicalHash(db, common.Hash{}, i)
|
WriteCanonicalHash(db, common.Hash{}, i)
|
||||||
|
WriteTd(db, common.Hash{}, i, big.NewInt(10)) // Write some interferential data
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, c := range cases {
|
for i, c := range cases {
|
||||||
|
|
@ -613,6 +646,7 @@ func BenchmarkWriteAncientBlocks(b *testing.B) {
|
||||||
// The benchmark loop writes batches of blocks, but note that the total block count is
|
// The benchmark loop writes batches of blocks, but note that the total block count is
|
||||||
// b.N. This means the resulting ns/op measurement is the time it takes to write a
|
// b.N. This means the resulting ns/op measurement is the time it takes to write a
|
||||||
// single block and its associated data.
|
// single block and its associated data.
|
||||||
|
var td = big.NewInt(55)
|
||||||
var totalSize int64
|
var totalSize int64
|
||||||
|
|
||||||
for i := 0; i < b.N; i += batchSize {
|
for i := 0; i < b.N; i += batchSize {
|
||||||
|
|
@ -624,7 +658,7 @@ func BenchmarkWriteAncientBlocks(b *testing.B) {
|
||||||
blocks := allBlocks[i : i+length]
|
blocks := allBlocks[i : i+length]
|
||||||
receipts := batchReceipts[:length]
|
receipts := batchReceipts[:length]
|
||||||
|
|
||||||
writeSize, err := WriteAncientBlocks(db, blocks, receipts, []types.Receipts{nil})
|
writeSize, err := WriteAncientBlocks(db, blocks, receipts, []types.Receipts{nil}, td)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -950,7 +984,7 @@ func TestHeadersRLPStorage(t *testing.T) {
|
||||||
var borReceipts []types.Receipts = make([]types.Receipts, 100)
|
var borReceipts []types.Receipts = make([]types.Receipts, 100)
|
||||||
|
|
||||||
// Write first half to ancients
|
// Write first half to ancients
|
||||||
WriteAncientBlocks(db, chain[:50], receipts[:50], borReceipts[:50])
|
WriteAncientBlocks(db, chain[:50], receipts[:50], borReceipts[:50], big.NewInt(100))
|
||||||
// Write second half to db
|
// Write second half to db
|
||||||
for i := 50; i < 100; i++ {
|
for i := 50; i < 100; i++ {
|
||||||
WriteCanonicalHash(db, chain[i].Hash(), chain[i].NumberU64())
|
WriteCanonicalHash(db, chain[i].Hash(), chain[i].NumberU64())
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,9 @@ const (
|
||||||
|
|
||||||
// ChainFreezerReceiptTable indicates the name of the freezer receipts table.
|
// ChainFreezerReceiptTable indicates the name of the freezer receipts table.
|
||||||
ChainFreezerReceiptTable = "receipts"
|
ChainFreezerReceiptTable = "receipts"
|
||||||
|
|
||||||
|
// ChainFreezerDifficultyTable indicates the name of the freezer total difficulty table.
|
||||||
|
ChainFreezerDifficultyTable = "diffs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// chainFreezerTableConfigs configures the settings for tables in the chain freezer.
|
// chainFreezerTableConfigs configures the settings for tables in the chain freezer.
|
||||||
|
|
@ -42,11 +45,12 @@ const (
|
||||||
// tail truncation is disabled for the header and hash tables, as these are intended
|
// tail truncation is disabled for the header and hash tables, as these are intended
|
||||||
// to be retained long-term.
|
// to be retained long-term.
|
||||||
var chainFreezerTableConfigs = map[string]freezerTableConfig{
|
var chainFreezerTableConfigs = map[string]freezerTableConfig{
|
||||||
ChainFreezerHeaderTable: {noSnappy: false, prunable: false},
|
ChainFreezerHeaderTable: {noSnappy: false, prunable: false},
|
||||||
ChainFreezerHashTable: {noSnappy: true, prunable: false},
|
ChainFreezerHashTable: {noSnappy: true, prunable: false},
|
||||||
ChainFreezerBodiesTable: {noSnappy: false, prunable: true},
|
ChainFreezerBodiesTable: {noSnappy: false, prunable: true},
|
||||||
ChainFreezerReceiptTable: {noSnappy: false, prunable: true},
|
ChainFreezerReceiptTable: {noSnappy: false, prunable: true},
|
||||||
freezerBorReceiptTable: {noSnappy: false, prunable: true},
|
ChainFreezerDifficultyTable: {noSnappy: false, prunable: true},
|
||||||
|
freezerBorReceiptTable: {noSnappy: false, prunable: true},
|
||||||
}
|
}
|
||||||
|
|
||||||
// freezerTableConfig contains the settings for a freezer table.
|
// freezerTableConfig contains the settings for a freezer table.
|
||||||
|
|
|
||||||
|
|
@ -348,6 +348,11 @@ func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hash
|
||||||
if len(receipts) == 0 {
|
if len(receipts) == 0 {
|
||||||
return fmt.Errorf("block receipts missing, can't freeze block %d", number)
|
return fmt.Errorf("block receipts missing, can't freeze block %d", number)
|
||||||
}
|
}
|
||||||
|
td := ReadTdRLP(nfdb, hash, number)
|
||||||
|
if len(td) == 0 {
|
||||||
|
return fmt.Errorf("total difficulty missing, can't freeze block %d", number)
|
||||||
|
}
|
||||||
|
|
||||||
// Write to the batch.
|
// Write to the batch.
|
||||||
if err := op.AppendRaw(ChainFreezerHashTable, number, hash[:]); err != nil {
|
if err := op.AppendRaw(ChainFreezerHashTable, number, hash[:]); err != nil {
|
||||||
return fmt.Errorf("can't write hash to Freezer: %v", err)
|
return fmt.Errorf("can't write hash to Freezer: %v", err)
|
||||||
|
|
@ -371,6 +376,9 @@ func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hash
|
||||||
return fmt.Errorf("can't write bor-receipt to freezer: %v", err)
|
return fmt.Errorf("can't write bor-receipt to freezer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := op.AppendRaw(ChainFreezerDifficultyTable, number, td); err != nil {
|
||||||
|
return fmt.Errorf("can't write td to Freezer: %v", err)
|
||||||
|
}
|
||||||
hashes = append(hashes, hash)
|
hashes = append(hashes, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -669,7 +669,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||||
{"Key-Value store", "Headers", headers.Size(), headers.Count()},
|
{"Key-Value store", "Headers", headers.Size(), headers.Count()},
|
||||||
{"Key-Value store", "Bodies", bodies.Size(), bodies.Count()},
|
{"Key-Value store", "Bodies", bodies.Size(), bodies.Count()},
|
||||||
{"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()},
|
{"Key-Value store", "Receipt lists", receipts.Size(), receipts.Count()},
|
||||||
{"Key-Value store", "Difficulties (deprecated)", tds.Size(), tds.Count()},
|
{"Key-Value store", "Difficulties", tds.Size(), tds.Count()},
|
||||||
{"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()},
|
{"Key-Value store", "Block number->hash", numHashPairings.Size(), numHashPairings.Count()},
|
||||||
{"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()},
|
{"Key-Value store", "Block hash->number", hashNumPairings.Size(), hashNumPairings.Count()},
|
||||||
{"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()},
|
{"Key-Value store", "Transaction index", txLookups.Size(), txLookups.Count()},
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ var (
|
||||||
|
|
||||||
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
|
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
|
||||||
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
|
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
|
||||||
headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td (deprecated)
|
headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td
|
||||||
headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash
|
headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash
|
||||||
headerNumberPrefix = []byte("H") // headerNumberPrefix + hash -> num (uint64 big endian)
|
headerNumberPrefix = []byte("H") // headerNumberPrefix + hash -> num (uint64 big endian)
|
||||||
|
|
||||||
|
|
@ -181,6 +181,11 @@ func headerKey(number uint64, hash common.Hash) []byte {
|
||||||
return append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
return append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// headerTDKey = headerPrefix + num (uint64 big endian) + hash + headerTDSuffix
|
||||||
|
func headerTDKey(number uint64, hash common.Hash) []byte {
|
||||||
|
return append(headerKey(number, hash), headerTDSuffix...)
|
||||||
|
}
|
||||||
|
|
||||||
// headerHashKey = headerPrefix + num (uint64 big endian) + headerHashSuffix
|
// headerHashKey = headerPrefix + num (uint64 big endian) + headerHashSuffix
|
||||||
func headerHashKey(number uint64) []byte {
|
func headerHashKey(number uint64) []byte {
|
||||||
return append(append(headerPrefix, encodeBlockNumber(number)...), headerHashSuffix...)
|
return append(append(headerPrefix, encodeBlockNumber(number)...), headerHashSuffix...)
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ type VMContext struct {
|
||||||
// It contains the block as well as consensus related information.
|
// It contains the block as well as consensus related information.
|
||||||
type BlockEvent struct {
|
type BlockEvent struct {
|
||||||
Block *types.Block
|
Block *types.Block
|
||||||
|
TD *big.Int
|
||||||
Finalized *types.Header
|
Finalized *types.Header
|
||||||
Safe *types.Header
|
Safe *types.Header
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,7 @@ func TestTxIndexerRepair(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), "", "", false)
|
||||||
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...))
|
rawdb.WriteAncientBlocks(db, append([]*types.Block{gspec.ToBlock()}, blocks...), append([]types.Receipts{{}}, receipts...), big.NewInt(0))
|
||||||
|
|
||||||
// Index the initial blocks from ancient store
|
// Index the initial blocks from ancient store
|
||||||
indexer := &txIndexer{
|
indexer := &txIndexer{
|
||||||
|
|
|
||||||
|
|
@ -277,6 +277,13 @@ func (b *EthAPIBackend) GetLogs(ctx context.Context, hash common.Hash, number ui
|
||||||
return rawdb.ReadLogs(b.eth.chainDb, hash, number), nil
|
return rawdb.ReadLogs(b.eth.chainDb, hash, number), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
|
||||||
|
if header := b.eth.blockchain.GetHeaderByHash(hash); header != nil {
|
||||||
|
return b.eth.blockchain.GetTd(hash, header.Number.Uint64())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM {
|
func (b *EthAPIBackend) GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM {
|
||||||
if vmConfig == nil {
|
if vmConfig == nil {
|
||||||
vmConfig = b.eth.blockchain.GetVMConfig()
|
vmConfig = b.eth.blockchain.GetVMConfig()
|
||||||
|
|
|
||||||
|
|
@ -364,12 +364,21 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
||||||
}
|
}
|
||||||
// Block is known locally, just sanity check that the beacon client does not
|
// Block is known locally, just sanity check that the beacon client does not
|
||||||
// attempt to push us back to before the merge.
|
// attempt to push us back to before the merge.
|
||||||
if block.Difficulty().BitLen() > 0 && block.NumberU64() > 0 {
|
if block.Difficulty().BitLen() > 0 || block.NumberU64() == 0 {
|
||||||
ph := api.eth.BlockChain().GetHeader(block.ParentHash(), block.NumberU64()-1)
|
var (
|
||||||
if ph == nil {
|
td = api.eth.BlockChain().GetTd(update.HeadBlockHash, block.NumberU64())
|
||||||
return engine.STATUS_INVALID, errors.New("parent unavailable for difficulty check")
|
ptd = api.eth.BlockChain().GetTd(block.ParentHash(), block.NumberU64()-1)
|
||||||
|
ttd = api.eth.BlockChain().Config().TerminalTotalDifficulty
|
||||||
|
)
|
||||||
|
if td == nil || (block.NumberU64() > 0 && ptd == nil) {
|
||||||
|
log.Error("TDs unavailable for TTD check", "number", block.NumberU64(), "hash", update.HeadBlockHash, "td", td, "parent", block.ParentHash(), "ptd", ptd)
|
||||||
|
return engine.STATUS_INVALID, errors.New("TDs unavailable for TDD check")
|
||||||
}
|
}
|
||||||
if ph.Difficulty.Sign() == 0 && block.Difficulty().Sign() > 0 {
|
if td.Cmp(ttd) < 0 {
|
||||||
|
log.Error("Refusing beacon update to pre-merge", "number", block.NumberU64(), "hash", update.HeadBlockHash, "diff", block.Difficulty(), "age", common.PrettyAge(time.Unix(int64(block.Time()), 0)))
|
||||||
|
return engine.ForkChoiceResponse{PayloadStatus: engine.INVALID_TERMINAL_BLOCK, PayloadID: nil}, nil
|
||||||
|
}
|
||||||
|
if block.NumberU64() > 0 && ptd.Cmp(ttd) >= 0 {
|
||||||
log.Error("Parent block is already post-ttd", "number", block.NumberU64(), "hash", update.HeadBlockHash, "diff", block.Difficulty(), "age", common.PrettyAge(time.Unix(int64(block.Time()), 0)))
|
log.Error("Parent block is already post-ttd", "number", block.NumberU64(), "hash", update.HeadBlockHash, "diff", block.Difficulty(), "age", common.PrettyAge(time.Unix(int64(block.Time()), 0)))
|
||||||
return engine.ForkChoiceResponse{PayloadStatus: engine.INVALID_TERMINAL_BLOCK, PayloadID: nil}, nil
|
return engine.ForkChoiceResponse{PayloadStatus: engine.INVALID_TERMINAL_BLOCK, PayloadID: nil}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -893,6 +902,21 @@ func (api *ConsensusAPI) newPayload(params engine.ExecutableData, versionedHashe
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
return api.delayPayloadImport(block), nil
|
return api.delayPayloadImport(block), nil
|
||||||
}
|
}
|
||||||
|
// We have an existing parent, do some sanity checks to avoid the beacon client
|
||||||
|
// triggering too early
|
||||||
|
var (
|
||||||
|
ptd = api.eth.BlockChain().GetTd(parent.Hash(), parent.NumberU64())
|
||||||
|
ttd = api.eth.BlockChain().Config().TerminalTotalDifficulty
|
||||||
|
gptd = api.eth.BlockChain().GetTd(parent.ParentHash(), parent.NumberU64()-1)
|
||||||
|
)
|
||||||
|
if ptd.Cmp(ttd) < 0 {
|
||||||
|
log.Warn("Ignoring pre-merge payload", "number", params.Number, "hash", params.BlockHash, "td", ptd, "ttd", ttd)
|
||||||
|
return engine.INVALID_TERMINAL_BLOCK, nil
|
||||||
|
}
|
||||||
|
if parent.Difficulty().BitLen() > 0 && gptd != nil && gptd.Cmp(ttd) >= 0 {
|
||||||
|
log.Error("Ignoring pre-merge parent block", "number", params.Number, "hash", params.BlockHash, "td", ptd, "ttd", ttd)
|
||||||
|
return engine.INVALID_TERMINAL_BLOCK, nil
|
||||||
|
}
|
||||||
if block.Time() <= parent.Time() {
|
if block.Time() <= parent.Time() {
|
||||||
log.Warn("Invalid timestamp", "parent", block.Time(), "block", block.Time())
|
log.Warn("Invalid timestamp", "parent", block.Time(), "block", block.Time())
|
||||||
return api.invalid(errors.New("invalid timestamp"), parent.Header()), nil
|
return api.invalid(errors.New("invalid timestamp"), parent.Header()), nil
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,8 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/beacon/engine"
|
"github.com/ethereum/go-ethereum/beacon/engine"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
|
beaconConsensus "github.com/ethereum/go-ethereum/consensus/beacon"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
@ -61,13 +62,10 @@ var (
|
||||||
|
|
||||||
func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) {
|
func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) {
|
||||||
config := *params.AllEthashProtocolChanges
|
config := *params.AllEthashProtocolChanges
|
||||||
engine := beacon.New(ethash.NewFaker())
|
engine := consensus.Engine(beaconConsensus.New(ethash.NewFaker()))
|
||||||
if merged {
|
if merged {
|
||||||
config.TerminalTotalDifficulty = common.Big0
|
config.TerminalTotalDifficulty = common.Big0
|
||||||
config.MergeNetsplitBlock = common.Big0
|
engine = beaconConsensus.NewFaker()
|
||||||
} else {
|
|
||||||
// When !merged, the tests expect the next block after the generated chain to be in PoS.
|
|
||||||
config.MergeNetsplitBlock = big.NewInt(int64(n + 1))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
genesis := &core.Genesis{
|
genesis := &core.Genesis{
|
||||||
|
|
@ -104,6 +102,7 @@ func generateMergeChain(n int, merged bool) (*core.Genesis, []*types.Block) {
|
||||||
}
|
}
|
||||||
config.TerminalTotalDifficulty = totalDifficulty
|
config.TerminalTotalDifficulty = totalDifficulty
|
||||||
}
|
}
|
||||||
|
|
||||||
return genesis, blocks
|
return genesis, blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,6 +165,24 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSetHeadBeforeTotalDifficulty(t *testing.T) {
|
||||||
|
genesis, blocks := generateMergeChain(10, false)
|
||||||
|
n, ethservice := startEthService(t, genesis, blocks)
|
||||||
|
defer n.Close()
|
||||||
|
|
||||||
|
api := NewConsensusAPI(ethservice)
|
||||||
|
fcState := engine.ForkchoiceStateV1{
|
||||||
|
HeadBlockHash: blocks[5].Hash(),
|
||||||
|
SafeBlockHash: common.Hash{},
|
||||||
|
FinalizedBlockHash: common.Hash{},
|
||||||
|
}
|
||||||
|
if resp, err := api.ForkchoiceUpdatedV1(fcState, nil); err != nil {
|
||||||
|
t.Errorf("fork choice updated should not error: %v", err)
|
||||||
|
} else if resp.PayloadStatus.Status != engine.INVALID_TERMINAL_BLOCK.Status {
|
||||||
|
t.Errorf("fork choice updated before total terminal difficulty should be INVALID")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestEth2PrepareAndGetPayload(t *testing.T) {
|
func TestEth2PrepareAndGetPayload(t *testing.T) {
|
||||||
genesis, blocks := generateMergeChain(10, false)
|
genesis, blocks := generateMergeChain(10, false)
|
||||||
// We need to properly set the terminal total difficulty
|
// We need to properly set the terminal total difficulty
|
||||||
|
|
@ -887,6 +904,70 @@ func TestInvalidBloom(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewPayloadOnInvalidTerminalBlock(t *testing.T) {
|
||||||
|
genesis, preMergeBlocks := generateMergeChain(100, false)
|
||||||
|
n, ethservice := startEthService(t, genesis, preMergeBlocks)
|
||||||
|
defer n.Close()
|
||||||
|
api := NewConsensusAPI(ethservice)
|
||||||
|
|
||||||
|
// Test parent already post TTD in FCU
|
||||||
|
parent := preMergeBlocks[len(preMergeBlocks)-2]
|
||||||
|
fcState := engine.ForkchoiceStateV1{
|
||||||
|
HeadBlockHash: parent.Hash(),
|
||||||
|
SafeBlockHash: common.Hash{},
|
||||||
|
FinalizedBlockHash: common.Hash{},
|
||||||
|
}
|
||||||
|
resp, err := api.ForkchoiceUpdatedV1(fcState, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error sending forkchoice, err=%v", err)
|
||||||
|
}
|
||||||
|
if resp.PayloadStatus != engine.INVALID_TERMINAL_BLOCK {
|
||||||
|
t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test parent already post TTD in NewPayload
|
||||||
|
args := &miner.BuildPayloadArgs{
|
||||||
|
Parent: parent.Hash(),
|
||||||
|
Timestamp: parent.Time() + 1,
|
||||||
|
Random: crypto.Keccak256Hash([]byte{byte(1)}),
|
||||||
|
FeeRecipient: parent.Coinbase(),
|
||||||
|
}
|
||||||
|
payload, err := api.eth.Miner().BuildPayload(args, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error preparing payload, err=%v", err)
|
||||||
|
}
|
||||||
|
data := *payload.Resolve().ExecutionPayload
|
||||||
|
// We need to recompute the blockhash, since the miner computes a wrong (correct) blockhash
|
||||||
|
txs, _ := decodeTransactions(data.Transactions)
|
||||||
|
header := &types.Header{
|
||||||
|
ParentHash: data.ParentHash,
|
||||||
|
UncleHash: types.EmptyUncleHash,
|
||||||
|
Coinbase: data.FeeRecipient,
|
||||||
|
Root: data.StateRoot,
|
||||||
|
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
|
||||||
|
ReceiptHash: data.ReceiptsRoot,
|
||||||
|
Bloom: types.BytesToBloom(data.LogsBloom),
|
||||||
|
Difficulty: common.Big0,
|
||||||
|
Number: new(big.Int).SetUint64(data.Number),
|
||||||
|
GasLimit: data.GasLimit,
|
||||||
|
GasUsed: data.GasUsed,
|
||||||
|
Time: data.Timestamp,
|
||||||
|
BaseFee: data.BaseFeePerGas,
|
||||||
|
Extra: data.ExtraData,
|
||||||
|
MixDigest: data.Random,
|
||||||
|
}
|
||||||
|
block := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs})
|
||||||
|
data.BlockHash = block.Hash()
|
||||||
|
// Send the new payload
|
||||||
|
resp2, err := api.NewPayloadV1(data)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error sending NewPayload, err=%v", err)
|
||||||
|
}
|
||||||
|
if resp2 != engine.INVALID_TERMINAL_BLOCK {
|
||||||
|
t.Fatalf("error sending invalid forkchoice, invalid status: %v", resp.PayloadStatus.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestSimultaneousNewBlock does several parallel inserts, both as
|
// TestSimultaneousNewBlock does several parallel inserts, both as
|
||||||
// newPayLoad and forkchoiceUpdate. This is to test that the api behaves
|
// newPayLoad and forkchoiceUpdate. This is to test that the api behaves
|
||||||
// well even of the caller is not being 'serial'.
|
// well even of the caller is not being 'serial'.
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,9 @@ type LightChain interface {
|
||||||
// CurrentHeader retrieves the head header from the local chain.
|
// CurrentHeader retrieves the head header from the local chain.
|
||||||
CurrentHeader() *types.Header
|
CurrentHeader() *types.Header
|
||||||
|
|
||||||
|
// GetTd returns the total difficulty of a local block.
|
||||||
|
GetTd(common.Hash, uint64) *big.Int
|
||||||
|
|
||||||
// InsertHeaderChain inserts a batch of headers into the local chain.
|
// InsertHeaderChain inserts a batch of headers into the local chain.
|
||||||
InsertHeaderChain([]*types.Header) (int, error)
|
InsertHeaderChain([]*types.Header) (int, error)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ package downloader
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -56,6 +57,7 @@ type peerConnection struct {
|
||||||
|
|
||||||
// Peer encapsulates the methods required to synchronise with a remote full peer.
|
// Peer encapsulates the methods required to synchronise with a remote full peer.
|
||||||
type Peer interface {
|
type Peer interface {
|
||||||
|
Head() (common.Hash, *big.Int)
|
||||||
RequestHeadersByHash(common.Hash, int, int, bool, chan *eth.Response) (*eth.Request, error)
|
RequestHeadersByHash(common.Hash, int, int, bool, chan *eth.Response) (*eth.Request, error)
|
||||||
RequestHeadersByNumber(uint64, int, int, bool, chan *eth.Response) (*eth.Request, error)
|
RequestHeadersByNumber(uint64, int, int, bool, chan *eth.Response) (*eth.Request, error)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,7 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, cancunBlock *big.Int, pe
|
||||||
// config.MergeNetsplitBlock = cancunBlock
|
// config.MergeNetsplitBlock = cancunBlock
|
||||||
// }
|
// }
|
||||||
// engine := beacon.New(ethash.NewFaker())
|
// engine := beacon.New(ethash.NewFaker())
|
||||||
|
config.TerminalTotalDifficulty = common.Big0
|
||||||
engine := ethash.NewFaker()
|
engine := ethash.NewFaker()
|
||||||
|
|
||||||
// if cancunBlock != nil {
|
// if cancunBlock != nil {
|
||||||
|
|
|
||||||
|
|
@ -321,9 +321,10 @@ func (h *handler) runEthPeer(peer *eth.Peer, handler eth.Handler) error {
|
||||||
head = h.chain.CurrentHeader()
|
head = h.chain.CurrentHeader()
|
||||||
hash = head.Hash()
|
hash = head.Hash()
|
||||||
number = head.Number.Uint64()
|
number = head.Number.Uint64()
|
||||||
|
td = h.chain.GetTd(hash, number)
|
||||||
)
|
)
|
||||||
forkID := forkid.NewID(h.chain.Config(), genesis, number, head.Time)
|
forkID := forkid.NewID(h.chain.Config(), genesis, number, head.Time)
|
||||||
if err := peer.Handshake(h.networkID, hash, genesis.Hash(), forkID, h.forkFilter); err != nil {
|
if err := peer.Handshake(h.networkID, td, hash, genesis.Hash(), forkID, h.forkFilter); err != nil {
|
||||||
peer.Log().Debug("Ethereum handshake failed", "err", err)
|
peer.Log().Debug("Ethereum handshake failed", "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -272,8 +272,9 @@ func testRecvTransactions(t *testing.T, protocol uint) {
|
||||||
var (
|
var (
|
||||||
genesis = handler.chain.Genesis()
|
genesis = handler.chain.Genesis()
|
||||||
head = handler.chain.CurrentBlock()
|
head = handler.chain.CurrentBlock()
|
||||||
|
td = handler.chain.GetTd(head.Hash(), head.Number.Uint64())
|
||||||
)
|
)
|
||||||
if err := src.Handshake(1, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain)); err != nil {
|
if err := src.Handshake(1, td, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain)); err != nil {
|
||||||
t.Fatalf("failed to run protocol handshake")
|
t.Fatalf("failed to run protocol handshake")
|
||||||
}
|
}
|
||||||
// Send the transaction to the sink and verify that it's added to the tx pool
|
// Send the transaction to the sink and verify that it's added to the tx pool
|
||||||
|
|
@ -332,8 +333,9 @@ func testSendTransactions(t *testing.T, protocol uint) {
|
||||||
var (
|
var (
|
||||||
genesis = handler.chain.Genesis()
|
genesis = handler.chain.Genesis()
|
||||||
head = handler.chain.CurrentBlock()
|
head = handler.chain.CurrentBlock()
|
||||||
|
td = handler.chain.GetTd(head.Hash(), head.Number.Uint64())
|
||||||
)
|
)
|
||||||
if err := sink.Handshake(1, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain)); err != nil {
|
if err := sink.Handshake(1, td, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain)); err != nil {
|
||||||
t.Fatalf("failed to run protocol handshake")
|
t.Fatalf("failed to run protocol handshake")
|
||||||
}
|
}
|
||||||
// After the handshake completes, the source handler should stream the sink
|
// After the handshake completes, the source handler should stream the sink
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -124,10 +125,11 @@ func MakeProtocols(backend Backend, network uint64, disc enode.Iterator) []p2p.P
|
||||||
// NodeInfo represents a short summary of the `eth` sub-protocol metadata
|
// NodeInfo represents a short summary of the `eth` sub-protocol metadata
|
||||||
// known about the host peer.
|
// known about the host peer.
|
||||||
type NodeInfo struct {
|
type NodeInfo struct {
|
||||||
Network uint64 `json:"network"` // Ethereum network ID (1=Mainnet, Holesky=17000)
|
Network uint64 `json:"network"` // Ethereum network ID (1=Mainnet, Holesky=17000)
|
||||||
Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block
|
Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain
|
||||||
Config *params.ChainConfig `json:"config"` // Chain configuration for the fork rules
|
Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block
|
||||||
Head common.Hash `json:"head"` // Hex hash of the host's best owned block
|
Config *params.ChainConfig `json:"config"` // Chain configuration for the fork rules
|
||||||
|
Head common.Hash `json:"head"` // Hex hash of the host's best owned block
|
||||||
}
|
}
|
||||||
|
|
||||||
// nodeInfo retrieves some `eth` protocol metadata about the running host node.
|
// nodeInfo retrieves some `eth` protocol metadata about the running host node.
|
||||||
|
|
@ -136,10 +138,11 @@ func nodeInfo(chain *core.BlockChain, network uint64) *NodeInfo {
|
||||||
hash := head.Hash()
|
hash := head.Hash()
|
||||||
|
|
||||||
return &NodeInfo{
|
return &NodeInfo{
|
||||||
Network: network,
|
Network: network,
|
||||||
Genesis: chain.Genesis().Hash(),
|
Difficulty: chain.GetTd(hash, head.Number.Uint64()),
|
||||||
Config: chain.Config(),
|
Genesis: chain.Genesis().Hash(),
|
||||||
Head: hash,
|
Config: chain.Config(),
|
||||||
|
Head: hash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
|
@ -74,10 +75,11 @@ func newTestBackend(blocks int) *testBackend {
|
||||||
func newTestBackendWithGenerator(blocks int, shanghai bool, cancun bool, generator func(int, *core.BlockGen)) *testBackend {
|
func newTestBackendWithGenerator(blocks int, shanghai bool, cancun bool, generator func(int, *core.BlockGen)) *testBackend {
|
||||||
var (
|
var (
|
||||||
// Create a database pre-initialize with a genesis block
|
// Create a database pre-initialize with a genesis block
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
config = params.TestChainConfig
|
config = params.TestChainConfig
|
||||||
engine = beacon.New(ethash.NewFaker())
|
engine consensus.Engine = ethash.NewFaker()
|
||||||
)
|
)
|
||||||
|
|
||||||
if shanghai {
|
if shanghai {
|
||||||
config = ¶ms.ChainConfig{
|
config = ¶ms.ChainConfig{
|
||||||
ChainID: big.NewInt(1),
|
ChainID: big.NewInt(1),
|
||||||
|
|
@ -102,6 +104,7 @@ func newTestBackendWithGenerator(blocks int, shanghai bool, cancun bool, generat
|
||||||
Ethash: new(params.EthashConfig),
|
Ethash: new(params.EthashConfig),
|
||||||
Bor: params.TestChainConfig.Bor,
|
Bor: params.TestChainConfig.Bor,
|
||||||
}
|
}
|
||||||
|
engine = beacon.NewFaker()
|
||||||
}
|
}
|
||||||
|
|
||||||
gspec := &core.Genesis{
|
gspec := &core.Genesis{
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ const (
|
||||||
|
|
||||||
// Handshake executes the eth protocol handshake, negotiating version number,
|
// Handshake executes the eth protocol handshake, negotiating version number,
|
||||||
// network IDs, difficulties, head and genesis blocks.
|
// network IDs, difficulties, head and genesis blocks.
|
||||||
func (p *Peer) Handshake(network uint64, head common.Hash, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter) error {
|
func (p *Peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter) error {
|
||||||
// Send out own handshake in a new thread
|
// Send out own handshake in a new thread
|
||||||
errc := make(chan error, 2)
|
errc := make(chan error, 2)
|
||||||
|
|
||||||
|
|
@ -46,7 +46,7 @@ func (p *Peer) Handshake(network uint64, head common.Hash, genesis common.Hash,
|
||||||
errc <- p2p.Send(p.rw, StatusMsg, &StatusPacket{
|
errc <- p2p.Send(p.rw, StatusMsg, &StatusPacket{
|
||||||
ProtocolVersion: uint32(p.version),
|
ProtocolVersion: uint32(p.version),
|
||||||
NetworkID: network,
|
NetworkID: network,
|
||||||
TD: new(big.Int), // unknown for post-merge tail=pruned networks
|
TD: td,
|
||||||
Head: head,
|
Head: head,
|
||||||
Genesis: genesis,
|
Genesis: genesis,
|
||||||
ForkID: forkID,
|
ForkID: forkID,
|
||||||
|
|
@ -71,6 +71,13 @@ func (p *Peer) Handshake(network uint64, head common.Hash, genesis common.Hash,
|
||||||
return p2p.DiscReadTimeout
|
return p2p.DiscReadTimeout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
p.td, p.head = status.TD, status.Head
|
||||||
|
|
||||||
|
// TD at mainnet block #7753254 is 76 bits. If it becomes 100 million times
|
||||||
|
// larger, it will still fit within 100 bits
|
||||||
|
if tdlen := p.td.BitLen(); tdlen > 100 {
|
||||||
|
return fmt.Errorf("too large total difficulty: bitlen %d", tdlen)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"math/big"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -41,6 +40,7 @@ func testHandshake(t *testing.T, protocol uint) {
|
||||||
var (
|
var (
|
||||||
genesis = backend.chain.Genesis()
|
genesis = backend.chain.Genesis()
|
||||||
head = backend.chain.CurrentBlock()
|
head = backend.chain.CurrentBlock()
|
||||||
|
td = backend.chain.GetTd(head.Hash(), head.Number.Uint64())
|
||||||
forkID = forkid.NewID(backend.chain.Config(), backend.chain.Genesis(), backend.chain.CurrentHeader().Number.Uint64(), backend.chain.CurrentHeader().Time)
|
forkID = forkid.NewID(backend.chain.Config(), backend.chain.Genesis(), backend.chain.CurrentHeader().Number.Uint64(), backend.chain.CurrentHeader().Time)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -54,19 +54,19 @@ func testHandshake(t *testing.T, protocol uint) {
|
||||||
want: errNoStatusMsg,
|
want: errNoStatusMsg,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: StatusMsg, data: StatusPacket{10, 1, new(big.Int), head.Hash(), genesis.Hash(), forkID},
|
code: StatusMsg, data: StatusPacket{10, 1, td, head.Hash(), genesis.Hash(), forkID},
|
||||||
want: errProtocolVersionMismatch,
|
want: errProtocolVersionMismatch,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: StatusMsg, data: StatusPacket{uint32(protocol), 999, new(big.Int), head.Hash(), genesis.Hash(), forkID},
|
code: StatusMsg, data: StatusPacket{uint32(protocol), 999, td, head.Hash(), genesis.Hash(), forkID},
|
||||||
want: errNetworkIDMismatch,
|
want: errNetworkIDMismatch,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: StatusMsg, data: StatusPacket{uint32(protocol), 1, new(big.Int), head.Hash(), common.Hash{3}, forkID},
|
code: StatusMsg, data: StatusPacket{uint32(protocol), 1, td, head.Hash(), common.Hash{3}, forkID},
|
||||||
want: errGenesisMismatch,
|
want: errGenesisMismatch,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: StatusMsg, data: StatusPacket{uint32(protocol), 1, new(big.Int), head.Hash(), genesis.Hash(), forkid.ID{Hash: [4]byte{0x00, 0x01, 0x02, 0x03}}},
|
code: StatusMsg, data: StatusPacket{uint32(protocol), 1, td, head.Hash(), genesis.Hash(), forkid.ID{Hash: [4]byte{0x00, 0x01, 0x02, 0x03}}},
|
||||||
want: errForkIDRejected,
|
want: errForkIDRejected,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +82,7 @@ func testHandshake(t *testing.T, protocol uint) {
|
||||||
// Send the junk test with one peer, check the handshake failure
|
// Send the junk test with one peer, check the handshake failure
|
||||||
go p2p.Send(app, test.code, test.data)
|
go p2p.Send(app, test.code, test.data)
|
||||||
|
|
||||||
err := peer.Handshake(1, head.Hash(), genesis.Hash(), forkID, forkid.NewFilter(backend.chain))
|
err := peer.Handshake(1, td, head.Hash(), genesis.Hash(), forkID, forkid.NewFilter(backend.chain))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("test %d: protocol returned nil error, want %q", i, test.want)
|
t.Errorf("test %d: protocol returned nil error, want %q", i, test.want)
|
||||||
} else if !errors.Is(err, test.want) {
|
} else if !errors.Is(err, test.want) {
|
||||||
|
|
|
||||||
|
|
@ -1289,7 +1289,7 @@ func TestTraceChain(t *testing.T) {
|
||||||
func newTestMergedBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *testBackend {
|
func newTestMergedBackend(t *testing.T, n int, gspec *core.Genesis, generator func(i int, b *core.BlockGen)) *testBackend {
|
||||||
backend := &testBackend{
|
backend := &testBackend{
|
||||||
chainConfig: gspec.Config,
|
chainConfig: gspec.Config,
|
||||||
engine: beacon.New(ethash.NewFaker()),
|
engine: beacon.NewFaker(),
|
||||||
chaindb: rawdb.NewMemoryDatabase(),
|
chaindb: rawdb.NewMemoryDatabase(),
|
||||||
}
|
}
|
||||||
// Generate blocks for testing
|
// Generate blocks for testing
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,7 @@ type backend interface {
|
||||||
SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription
|
SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription
|
||||||
CurrentHeader() *types.Header
|
CurrentHeader() *types.Header
|
||||||
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
||||||
|
GetTd(ctx context.Context, hash common.Hash) *big.Int
|
||||||
Stats() (pending int, queued int)
|
Stats() (pending int, queued int)
|
||||||
SyncProgress() ethereum.SyncProgress
|
SyncProgress() ethereum.SyncProgress
|
||||||
|
|
||||||
|
|
@ -707,6 +708,7 @@ func (s *Service) reportBlock(conn *connWrapper, header *types.Header) error {
|
||||||
func (s *Service) assembleBlockStats(header *types.Header) *blockStats {
|
func (s *Service) assembleBlockStats(header *types.Header) *blockStats {
|
||||||
// Gather the block infos from the local blockchain
|
// Gather the block infos from the local blockchain
|
||||||
var (
|
var (
|
||||||
|
td *big.Int
|
||||||
txs []txStats
|
txs []txStats
|
||||||
uncles []*types.Header
|
uncles []*types.Header
|
||||||
)
|
)
|
||||||
|
|
@ -722,6 +724,8 @@ func (s *Service) assembleBlockStats(header *types.Header) *blockStats {
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
td = fullBackend.GetTd(context.Background(), header.Hash())
|
||||||
|
|
||||||
txs = make([]txStats, len(block.Transactions()))
|
txs = make([]txStats, len(block.Transactions()))
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
txs[i].Hash = tx.Hash()
|
txs[i].Hash = tx.Hash()
|
||||||
|
|
@ -733,6 +737,7 @@ func (s *Service) assembleBlockStats(header *types.Header) *blockStats {
|
||||||
if header == nil {
|
if header == nil {
|
||||||
header = s.backend.CurrentHeader()
|
header = s.backend.CurrentHeader()
|
||||||
}
|
}
|
||||||
|
td = s.backend.GetTd(context.Background(), header.Hash())
|
||||||
txs = []txStats{}
|
txs = []txStats{}
|
||||||
}
|
}
|
||||||
// Assemble and return the block stats
|
// Assemble and return the block stats
|
||||||
|
|
@ -747,7 +752,7 @@ func (s *Service) assembleBlockStats(header *types.Header) *blockStats {
|
||||||
GasUsed: header.GasUsed,
|
GasUsed: header.GasUsed,
|
||||||
GasLimit: header.GasLimit,
|
GasLimit: header.GasLimit,
|
||||||
Diff: header.Difficulty.String(),
|
Diff: header.Difficulty.String(),
|
||||||
TotalDiff: "0", // unknown post-merge with pruned chain tail
|
TotalDiff: td.String(),
|
||||||
Txs: txs,
|
Txs: txs,
|
||||||
TxHash: header.TxHash,
|
TxHash: header.TxHash,
|
||||||
Root: header.Root,
|
Root: header.Root,
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
"github.com/ethereum/go-ethereum/consensus/beacon"
|
"github.com/ethereum/go-ethereum/consensus/beacon"
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
|
@ -466,7 +467,7 @@ func newGQLService(t *testing.T, stack *node.Node, shanghai bool, gspec *core.Ge
|
||||||
RPCGasCap: 1000000,
|
RPCGasCap: 1000000,
|
||||||
StateScheme: rawdb.HashScheme,
|
StateScheme: rawdb.HashScheme,
|
||||||
}
|
}
|
||||||
var engine = beacon.New(ethash.NewFaker())
|
var engine consensus.Engine = ethash.NewFaker()
|
||||||
if shanghai {
|
if shanghai {
|
||||||
engine = beacon.NewFaker()
|
engine = beacon.NewFaker()
|
||||||
gspec.Config.TerminalTotalDifficulty = common.Big0
|
gspec.Config.TerminalTotalDifficulty = common.Big0
|
||||||
|
|
|
||||||
|
|
@ -564,6 +564,12 @@ func (b testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.R
|
||||||
receipts := rawdb.ReadReceipts(b.db, hash, header.Number.Uint64(), header.Time, b.chain.Config())
|
receipts := rawdb.ReadReceipts(b.db, hash, header.Number.Uint64(), header.Time, b.chain.Config())
|
||||||
return receipts, nil
|
return receipts, nil
|
||||||
}
|
}
|
||||||
|
func (b testBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
|
||||||
|
if b.pending != nil && hash == b.pending.Hash() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return big.NewInt(1)
|
||||||
|
}
|
||||||
func (b testBackend) GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockContext *vm.BlockContext) *vm.EVM {
|
func (b testBackend) GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockContext *vm.BlockContext) *vm.EVM {
|
||||||
if vmConfig == nil {
|
if vmConfig == nil {
|
||||||
vmConfig = b.chain.GetVMConfig()
|
vmConfig = b.chain.GetVMConfig()
|
||||||
|
|
|
||||||
|
|
@ -380,6 +380,7 @@ func (b *backendMock) GetLogs(ctx context.Context, blockHash common.Hash, number
|
||||||
//nolint:nilnil
|
//nolint:nilnil
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
func (b *backendMock) GetTd(ctx context.Context, hash common.Hash) *big.Int { return nil }
|
||||||
func (b *backendMock) GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM {
|
func (b *backendMock) GetEVM(ctx context.Context, state *state.StateDB, header *types.Header, vmConfig *vm.Config, blockCtx *vm.BlockContext) *vm.EVM {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,8 +57,8 @@ func TestBlockchain(t *testing.T) {
|
||||||
bt.skipLoad(`.*InvalidBlocks*`)
|
bt.skipLoad(`.*InvalidBlocks*`)
|
||||||
bt.skipLoad(`.*TransitionTests*`)
|
bt.skipLoad(`.*TransitionTests*`)
|
||||||
|
|
||||||
// With chain history removal, TDs become unavailable, this transition tests based on TTD are unrunnable
|
// // With chain history removal, TDs become unavailable, this transition tests based on TTD are unrunnable
|
||||||
bt.skipLoad(`.*bcArrowGlacierToParis/powToPosBlockRejection.json`)
|
// bt.skipLoad(`.*bcArrowGlacierToParis/powToPosBlockRejection.json`)
|
||||||
|
|
||||||
// This directory contains no test.
|
// This directory contains no test.
|
||||||
bt.skipLoad(`.*\.meta/.*`)
|
bt.skipLoad(`.*\.meta/.*`)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue