mirror of https://github.com/ethereum/go-ethereum
parent
d11db22a96
commit
f8e98ae974
@ -0,0 +1,280 @@ |
||||
package filtermaps |
||||
|
||||
import ( |
||||
"math/big" |
||||
"math/rand" |
||||
"sync" |
||||
"testing" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/consensus/ethash" |
||||
"github.com/ethereum/go-ethereum/core" |
||||
"github.com/ethereum/go-ethereum/core/rawdb" |
||||
"github.com/ethereum/go-ethereum/core/types" |
||||
"github.com/ethereum/go-ethereum/ethdb" |
||||
"github.com/ethereum/go-ethereum/event" |
||||
"github.com/ethereum/go-ethereum/params" |
||||
) |
||||
|
||||
const ( |
||||
testHookInit = iota |
||||
testHookUpdateHeadEpoch |
||||
testHookUpdateHead |
||||
testHookExtendTailEpoch |
||||
testHookExtendTail |
||||
testHookPruneTail |
||||
testHookPruneTailMaps |
||||
testHookRevert |
||||
testHookWait |
||||
testHookStop |
||||
) |
||||
|
||||
var testParams = Params{ |
||||
logMapHeight: 2, |
||||
logMapsPerEpoch: 4, |
||||
logValuesPerMap: 4, |
||||
} |
||||
|
||||
func TestIndexerSetHistory(t *testing.T) { |
||||
ts := newTestSetup(t) |
||||
ts.setHistory(0, false) |
||||
ts.chain.addBlocks(1000, 5, 2, 4, false) // 50 log values per block
|
||||
ts.runUntilWait() |
||||
ts.setHistory(100, false) |
||||
ts.runUntil(func() bool { |
||||
l := ts.lastRange.headLvPointer - ts.lastRange.tailLvPointer |
||||
return l > 44000 && l < 45000 |
||||
}) |
||||
ts.setHistory(200, false) |
||||
ts.runUntilWait() |
||||
ts.setHistory(0, false) |
||||
ts.runUntilWait() |
||||
if ts.lastRange.headLvPointer-ts.lastRange.tailLvPointer != 50000 { |
||||
t.Fatalf("Invalid number of log values in the final state (expected %d, got %d)", 50000, ts.lastRange.headLvPointer-ts.lastRange.tailLvPointer) |
||||
} |
||||
} |
||||
|
||||
func TestIndexerRandomSetHistory(t *testing.T) { |
||||
ts := newTestSetup(t) |
||||
ts.chain.addBlocks(100, 5, 2, 4, false) // 50 log values per block
|
||||
for i := 0; i < 3000; i++ { |
||||
ts.setHistory(uint64(rand.Intn(1001)), false) |
||||
ts.nextEvent() |
||||
for rand.Intn(20) != 0 && ts.lastEvent != testHookWait { |
||||
ts.nextEvent() |
||||
} |
||||
} |
||||
ts.setHistory(0, false) |
||||
ts.runUntilWait() |
||||
if ts.lastRange.headLvPointer-ts.lastRange.tailLvPointer != 5000 { |
||||
t.Fatalf("Invalid number of log values in the final state (expected %d, got %d)", 5000, ts.lastRange.headLvPointer-ts.lastRange.tailLvPointer) |
||||
} |
||||
} |
||||
|
||||
type testSetup struct { |
||||
t *testing.T |
||||
fm *FilterMaps |
||||
db ethdb.Database |
||||
chain *testChain |
||||
eventCh chan int |
||||
resumeCh chan struct{} |
||||
lastEvent int |
||||
lastRange filterMapsRange |
||||
} |
||||
|
||||
func newTestSetup(t *testing.T) *testSetup { |
||||
return &testSetup{ |
||||
t: t, |
||||
chain: newTestChain(), |
||||
db: rawdb.NewMemoryDatabase(), |
||||
eventCh: make(chan int), |
||||
resumeCh: make(chan struct{}), |
||||
} |
||||
} |
||||
|
||||
func (ts *testSetup) runUntil(stop func() bool) { |
||||
for !stop() { |
||||
ts.nextEvent() |
||||
for ts.lastEvent == testHookWait { |
||||
ts.t.Fatalf("Indexer in waiting state before runUntil condition is met") |
||||
} |
||||
} |
||||
} |
||||
|
||||
func (ts *testSetup) runUntilWait() { |
||||
ts.nextEvent() |
||||
for ts.lastEvent != testHookWait { |
||||
ts.nextEvent() |
||||
} |
||||
} |
||||
|
||||
func (ts *testSetup) setHistory(history uint64, noHistory bool) { |
||||
if ts.fm != nil { |
||||
ts.stopFm() |
||||
} |
||||
ts.fm = NewFilterMaps(ts.db, ts.chain, testParams, history, noHistory) |
||||
ts.fm.testHook = ts.testHook |
||||
ts.fm.Start() |
||||
ts.lastEvent = <-ts.eventCh |
||||
} |
||||
|
||||
func (ts *testSetup) testHook(event int) { |
||||
ts.eventCh <- event |
||||
<-ts.resumeCh |
||||
} |
||||
|
||||
func (ts *testSetup) nextEvent() { |
||||
ts.resumeCh <- struct{}{} |
||||
ts.lastEvent = <-ts.eventCh |
||||
ts.lastRange = ts.fm.getRange() |
||||
} |
||||
|
||||
func (ts *testSetup) stopFm() { |
||||
close(ts.fm.closeCh) |
||||
for { |
||||
ts.nextEvent() |
||||
if ts.lastEvent == testHookStop { |
||||
break |
||||
} |
||||
} |
||||
ts.resumeCh <- struct{}{} |
||||
ts.fm.closeWg.Wait() |
||||
} |
||||
|
||||
func (ts *testSetup) close() { |
||||
ts.stopFm() |
||||
ts.db.Close() |
||||
ts.chain.db.Close() |
||||
} |
||||
|
||||
type testChain struct { |
||||
db ethdb.Database |
||||
lock sync.RWMutex |
||||
canonical []common.Hash |
||||
chainHeadFeed event.Feed |
||||
blocks map[common.Hash]*types.Block |
||||
receipts map[common.Hash]types.Receipts |
||||
} |
||||
|
||||
func newTestChain() *testChain { |
||||
return &testChain{ |
||||
blocks: make(map[common.Hash]*types.Block), |
||||
receipts: make(map[common.Hash]types.Receipts), |
||||
} |
||||
} |
||||
|
||||
func (tc *testChain) CurrentBlock() *types.Header { |
||||
tc.lock.RLock() |
||||
defer tc.lock.RUnlock() |
||||
|
||||
if len(tc.canonical) == 0 { |
||||
return nil |
||||
} |
||||
return tc.blocks[tc.canonical[len(tc.canonical)-1]].Header() |
||||
} |
||||
|
||||
func (tc *testChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { |
||||
return tc.chainHeadFeed.Subscribe(ch) |
||||
} |
||||
|
||||
func (tc *testChain) GetHeader(hash common.Hash, number uint64) *types.Header { |
||||
tc.lock.RLock() |
||||
defer tc.lock.RUnlock() |
||||
|
||||
return tc.blocks[hash].Header() |
||||
} |
||||
|
||||
func (tc *testChain) GetCanonicalHash(number uint64) common.Hash { |
||||
tc.lock.RLock() |
||||
defer tc.lock.RUnlock() |
||||
|
||||
if uint64(len(tc.canonical)) <= number { |
||||
return common.Hash{} |
||||
} |
||||
return tc.canonical[number] |
||||
} |
||||
|
||||
func (tc *testChain) GetReceiptsByHash(hash common.Hash) types.Receipts { |
||||
tc.lock.RLock() |
||||
defer tc.lock.RUnlock() |
||||
|
||||
return tc.receipts[hash] |
||||
} |
||||
|
||||
func (tc *testChain) addBlocks(count, maxTxPerBlock, maxLogsPerReceipt, maxTopicsPerLog int, random bool) { |
||||
tc.lock.Lock() |
||||
defer tc.lock.Unlock() |
||||
|
||||
blockGen := func(i int, gen *core.BlockGen) { |
||||
var txCount int |
||||
if random { |
||||
txCount = rand.Intn(maxTxPerBlock + 1) |
||||
} else { |
||||
txCount = maxTxPerBlock |
||||
} |
||||
for k := txCount; k > 0; k-- { |
||||
receipt := types.NewReceipt(nil, false, 0) |
||||
var logCount int |
||||
if random { |
||||
logCount = rand.Intn(maxLogsPerReceipt + 1) |
||||
} else { |
||||
logCount = maxLogsPerReceipt |
||||
} |
||||
receipt.Logs = make([]*types.Log, logCount) |
||||
for i := range receipt.Logs { |
||||
log := &types.Log{} |
||||
receipt.Logs[i] = log |
||||
rand.Read(log.Address[:]) |
||||
var topicCount int |
||||
if random { |
||||
topicCount = rand.Intn(maxTopicsPerLog + 1) |
||||
} else { |
||||
topicCount = maxTopicsPerLog |
||||
} |
||||
log.Topics = make([]common.Hash, topicCount) |
||||
for j := range log.Topics { |
||||
rand.Read(log.Topics[j][:]) |
||||
} |
||||
} |
||||
gen.AddUncheckedReceipt(receipt) |
||||
gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, gen.BaseFee(), nil)) |
||||
} |
||||
} |
||||
|
||||
var ( |
||||
blocks []*types.Block |
||||
receipts []types.Receipts |
||||
engine = ethash.NewFaker() |
||||
) |
||||
|
||||
if len(tc.canonical) == 0 { |
||||
gspec := &core.Genesis{ |
||||
Alloc: types.GenesisAlloc{}, |
||||
BaseFee: big.NewInt(params.InitialBaseFee), |
||||
Config: params.TestChainConfig, |
||||
} |
||||
tc.db, blocks, receipts = core.GenerateChainWithGenesis(gspec, engine, count, blockGen) |
||||
gblock := gspec.ToBlock() |
||||
ghash := gblock.Hash() |
||||
tc.canonical = []common.Hash{ghash} |
||||
tc.blocks[ghash] = gblock |
||||
tc.receipts[ghash] = types.Receipts{} |
||||
} else { |
||||
blocks, receipts = core.GenerateChain(params.TestChainConfig, tc.blocks[tc.canonical[len(tc.canonical)-1]], engine, tc.db, count, blockGen) |
||||
} |
||||
|
||||
for i, block := range blocks { |
||||
num, hash := int(block.NumberU64()), block.Hash() |
||||
if len(tc.canonical) != num { |
||||
panic(nil) |
||||
} |
||||
tc.canonical = append(tc.canonical, hash) |
||||
tc.blocks[hash] = block |
||||
if receipts[i] != nil { |
||||
tc.receipts[hash] = receipts[i] |
||||
} else { |
||||
tc.receipts[hash] = types.Receipts{} |
||||
} |
||||
} |
||||
tc.chainHeadFeed.Send(core.ChainHeadEvent{Block: tc.blocks[tc.canonical[len(tc.canonical)-1]]}) |
||||
} |
Loading…
Reference in new issue