Skip to content

Commit d41ad33

Browse files
committed
fix(consensus): fix error invalid merkle root on pre-tip-signing replay
Symptom: nodes that resumed syncing from local state could fail checkpoint validation with an invalid merkle root while replaying pre-TIPSigning blocks after restart. Cause: the non-TIP signing transaction replay path rebuilt signer data from raw receipts, but raw receipts may not carry TxHash metadata. As a result, signing transactions could be dropped and checkpoint rewards recalculated with totalSigner=0. Fix: match non-TIP signing transactions to receipts by transaction index when receipt metadata is unavailable, keep the failed-receipt filter intact, and add regression tests covering raw-receipt replay.
1 parent 146252a commit d41ad33

2 files changed

Lines changed: 112 additions & 13 deletions

File tree

consensus/XDPoS/XDPoS.go

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,6 @@ func (x *XDPoS) VerifyHeaders(chain consensus.ChainReader, headers []*types.Head
234234
}
235235
}
236236

237-
238237
if v1headers != nil {
239238
x.EngineV1.VerifyHeaders(chain, v1headers, v1fullVerifies, abort, results)
240239
}
@@ -514,21 +513,18 @@ Caching
514513
// Cache signing transaction data into BlockSingers cache object
515514
func (x *XDPoS) CacheNoneTIPSigningTxs(header *types.Header, txs []*types.Transaction, receipts []*types.Receipt) []*types.Transaction {
516515
signTxs := []*types.Transaction{}
517-
for _, tx := range txs {
516+
for txIndex, tx := range txs {
518517
if tx.IsSigningTransaction() {
519-
var b uint64
520-
for _, r := range receipts {
521-
if r.TxHash == tx.Hash() {
522-
if len(r.PostState) > 0 {
523-
b = types.ReceiptStatusSuccessful
524-
} else {
525-
b = r.Status
526-
}
527-
break
528-
}
518+
receipt := findTransactionReceipt(txIndex, tx.Hash(), receipts)
519+
if receipt == nil {
520+
continue
529521
}
530522

531-
if b == types.ReceiptStatusFailed {
523+
status := receipt.Status
524+
if len(receipt.PostState) > 0 {
525+
status = types.ReceiptStatusSuccessful
526+
}
527+
if status == types.ReceiptStatusFailed {
532528
continue
533529
}
534530

@@ -542,6 +538,21 @@ func (x *XDPoS) CacheNoneTIPSigningTxs(header *types.Header, txs []*types.Transa
542538
return signTxs
543539
}
544540

541+
func findTransactionReceipt(txIndex int, txHash common.Hash, receipts []*types.Receipt) *types.Receipt {
542+
if txIndex < len(receipts) {
543+
receipt := receipts[txIndex]
544+
if receipt != nil && (receipt.TxHash == (common.Hash{}) || receipt.TxHash == txHash) {
545+
return receipt
546+
}
547+
}
548+
for _, receipt := range receipts {
549+
if receipt != nil && receipt.TxHash == txHash {
550+
return receipt
551+
}
552+
}
553+
return nil
554+
}
555+
545556
// Cache
546557
func (x *XDPoS) CacheSigningTxs(hash common.Hash, txs []*types.Transaction) []*types.Transaction {
547558
signTxs := []*types.Transaction{}

consensus/XDPoS/XDPoS_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package XDPoS
22

33
import (
4+
"math/big"
45
"testing"
56

7+
"github.com/XinFinOrg/XDPoSChain/common"
68
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
9+
"github.com/XinFinOrg/XDPoSChain/core/types"
710
"github.com/XinFinOrg/XDPoSChain/params"
811
"github.com/stretchr/testify/assert"
912
)
@@ -16,3 +19,88 @@ func TestAdaptorShouldShareDbWithV1Engine(t *testing.T) {
1619
assert := assert.New(t)
1720
assert.Equal(engine.EngineV1.GetDb(), engine.GetDb())
1821
}
22+
23+
func TestCacheNoneTIPSigningTxsSupportsRawReceiptsWithoutTxHash(t *testing.T) {
24+
database := rawdb.NewMemoryDatabase()
25+
config := params.TestXDPoSMockChainConfig
26+
engine := New(config, database)
27+
28+
signingTx := types.NewTransaction(
29+
0,
30+
common.BlockSignersBinary,
31+
big.NewInt(0),
32+
200000,
33+
big.NewInt(0),
34+
append(common.Hex2Bytes(common.HexSignMethod), make([]byte, 64)...),
35+
)
36+
normalTx := types.NewTransaction(
37+
1,
38+
common.Address{0x1},
39+
big.NewInt(0),
40+
21000,
41+
big.NewInt(0),
42+
nil,
43+
)
44+
receipts := []*types.Receipt{
45+
{Status: types.ReceiptStatusSuccessful},
46+
{Status: types.ReceiptStatusSuccessful},
47+
}
48+
49+
cached := engine.CacheNoneTIPSigningTxs(&types.Header{Number: big.NewInt(1)}, []*types.Transaction{signingTx, normalTx}, receipts)
50+
51+
assert.Len(t, cached, 1)
52+
assert.Equal(t, signingTx.Hash(), cached[0].Hash())
53+
}
54+
55+
func TestCacheNoneTIPSigningTxsSkipsFailedSigningReceiptByIndex(t *testing.T) {
56+
database := rawdb.NewMemoryDatabase()
57+
config := params.TestXDPoSMockChainConfig
58+
engine := New(config, database)
59+
60+
signingTx := types.NewTransaction(
61+
0,
62+
common.BlockSignersBinary,
63+
big.NewInt(0),
64+
200000,
65+
big.NewInt(0),
66+
append(common.Hex2Bytes(common.HexSignMethod), make([]byte, 64)...),
67+
)
68+
receipts := []*types.Receipt{{Status: types.ReceiptStatusFailed}}
69+
70+
cached := engine.CacheNoneTIPSigningTxs(&types.Header{Number: big.NewInt(1)}, []*types.Transaction{signingTx}, receipts)
71+
72+
assert.Empty(t, cached)
73+
}
74+
75+
func TestCacheNoneTIPSigningTxsWithRawReceiptRoundTrip(t *testing.T) {
76+
database := rawdb.NewMemoryDatabase()
77+
config := params.TestXDPoSMockChainConfig
78+
engine := New(config, database)
79+
blockHash := common.HexToHash("0x1234")
80+
blockNumber := uint64(1)
81+
82+
signingTx := types.NewTransaction(
83+
0,
84+
common.BlockSignersBinary,
85+
big.NewInt(0),
86+
200000,
87+
big.NewInt(0),
88+
append(common.Hex2Bytes(common.HexSignMethod), make([]byte, 64)...),
89+
)
90+
receipts := []*types.Receipt{{
91+
Status: types.ReceiptStatusSuccessful,
92+
CumulativeGasUsed: 200000,
93+
TxHash: signingTx.Hash(),
94+
}}
95+
96+
rawdb.WriteReceipts(database, blockHash, blockNumber, receipts)
97+
rawReceipts := rawdb.ReadRawReceipts(database, blockHash, blockNumber)
98+
99+
assert.Len(t, rawReceipts, 1)
100+
assert.Equal(t, common.Hash{}, rawReceipts[0].TxHash)
101+
102+
cached := engine.CacheNoneTIPSigningTxs(&types.Header{Number: big.NewInt(int64(blockNumber))}, []*types.Transaction{signingTx}, rawReceipts)
103+
104+
assert.Len(t, cached, 1)
105+
assert.Equal(t, signingTx.Hash(), cached[0].Hash())
106+
}

0 commit comments

Comments
 (0)