Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions eth/downloader/downloader.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,16 +158,12 @@ type Downloader struct {
syncStartBlock uint64 // Head snap block when Geth was started
syncStartTime time.Time // Time instance when chain sync started
syncLogTime time.Time // Time instance when status was last reported

// Chain ID for downloaders to reference
chainID uint64
}

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

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

// New creates a new downloader to fetch hashes and blocks from remote peers.
func New(stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, dropPeer peerDropFn, success func(), chainID uint64) *Downloader {
func New(stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, dropPeer peerDropFn, success func()) *Downloader {
cutoffNumber, cutoffHash := chain.HistoryPruningCutoff()
dl := &Downloader{
stateDB: stateDb,
Expand All @@ -245,7 +241,6 @@ func New(stateDb ethdb.Database, mux *event.TypeMux, chain BlockChain, dropPeer
SnapSyncer: snap.NewSyncer(stateDb, chain.TrieDB().Scheme()),
stateSyncStart: make(chan *stateSync),
syncStartBlock: chain.CurrentSnapBlock().Number.Uint64(),
chainID: chainID,
}
// Create the post-merge skeleton syncer and start the process
dl.skeleton = newSkeleton(stateDb, dl.peers, dropPeer, newBeaconBackfiller(dl, success))
Expand Down Expand Up @@ -1058,7 +1053,11 @@ func (d *Downloader) commitSnapSyncData(results []*fetchResult, stateSync *state
receipts := make([]rlp.RawValue, len(results))
for i, result := range results {
blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.body())
receipts[i] = correctReceiptsRLP(result.Receipts, result.Transactions, blocks[i].NumberU64(), d.chainID)
recs := result.Receipts
if cfg := d.blockchain.Config(); cfg.IsOptimism() && !cfg.IsCanyon(blocks[i].Time()) {
recs = correctReceipts(recs, result.Transactions, blocks[i].NumberU64(), d.blockchain.Config().ChainID)
}
receipts[i] = recs
}
if index, err := d.blockchain.InsertReceiptChain(blocks, receipts, d.ancientLimit); err != nil {
log.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err)
Expand Down
2 changes: 1 addition & 1 deletion eth/downloader/downloader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func newTesterWithNotification(t *testing.T, success func()) *downloadTester {
chain: chain,
peers: make(map[string]*downloadTesterPeer),
}
tester.downloader = New(db, new(event.TypeMux), tester.chain, tester.dropPeer, success, 0)
tester.downloader = New(db, new(event.TypeMux), tester.chain, tester.dropPeer, success)
return tester
}

Expand Down
72 changes: 32 additions & 40 deletions eth/downloader/receiptreference.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ var (
systemAddress = common.HexToAddress("0xDeaDDEaDDeAdDeAdDEAdDEaddeAddEAdDEAd0001")
receiptReferencePath = "userDepositData"
//go:embed userDepositData/*.gob
receiptReference embed.FS
userDepositNoncesAlreadySearched = map[uint64]bool{}
userDepositNoncesReference = map[uint64]userDepositNonces{}
receiptReference embed.FS
userDepositNoncesInitialized = map[uint64]bool{}
userDepositNoncesReference = map[uint64]userDepositNonces{}
)

// lazy load the reference data for the requested chain
// if this chain data was already requested, returns early
func initReceiptReferences(chainID uint64) {
// if already loaded, return
if userDepositNoncesAlreadySearched[chainID] {
if userDepositNoncesInitialized[chainID] {
return
}
// look for a file prefixed by the chainID
Expand All @@ -48,7 +48,7 @@ func initReceiptReferences(chainID uint64) {
return
}
// mark as loaded so we don't try again, even if no files match
userDepositNoncesAlreadySearched[chainID] = true
userDepositNoncesInitialized[chainID] = true
for _, file := range files {
// skip files which don't match the prefix
if !strings.HasPrefix(file.Name(), fPrefix) {
Expand All @@ -67,8 +67,11 @@ func initReceiptReferences(chainID uint64) {
continue
}
userDepositNoncesReference[udns.ChainID] = udns
log.Info("Receipt Correction: Reference data initialized", "file", fpath,
"chainID", chainID, "count", len(udns.Results), "first", udns.First, "last", udns.Last)
return
}
log.Info("Receipt Correction: No reference data for chain", "chainID", chainID)
}

// correctReceipts corrects the deposit nonce in the receipts using the reference data
Expand All @@ -77,13 +80,19 @@ func initReceiptReferences(chainID uint64) {
// This function inspects transaction data for user deposits, and if it is found to be incorrect, it is corrected.
// The data used to correct the deposit nonce is stored in the userDepositData directory,
// and was generated with the receipt reference tool in the optimism monorepo.
func correctReceipts(receipts types.Receipts, transactions types.Transactions, blockNumber uint64, chainID uint64) types.Receipts {
func correctReceipts(receiptsRLP rlp.RawValue, transactions types.Transactions, blockNumber uint64, chainIDBig *big.Int) rlp.RawValue {
if chainIDBig == nil || !chainIDBig.IsUint64() {
// Known chains that need receipt correction have uint64 chain IDs
return receiptsRLP
}
chainID := chainIDBig.Uint64()
initReceiptReferences(chainID)

// if there is no data even after initialization, return the receipts as is
depositNoncesForChain, ok := userDepositNoncesReference[chainID]
if !ok {
log.Trace("Receipt Correction: No data source for chain", "chainID", chainID)
return receipts
return receiptsRLP
}

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

// get the block nonces
blockNonces, ok := depositNoncesForChain.Results[blockNumber]
if !ok {
log.Trace("Receipt Correction: Block does not contain user deposits", "blockNumber", blockNumber)
return receipts
return receiptsRLP
}

// Decode RLP receipts to Receipt structs
var receipts []*types.ReceiptForStorage
if err := rlp.DecodeBytes(receiptsRLP, &receipts); err != nil {
log.Warn("Receipt Correction: Failed to decode RLP receipts", "err", err)
return receiptsRLP
}

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

tx := transactions[i]
from, err := types.Sender(signer, tx)
if err != nil {
log.Warn("Receipt Correction: Failed to determine sender", "err", err)
continue
}
// ignore any transactions from the system address
if from != systemAddress {
if from := tx.From(); from != systemAddress {
// prevent index out of range (indicates a problem with the reference data or the block data)
if udCount >= len(blockNonces) {
log.Warn("Receipt Correction: More user deposits in block than included in reference data", "in_reference", len(blockNonces))
break
}
nonce := blockNonces[udCount]
udCount++
log.Trace("Receipt Correction: User Deposit detected", "address", from, "nonce", nonce)
log.Trace("Receipt Correction: User Deposit detected", "from", from, "nonce", nonce)
if nonce != *r.DepositNonce {
// correct the deposit nonce
// warn because this should not happen unless the data was modified by corruption or a malicious peer
// by correcting the nonce, the entire block is still valid for use
log.Warn("Receipt Correction: Corrected deposit nonce", "nonce", *r.DepositNonce, "corrected", nonce)
log.Warn("Receipt Correction: Corrected deposit nonce", "from", from, "nonce", *r.DepositNonce, "corrected", nonce)
r.DepositNonce = &nonce
}
}
Expand All @@ -144,29 +153,12 @@ func correctReceipts(receipts types.Receipts, transactions types.Transactions, b
}

log.Trace("Receipt Correction: Completed", "blockNumber", blockNumber, "userDeposits", udCount, "receipts", len(receipts), "transactions", len(transactions))
return receipts
}

// correctReceiptsRLP corrects the deposit nonce in the receipts using the reference data
// This function works with RLP encoded receipts, decoding them to Receipt structs,
// applying corrections, and re-encoding them back to RLP.
func correctReceiptsRLP(receiptsRLP rlp.RawValue, transactions types.Transactions, blockNumber uint64, chainID uint64) rlp.RawValue {
// Decode RLP receipts to Receipt structs
var receipts types.Receipts
if err := rlp.DecodeBytes(receiptsRLP, &receipts); err != nil {
log.Warn("Receipt Correction: Failed to decode RLP receipts", "err", err)
return receiptsRLP
}

// Apply corrections using existing correctReceipts function
correctedReceipts := correctReceipts(receipts, transactions, blockNumber, chainID)

// Re-encode to RLP
encoded, err := rlp.EncodeToBytes(correctedReceipts)
encoded, err := rlp.EncodeToBytes(receipts)
if err != nil {
log.Warn("Receipt Correction: Failed to encode corrected receipts to RLP", "err", err)
return receiptsRLP
}

return encoded
}
Loading