Fix invalid pre-fetched header broadcast (#8442)

Fixes and issue with Polygon validators where locally mined blocks are
broadcast with invalid header hashes because the NewBlock message
constructor was removing the ReceiptHash which contributed to the header
hash.

The results in the bor header validation code not being able to
correctly identify the signer of the header - so header validation
fails.

This also likely fixes part of the bogon-block issue which was
identified by the polygon team.
This commit is contained in:
Mark Holt 2023-10-12 08:27:02 +01:00 committed by GitHub
parent 02032ada42
commit 6f7186e0f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 48 additions and 18 deletions

View File

@ -399,6 +399,14 @@ func initDevnet(ctx *cli.Context, logger log.Logger) (devnet.Devnet, error) {
},
AccountSlots: 200,
},
/*args.BlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",
DirVerbosity: "5",
HeimdallGRpc: heimdallGrpc,
},
AccountSlots: 200,
},*/
args.NonBlockProducer{
Node: args.Node{
ConsoleVerbosity: "0",

View File

@ -55,21 +55,20 @@ func (cs *MultiClient) BroadcastNewBlock(ctx context.Context, header *types.Head
cs.lock.RLock()
defer cs.lock.RUnlock()
txs := make([]types.Transaction, len(body.Transactions))
for i, tx := range body.Transactions {
var err error
if txs[i], err = types.DecodeTransaction(tx); err != nil {
log.Error("broadcastNewBlock", "err", err)
return
}
block, err := types.RawBlock{Header: header, Body: body}.AsBlock()
if err != nil {
log.Error("broadcastNewBlock", "err", err)
}
data, err := rlp.EncodeToBytes(&eth.NewBlockPacket{
Block: types.NewBlock(header, txs, body.Uncles, nil, body.Withdrawals),
Block: block,
TD: td,
})
if err != nil {
log.Error("broadcastNewBlock", "err", err)
return
}
req66 := proto_sentry.SendMessageToRandomPeersRequest{

View File

@ -306,7 +306,7 @@ func NewMultiClient(
if err := hd.RecoverFromDb(db); err != nil {
return nil, fmt.Errorf("recovery from DB failed: %w", err)
}
bd := bodydownload.NewBodyDownload(engine, blockBufferSize, int(syncCfg.BodyCacheLimit), blockReader)
bd := bodydownload.NewBodyDownload(engine, blockBufferSize, int(syncCfg.BodyCacheLimit), blockReader, logger)
cs := &MultiClient{
nodeName: nodeName,

View File

@ -261,6 +261,11 @@ func newValidator(t *testing.T, heimdall *test_heimdall, blocks map[uint64]*type
validatorKey, _ := crypto.GenerateKey()
validatorAddress := crypto.PubkeyToAddress(validatorKey.PublicKey)
/*fmt.Printf("Private: 0x%s\nPublic: 0x%s\nAddress: %s\n",
hex.EncodeToString(crypto.FromECDSA(validatorKey)),
hex.EncodeToString(crypto.MarshalPubkey(&validatorKey.PublicKey)),
strings.ToLower(validatorAddress.Hex()))*/
if heimdall.validatorSet == nil {
heimdall.validatorSet = valset.NewValidatorSet([]*valset.Validator{
{

View File

@ -641,6 +641,22 @@ type RawBlock struct {
Body *RawBody
}
func (r RawBlock) AsBlock() (*Block, error) {
b := &Block{header: r.Header}
b.uncles = r.Body.Uncles
b.withdrawals = r.Body.Withdrawals
txs := make([]Transaction, len(r.Body.Transactions))
for i, tx := range r.Body.Transactions {
var err error
if txs[i], err = DecodeTransaction(tx); err != nil {
return nil, err
}
}
return b, nil
}
// Block represents an entire block in the Ethereum blockchain.
type Block struct {
header *Header

View File

@ -11,7 +11,6 @@ import (
"github.com/ledgerwatch/erigon-lib/common/dbg"
"github.com/ledgerwatch/erigon-lib/common/length"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/log/v3"
"golang.org/x/exp/maps"
"github.com/ledgerwatch/erigon/core/rawdb"
@ -203,12 +202,12 @@ func (bd *BodyDownload) checkPrefetchedBlock(hash libcommon.Hash, tx kv.RwTx, bl
// Calculate the TD of the block (it's not imported yet, so block.Td is not valid)
if header.Difficulty.Sign() != 0 { // don't propagate proof-of-stake blocks
if parent, err := rawdb.ReadTd(tx, header.ParentHash, header.Number.Uint64()-1); err != nil {
log.Error("Failed to ReadTd", "err", err, "number", header.Number.Uint64()-1, "hash", header.ParentHash)
bd.logger.Error("Failed to ReadTd", "err", err, "number", header.Number.Uint64()-1, "hash", header.ParentHash)
} else if parent != nil {
td := new(big.Int).Add(header.Difficulty, parent)
go blockPropagator(context.Background(), header, body, td)
} else {
log.Error("Propagating dangling block", "number", header.Number.Uint64(), "hash", hash)
bd.logger.Error("Propagating dangling block", "number", header.Number.Uint64(), "hash", hash)
}
}
@ -280,16 +279,16 @@ Loop:
}
if delivery.txs == nil {
log.Warn("nil transactions delivered", "peer_id", delivery.peerID, "p2p_msg_len", delivery.lenOfP2PMessage)
bd.logger.Warn("nil transactions delivered", "peer_id", delivery.peerID, "p2p_msg_len", delivery.lenOfP2PMessage)
}
if delivery.uncles == nil {
log.Warn("nil uncles delivered", "peer_id", delivery.peerID, "p2p_msg_len", delivery.lenOfP2PMessage)
bd.logger.Warn("nil uncles delivered", "peer_id", delivery.peerID, "p2p_msg_len", delivery.lenOfP2PMessage)
}
if delivery.withdrawals == nil {
log.Warn("nil withdrawals delivered", "peer_id", delivery.peerID, "p2p_msg_len", delivery.lenOfP2PMessage)
bd.logger.Warn("nil withdrawals delivered", "peer_id", delivery.peerID, "p2p_msg_len", delivery.lenOfP2PMessage)
}
if delivery.txs == nil || delivery.uncles == nil || delivery.withdrawals == nil {
log.Debug("delivery body processing has been skipped due to nil tx|data")
bd.logger.Debug("delivery body processing has been skipped due to nil tx|data")
continue
}

View File

@ -6,6 +6,7 @@ import (
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/common/length"
"github.com/ledgerwatch/erigon/turbo/services"
"github.com/ledgerwatch/log/v3"
"github.com/ledgerwatch/erigon/consensus"
"github.com/ledgerwatch/erigon/core/types"
@ -51,6 +52,7 @@ type BodyDownload struct {
bodyCacheLimit int // Limit of body Cache size
blockBufferSize int
br services.FullBlockReader
logger log.Logger
}
// BodyRequest is a sketch of the request for block bodies, meaning that access to the database is required to convert it to the actual BlockBodies request (look up hashes of canonical blocks)
@ -62,7 +64,7 @@ type BodyRequest struct {
}
// NewBodyDownload create a new body download state object
func NewBodyDownload(engine consensus.Engine, blockBufferSize, bodyCacheLimit int, br services.FullBlockReader) *BodyDownload {
func NewBodyDownload(engine consensus.Engine, blockBufferSize, bodyCacheLimit int, br services.FullBlockReader, logger log.Logger) *BodyDownload {
bd := &BodyDownload{
requestedMap: make(map[TripleHash]uint64),
bodyCacheLimit: bodyCacheLimit,
@ -82,6 +84,7 @@ func NewBodyDownload(engine consensus.Engine, blockBufferSize, bodyCacheLimit in
bodyCache: btree.NewG[BodyTreeItem](32, func(a, b BodyTreeItem) bool { return a.blockNum < b.blockNum }),
br: br,
blockBufferSize: blockBufferSize,
logger: logger,
}
return bd
}

View File

@ -15,7 +15,7 @@ func TestCreateBodyDownload(t *testing.T) {
tx, err := m.DB.BeginRo(m.Ctx)
require.NoError(t, err)
defer tx.Rollback()
bd := bodydownload.NewBodyDownload(ethash.NewFaker(), 128, 100, m.BlockReader)
bd := bodydownload.NewBodyDownload(ethash.NewFaker(), 128, 100, m.BlockReader, m.Log)
if _, _, _, _, err := bd.UpdateFromDb(tx); err != nil {
t.Fatalf("update from db: %v", err)
}