Skip to content

Commit

Permalink
Fix invalid pre-fetched header broadcast (erigontech#8442)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mh0lt authored Oct 12, 2023
1 parent 02032ad commit 6f7186e
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 18 deletions.
8 changes: 8 additions & 0 deletions cmd/devnet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
15 changes: 7 additions & 8 deletions cmd/sentry/sentry/broadcast.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down
2 changes: 1 addition & 1 deletion cmd/sentry/sentry/sentry_multi_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions consensus/bor/bor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
{
Expand Down
16 changes: 16 additions & 0 deletions core/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 6 additions & 7 deletions turbo/stages/bodydownload/body_algos.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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
}

Expand Down
5 changes: 4 additions & 1 deletion turbo/stages/bodydownload/body_data_struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand All @@ -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,
Expand All @@ -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
}
2 changes: 1 addition & 1 deletion turbo/stages/bodydownload/body_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down

0 comments on commit 6f7186e

Please sign in to comment.