test: add test

This commit is contained in:
healthykim 2025-11-03 16:14:20 +09:00
parent c0c22a9c2c
commit 90df0f42dd
2 changed files with 119 additions and 29 deletions

View file

@ -643,6 +643,7 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt, recei
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()
// Note that result can be nil here
validate := func(index int, header *types.Header, result *fetchResult) error { validate := func(index int, header *types.Header, result *fetchResult) error {
// Case 1) last block, incomplete // Case 1) last block, incomplete
if lastBlockIncomplete && index == len(receiptList)-1 { if lastBlockIncomplete && index == len(receiptList)-1 {
@ -679,11 +680,11 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt, recei
} }
// Case 2) fist block, partially collected before and completed by this response // Case 2) fist block, partially collected before and completed by this response
if index == 0 && len(result.Receipts) != 0 { if index == 0 {
if result == nil { if result == nil {
return errInvalidReceipt return errInvalidReceipt
} }
if len(result.Receipts) != 0 {
var hash common.Hash var hash common.Hash
hasher := trie.NewStackTrie(nil) hasher := trie.NewStackTrie(nil)
hash = types.DeriveSha(append(result.Receipts, receiptList[index]...), hasher) hash = types.DeriveSha(append(result.Receipts, receiptList[index]...), hasher)
@ -693,6 +694,7 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt, recei
} }
return nil return nil
} }
}
if receiptListHashes[index] != header.ReceiptHash { if receiptListHashes[index] != header.ReceiptHash {
return errInvalidReceipt return errInvalidReceipt
@ -752,7 +754,7 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header,
break break
} }
// Validate the fields // Validate the fields
res, stale, err := q.resultCache.GetDeliverySlot(header.Number.Uint64()) res, _, _ := q.resultCache.GetDeliverySlot(header.Number.Uint64())
results = append(results, res) results = append(results, res)
// If stale || err != nil, res is set to nil. // If stale || err != nil, res is set to nil.
@ -760,9 +762,6 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header,
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++

View file

