fix: fix errors and move validation code

This commit is contained in:
healthykim 2025-10-30 17:20:13 +09:00
parent bbf46450ba
commit c0c22a9c2c

View file

@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
) )
const ( const (
@ -59,6 +60,8 @@ type fetchRequest struct {
From uint64 // Requested chain element index (used for skeleton fills only) From uint64 // Requested chain element index (used for skeleton fills only)
Headers []*types.Header // Requested headers, sorted by request order Headers []*types.Header // Requested headers, sorted by request order
Time time.Time // Time when the request was made Time time.Time // Time when the request was made
FromIndex uint64 // TODO: Can we reuse `From` field here?
} }
// fetchResult is a struct collecting partial results from data fetchers until // fetchResult is a struct collecting partial results from data fetchers until
@ -568,7 +571,7 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, txListH
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()
validate := func(index int, header *types.Header) error { validate := func(index int, header *types.Header, _ *fetchResult) error {
if txListHashes[index] != header.TxHash { if txListHashes[index] != header.TxHash {
return errInvalidBody return errInvalidBody
} }
@ -640,19 +643,18 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt, recei
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()
validate := func(index int, header *types.Header) error { validate := func(index int, header *types.Header, result *fetchResult) error {
if receiptListHashes[index] != header.ReceiptHash { // Case 1) last block, incomplete
if lastBlockIncomplete && index == len(receiptList)-1 {
if result == nil {
return errInvalidReceipt return errInvalidReceipt
} }
return nil
}
reconstruct := func(index int, result *fetchResult) int {
result.Receipts = append(result.Receipts, receiptList[index]...)
if lastBlockIncomplete && index == len(receiptList) {
// 1. Verify the total number of tx delivered // 1. Verify the total number of tx delivered
if uint64(len(receiptList[index])) > result.Header.GasUsed/21_000 { if uint64(len(result.Receipts)+len(receiptList[index])) > result.Header.GasUsed/21_000 {
result.Receipts = []*types.Receipt{} result.Receipts = []*types.Receipt{}
return 0 result.receiptSize = 0
return errInvalidReceipt
} }
// 2. Verify the size of each receipt against the gas limit of the corresponding transaction // 2. Verify the size of each receipt against the gas limit of the corresponding transaction
for _, rc := range receiptList[index] { for _, rc := range receiptList[index] {
@ -662,18 +664,46 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt, recei
} }
if logSize > params.MaxTxGas/params.LogDataGas { if logSize > params.MaxTxGas/params.LogDataGas {
result.Receipts = []*types.Receipt{} result.Receipts = []*types.Receipt{}
return 0 result.receiptSize = 0
return errInvalidReceipt
} }
result.receiptSize += logSize result.receiptSize += logSize
} }
// 3. Verify the total download receipt size is no longer than allowed by the block gas limit // 3. Verify the total download receipt size is no longer than allowed by the block gas limit
if result.receiptSize > result.Header.GasLimit/params.LogDataGas { if result.receiptSize > result.Header.GasLimit/params.LogDataGas {
result.Receipts = []*types.Receipt{} result.Receipts = []*types.Receipt{}
return 0 result.receiptSize = 0
return errInvalidReceipt
} }
return nil
}
// Case 2) fist block, partially collected before and completed by this response
if index == 0 && len(result.Receipts) != 0 {
if result == nil {
return errInvalidReceipt
}
var hash common.Hash
hasher := trie.NewStackTrie(nil)
hash = types.DeriveSha(append(result.Receipts, receiptList[index]...), hasher)
if hash != header.ReceiptHash {
return errInvalidReceipt
}
return nil
}
if receiptListHashes[index] != header.ReceiptHash {
return errInvalidReceipt
}
return nil
}
reconstruct := func(index int, result *fetchResult) int {
result.Receipts = append(result.Receipts, receiptList[index]...)
if lastBlockIncomplete && index == len(receiptList)-1 {
return len(result.Receipts) return len(result.Receipts)
} }
// receipt fetch is done
result.SetReceiptsDone() result.SetReceiptsDone()
return -1 return -1
} }
@ -689,21 +719,21 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt, recei
func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header,
taskQueue *prque.Prque[int64, *types.Header], pendPool map[string]*fetchRequest, taskQueue *prque.Prque[int64, *types.Header], pendPool map[string]*fetchRequest,
reqTimer *metrics.Timer, resInMeter, resDropMeter *metrics.Meter, reqTimer *metrics.Timer, resInMeter, resDropMeter *metrics.Meter,
results int, validate func(index int, header *types.Header) error, resultCount int, validate func(index int, header *types.Header, result *fetchResult) error,
reconstruct func(index int, result *fetchResult) int) (int, error) { reconstruct func(index int, result *fetchResult) int) (int, error) {
// Short circuit if the data was never requested // Short circuit if the data was never requested
request := pendPool[id] request := pendPool[id]
if request == nil { if request == nil {
resDropMeter.Mark(int64(results)) resDropMeter.Mark(int64(resultCount))
return 0, errNoFetchesPending return 0, errNoFetchesPending
} }
delete(pendPool, id) delete(pendPool, id)
reqTimer.UpdateSince(request.Time) reqTimer.UpdateSince(request.Time)
resInMeter.Mark(int64(results)) resInMeter.Mark(int64(resultCount))
// If no data items were retrieved, mark them as unavailable for the origin peer // If no data items were retrieved, mark them as unavailable for the origin peer
if results == 0 { if resultCount == 0 {
for _, header := range request.Headers { for _, header := range request.Headers {
request.Peer.MarkLacking(header.Hash()) request.Peer.MarkLacking(header.Hash())
} }
@ -714,30 +744,38 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header,
failure error failure error
i int i int
hashes []common.Hash hashes []common.Hash
results []*fetchResult
) )
for _, header := range request.Headers { for _, header := range request.Headers {
// Short circuit assembly if no more fetch results are found // Short circuit assembly if no more fetch results are found
if i >= results { if i >= resultCount {
break break
} }
// Validate the fields // Validate the fields
if err := validate(i, header); err != nil { res, stale, err := q.resultCache.GetDeliverySlot(header.Number.Uint64())
results = append(results, res)
// If stale || err != nil, res is set to nil.
if err := validate(i, header, res); err != nil {
failure = err failure = err
break break
} }
if !stale && err == nil {
log.Error("Delivery stale", "stale", stale, "number", header.Number.Uint64(), "err", err)
}
hashes = append(hashes, header.Hash()) hashes = append(hashes, header.Hash())
i++ i++
} }
for _, header := range request.Headers[:i] { for _, header := range request.Headers[:i] {
resume := -1 resume := -1
if res, stale, err := q.resultCache.GetDeliverySlot(header.Number.Uint64()); err == nil && !stale { if results[accepted] != nil {
resume = reconstruct(accepted, res) resume = reconstruct(accepted, results[accepted])
} else { } else {
// else: between here and above, some other peer filled this result, // else: between here and above, some other peer filled this result,
// or it was indeed a no-op. This should not happen, but if it does it's // or it was indeed a no-op. This should not happen, but if it does it's
// not something to panic about // not something to panic about
log.Error("Delivery stale", "stale", stale, "number", header.Number.Uint64(), "err", err)
failure = errStaleDelivery failure = errStaleDelivery
} }
// Clean up a successful fetch // Clean up a successful fetch
@ -749,7 +787,7 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header,
} }
accepted++ accepted++
} }
resDropMeter.Mark(int64(results - accepted)) resDropMeter.Mark(int64(resultCount - accepted))
// Return all failed or missing fetches to the queue // Return all failed or missing fetches to the queue
for _, header := range request.Headers[accepted:] { for _, header := range request.Headers[accepted:] {