Skip to content

Commit b51c72e

Browse files
authored
eth/downloader: Fix deposit receipt correction (#680)
1 parent 0dc986b commit b51c72e

File tree

5 files changed

+99
-203
lines changed

5 files changed

+99
-203
lines changed

eth/downloader/downloader.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -158,16 +158,12 @@ type Downloader struct {
158158
syncStartBlock uint64 // Head snap block when Geth was started
159159
syncStartTime time.Time // Time instance when chain sync started
160160
syncLogTime time.Time // Time instance when status was last reported
161-
162-
// Chain ID for downloaders to reference
163-
chainID uint64
164161
}
165162

166163
// BlockChain encapsulates functions required to sync a (full or snap) blockchain.
167164
type BlockChain interface {
168165
// Config returns the chain configuration.
169166
// OP-Stack diff, to adjust withdrawal-hash verification.
170-
// Usage of ths in the Downloader is discouraged.
171167
Config() *params.ChainConfig
172168

173169
// HasHeader verifies a header's presence in the local chain.
@@ -229,7 +225,7 @@ type BlockChain interface {
229225
}
230226

231227
// New creates a new downloader to fetch hashes and blocks from remote peers.
232-
func New(stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, dropPeer peerDropFn, success func(), chainID uint64) *Downloader {
228+
func New(stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, dropPeer peerDropFn, success func()) *Downloader {
233229
cutoffNumber, cutoffHash := chain.HistoryPruningCutoff()
234230
dl := &Downloader{
235231
stateDB: stateDb,
@@ -245,7 +241,6 @@ func New(stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, dropPeer
245241
SnapSyncer: snap.NewSyncer(stateDb, chain.TrieDB().Scheme()),
246242
stateSyncStart: make(chan *stateSync),
247243
syncStartBlock: chain.CurrentSnapBlock().Number.Uint64(),
248-
chainID: chainID,
249244
}
250245
// Create the post-merge skeleton syncer and start the process
251246
dl.skeleton = newSkeleton(stateDb, dl.peers, dropPeer, newBeaconBackfiller(dl, success))
@@ -1058,7 +1053,11 @@ func (d *Downloader) commitSnapSyncData(results []*fetchResult, stateSync *state
10581053
receipts := make([]rlp.RawValue, len(results))
10591054
for i, result := range results {
10601055
blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.body())
1061-
receipts[i] = correctReceiptsRLP(result.Receipts, result.Transactions, blocks[i].NumberU64(), d.chainID)
1056+
recs := result.Receipts
1057+
if cfg := d.blockchain.Config(); cfg.IsOptimism() && !cfg.IsCanyon(blocks[i].Time()) {
1058+
recs = correctReceipts(recs, result.Transactions, blocks[i].NumberU64(), d.blockchain.Config().ChainID)
1059+
}
1060+
receipts[i] = recs
10621061
}
10631062
if index, err := d.blockchain.InsertReceiptChain(blocks, receipts, d.ancientLimit); err != nil {
10641063
log.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err)

eth/downloader/downloader_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func newTesterWithNotification(t *testing.T, success func()) *downloadTester {
7575
chain: chain,
7676
peers: make(map[string]*downloadTesterPeer),
7777
}
78-
tester.downloader = New(db, new(event.TypeMux), tester.chain, tester.dropPeer, success, 0)
78+
tester.downloader = New(db, new(event.TypeMux), tester.chain, tester.dropPeer, success)
7979
return tester
8080
}
8181

eth/downloader/receiptreference.go

Lines changed: 32 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,16 @@ var (
2828
systemAddress = common.HexToAddress("0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001")
2929
receiptReferencePath = "userDepositData"
3030
//go:embed userDepositData/*.gob
31-
receiptReference embed.FS
32-
userDepositNoncesAlreadySearched = map[uint64]bool{}
33-
userDepositNoncesReference = map[uint64]userDepositNonces{}
31+
receiptReference embed.FS
32+
userDepositNoncesInitialized = map[uint64]bool{}
33+
userDepositNoncesReference = map[uint64]userDepositNonces{}
3434
)
3535

3636
// lazy load the reference data for the requested chain
3737
// if this chain data was already requested, returns early
3838
func initReceiptReferences(chainID uint64) {
3939
// if already loaded, return
40-
if userDepositNoncesAlreadySearched[chainID] {
40+
if userDepositNoncesInitialized[chainID] {
4141
return
4242
}
4343
// look for a file prefixed by the chainID
@@ -48,7 +48,7 @@ func initReceiptReferences(chainID uint64) {
4848
return
4949
}
5050
// mark as loaded so we don't try again, even if no files match
51-
userDepositNoncesAlreadySearched[chainID] = true
51+
userDepositNoncesInitialized[chainID] = true
5252
for _, file := range files {
5353
// skip files which don't match the prefix
5454
if !strings.HasPrefix(file.Name(), fPrefix) {
@@ -67,8 +67,11 @@ func initReceiptReferences(chainID uint64) {
6767
continue
6868
}
6969
userDepositNoncesReference[udns.ChainID] = udns
70+
log.Info("Receipt Correction: Reference data initialized", "file", fpath,
71+
"chainID", chainID, "count", len(udns.Results), "first", udns.First, "last", udns.Last)
7072
return
7173
}
74+
log.Info("Receipt Correction: No reference data for chain", "chainID", chainID)
7275
}
7376

7477
// correctReceipts corrects the deposit nonce in the receipts using the reference data
@@ -77,13 +80,19 @@ func initReceiptReferences(chainID uint64) {
7780
// This function inspects transaction data for user deposits, and if it is found to be incorrect, it is corrected.
7881
// The data used to correct the deposit nonce is stored in the userDepositData directory,
7982
// and was generated with the receipt reference tool in the optimism monorepo.
80-
func correctReceipts(receipts types.Receipts, transactions types.Transactions, blockNumber uint64, chainID uint64) types.Receipts {
83+
func correctReceipts(receiptsRLP rlp.RawValue, transactions types.Transactions, blockNumber uint64, chainIDBig *big.Int) rlp.RawValue {
84+
if chainIDBig == nil || !chainIDBig.IsUint64() {
85+
// Known chains that need receipt correction have uint64 chain IDs
86+
return receiptsRLP
87+
}
88+
chainID := chainIDBig.Uint64()
8189
initReceiptReferences(chainID)
90+
8291
// if there is no data even after initialization, return the receipts as is
8392
depositNoncesForChain, ok := userDepositNoncesReference[chainID]
8493
if !ok {
8594
log.Trace("Receipt Correction: No data source for chain", "chainID", chainID)
86-
return receipts
95+
return receiptsRLP
8796
}
8897

8998
// check that the block number being examined is within the range of the reference data
@@ -92,48 +101,48 @@ func correctReceipts(receipts types.Receipts, transactions types.Transactions, b
92101
"blockNumber", blockNumber,
93102
"start", depositNoncesForChain.First,
94103
"end", depositNoncesForChain.Last)
95-
return receipts
104+
return receiptsRLP
96105
}
97106

98107
// get the block nonces
99108
blockNonces, ok := depositNoncesForChain.Results[blockNumber]
100109
if !ok {
101110
log.Trace("Receipt Correction: Block does not contain user deposits", "blockNumber", blockNumber)
102-
return receipts
111+
return receiptsRLP
112+
}
113+
114+
// Decode RLP receipts to Receipt structs
115+
var receipts []*types.ReceiptForStorage
116+
if err := rlp.DecodeBytes(receiptsRLP, &receipts); err != nil {
117+
log.Warn("Receipt Correction: Failed to decode RLP receipts", "err", err)
118+
return receiptsRLP
103119
}
104120

105-
signer := types.LatestSignerForChainID(big.NewInt(int64(chainID)))
106121
// iterate through the receipts and transactions to correct the deposit nonce
107122
// user deposits should always be at the front of the block, but we will check all transactions to be sure
108123
udCount := 0
109-
for i := 0; i < len(receipts); i++ {
110-
r := receipts[i]
124+
for i, r := range receipts {
125+
tx := transactions[i]
111126
// break as soon as a non deposit is found
112-
if r.Type != types.DepositTxType {
127+
if tx.Type() != types.DepositTxType {
113128
break
114129
}
115130

116-
tx := transactions[i]
117-
from, err := types.Sender(signer, tx)
118-
if err != nil {
119-
log.Warn("Receipt Correction: Failed to determine sender", "err", err)
120-
continue
121-
}
122131
// ignore any transactions from the system address
123-
if from != systemAddress {
132+
if from := tx.From(); from != systemAddress {
124133
// prevent index out of range (indicates a problem with the reference data or the block data)
125134
if udCount >= len(blockNonces) {
126135
log.Warn("Receipt Correction: More user deposits in block than included in reference data", "in_reference", len(blockNonces))
127136
break
128137
}
129138
nonce := blockNonces[udCount]
130139
udCount++
131-
log.Trace("Receipt Correction: User Deposit detected", "address", from, "nonce", nonce)
140+
log.Trace("Receipt Correction: User Deposit detected", "from", from, "nonce", nonce)
132141
if nonce != *r.DepositNonce {
133142
// correct the deposit nonce
134143
// warn because this should not happen unless the data was modified by corruption or a malicious peer
135144
// by correcting the nonce, the entire block is still valid for use
136-
log.Warn("Receipt Correction: Corrected deposit nonce", "nonce", *r.DepositNonce, "corrected", nonce)
145+
log.Warn("Receipt Correction: Corrected deposit nonce", "from", from, "nonce", *r.DepositNonce, "corrected", nonce)
137146
r.DepositNonce = &nonce
138147
}
139148
}
@@ -144,29 +153,12 @@ func correctReceipts(receipts types.Receipts, transactions types.Transactions, b
144153
}
145154

146155
log.Trace("Receipt Correction: Completed", "blockNumber", blockNumber, "userDeposits", udCount, "receipts", len(receipts), "transactions", len(transactions))
147-
return receipts
148-
}
149-
150-
// correctReceiptsRLP corrects the deposit nonce in the receipts using the reference data
151-
// This function works with RLP encoded receipts, decoding them to Receipt structs,
152-
// applying corrections, and re-encoding them back to RLP.
153-
func correctReceiptsRLP(receiptsRLP rlp.RawValue, transactions types.Transactions, blockNumber uint64, chainID uint64) rlp.RawValue {
154-
// Decode RLP receipts to Receipt structs
155-
var receipts types.Receipts
156-
if err := rlp.DecodeBytes(receiptsRLP, &receipts); err != nil {
157-
log.Warn("Receipt Correction: Failed to decode RLP receipts", "err", err)
158-
return receiptsRLP
159-
}
160-
161-
// Apply corrections using existing correctReceipts function
162-
correctedReceipts := correctReceipts(receipts, transactions, blockNumber, chainID)
163156

164157
// Re-encode to RLP
165-
encoded, err := rlp.EncodeToBytes(correctedReceipts)
158+
encoded, err := rlp.EncodeToBytes(receipts)
166159
if err != nil {
167160
log.Warn("Receipt Correction: Failed to encode corrected receipts to RLP", "err", err)
168161
return receiptsRLP
169162
}
170-
171163
return encoded
172164
}

0 commit comments

Comments
 (0)