@ -35,15 +35,24 @@ import (
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
) )
type blockConfig struct {
txPeriod int
txCount int
}
var emptyBlock = blockConfig{txPeriod: 0, txCount: 0}
var defaultBlock = blockConfig{txPeriod: 2, txCount: 1}
// makeChain creates a chain of n blocks starting at and including parent. // makeChain creates a chain of n blocks starting at and including parent.
// The returned hash chain is ordered head->parent. // The returned hash chain is ordered head->parent.
// If empty is false, every second block (i%2==0) contains one transaction. // If config.txCount > 0, every config.txPeriod-th block contains config.txCount transactions.
// No uncles are added. // No uncles are added.
func makeChain(n int, seed byte, parent *types.Block, empty bool) ([]*types.Block, []types.Receipts) { func makeChain(n int, seed byte, parent *types.Block, config blockConfig) ([]*types.Block, []types.Receipts) {
blocks, receipts := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testDB, n, func(i int, block *core.BlockGen) { blocks, receipts := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testDB, n, func(i int, block *core.BlockGen) {
block.SetCoinbase(common.Address{seed}) block.SetCoinbase(common.Address{seed})
// Add one tx to every second block // Add transactions according to config
if !empty && i%2 == 0 { if config.txCount > 0 && i%config.txPeriod == 0 {
for range config.txCount {
signer := types.MakeSigner(params.TestChainConfig, block.Number(), block.Timestamp()) signer := types.MakeSigner(params.TestChainConfig, block.Number(), block.Timestamp())
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey) tx, err := types.SignTx(types.NewTransaction(block.TxNonce(testAddress), common.Address{seed}, big.NewInt(1000), params.TxGas, block.BaseFee(), nil), signer, testKey)
if err != nil { if err != nil {
@ -51,12 +60,14 @@ func makeChain(n int, seed byte, parent *types.Block, empty bool) ([]*types.Bloc
} }
block.AddTx(tx) block.AddTx(tx)
} }
}
}) })
return blocks, receipts return blocks, receipts
} }
type chainData struct { type chainData struct {
blocks []*types.Block blocks []*types.Block
receipts []types.Receipts
offset int offset int
} }
@ -66,11 +77,11 @@ var emptyChain *chainData
func init() { func init() {
// Create a chain of blocks to import // Create a chain of blocks to import
targetBlocks := 128 targetBlocks := 128
blocks, _ := makeChain(targetBlocks, 0, testGenesis, false) blocks, receipts := makeChain(targetBlocks, 0, testGenesis, defaultBlock)
chain = &chainData{blocks, 0} chain = &chainData{blocks, receipts, 0}
blocks, _ = makeChain(targetBlocks, 0, testGenesis, true) blocks, receipts = makeChain(targetBlocks, 0, testGenesis, emptyBlock)
emptyChain = &chainData{blocks, 0} emptyChain = &chainData{blocks, receipts, 0}
} }
func (chain *chainData) headers() []*types.Header { func (chain *chainData) headers() []*types.Header {
@ -261,13 +272,93 @@ func TestEmptyBlocks(t *testing.T) {
} }
} }
// TestPartialReceiptDelivery checks that when DeliverReceipts
// deliveres only subset of last block receipts, the remaining
// unprocessed headers are correctly re-queued into the receipt task queue.
func TestPartialReceiptDelivery(t *testing.T) {
network := newNetwork()
blocks, receipts := makeChain(10, 0, testGenesis, blockConfig{txPeriod: 1, txCount: 5})
network.chain = blocks
network.receipts = receipts
q := newQueue(10, 10)
q.Prepare(1, SnapSync)
headers := network.headers(1)[:5]
hashes := make([]common.Hash, len(headers))
for i, header := range headers {
hashes[i] = header.Hash()
}
q.Schedule(headers, hashes, 1)
peer := dummyPeer("peer-1")
req, _, _ := q.ReserveReceipts(peer, 5)
t.Logf("request: length %d", len(req.Headers))
// First delivery
cutoff := len(req.Headers) / 2
receiptSets, rcHashes, last := getPartialReceipts(cutoff, 0, receipts, true)
accepted, err := q.DeliverReceipts(peer.id, receiptSets, rcHashes, true)
if err != nil {
t.Errorf("delivery failed: %v\n", err)
}
if pending := q.PendingReceipts(); pending != 5-cutoff {
t.Errorf("wrong pending receipt length, got %d, exp %d", pending, 5-cutoff)
}
t.Logf("receipt delivered: length %d, pending %d", accepted, q.PendingReceipts())
// Second delivery
req, _, _ = q.ReserveReceipts(peer, 5)
t.Logf("request: length %d", len(req.Headers))
receiptSets, rcHashes, last = getPartialReceipts(5-cutoff, last, receipts[cutoff:], false)
accepted, err = q.DeliverReceipts(peer.id, receiptSets, rcHashes, true)
if err != nil {
t.Errorf("delivery failed: %v\n", err)
}
if pending := q.PendingReceipts(); pending != 0 {
t.Errorf("wrong pending receipt length, got %d, exp %d", pending, 0)
}
t.Logf("receipt delivered: length %d, pending %d", accepted, q.PendingReceipts())
}
func getPartialReceipts(cutoff int, prevLastIndex int, receipts []types.Receipts, incomplete bool) ([][]*types.Receipt, []common.Hash, int) {
var lastIndex int
receiptSets := make([][]*types.Receipt, cutoff+1)
for i := range receiptSets {
if cutoff == i && incomplete {
lastIndex = len(receipts[i]) / 2
receiptSets[i] = receipts[i][:lastIndex]
} else if i == 0 && prevLastIndex > 0 {
receiptSets[i] = receipts[i][prevLastIndex:]
} else {
receiptSets[i] = receipts[i]
}
}
hasher := trie.NewStackTrie(nil)
rcHashes := make([]common.Hash, len(receiptSets))
for i, rc := range receiptSets {
if cutoff == i {
break
}
rcHashes[i] = types.DeriveSha(types.Receipts(rc), hasher)
}
return receiptSets, rcHashes, lastIndex
}
// XTestDelivery does some more extensive testing of events that happen, // XTestDelivery does some more extensive testing of events that happen,
// blocks that become known and peers that make reservations and deliveries. // blocks that become known and peers that make reservations and deliveries.
// disabled since it's not really a unit-test, but can be executed to test // disabled since it's not really a unit-test, but can be executed to test
// some more advanced scenarios // some more advanced scenarios
func XTestDelivery(t *testing.T) { func XTestDelivery(t *testing.T) {
// the outside network, holding blocks // the outside network, holding blocks
blo, rec := makeChain(128, 0, testGenesis, false) blo, rec := makeChain(128, 0, testGenesis, defaultBlock)
world := newNetwork() world := newNetwork()
world.receipts = rec world.receipts = rec
world.chain = blo world.chain = blo
@ -447,7 +538,7 @@ func (n *network) progress(numBlocks int) {
n.lock.Lock() n.lock.Lock()
defer n.lock.Unlock() defer n.lock.Unlock()
//fmt.Printf("progressing...\n") //fmt.Printf("progressing...\n")
newBlocks, newR := makeChain(numBlocks, 0, n.chain[len(n.chain)-1], false) newBlocks, newR := makeChain(numBlocks, 0, n.chain[len(n.chain)-1], defaultBlock)
n.chain = append(n.chain, newBlocks...) n.chain = append(n.chain, newBlocks...)
n.receipts = append(n.receipts, newR...) n.receipts = append(n.receipts, newR...)
n.cond.Broadcast() n.cond.Broadcast()