core: add basic chain history support in GenerateChain (#28428)

This change improves GenerateChain to support internal chain history access (ChainReader)
for the consensus engine and EVM.

GenerateChain takes a `parent` block and the number of blocks to create. With my changes,
the consensus engine and EVM can now access blocks from `parent` up to the block currently
being generated. This is required to make the BLOCKHASH instruction work, and also needed
to create real clique chains.  Clique uses chain history to figure out if the current signer is in-turn,
for example.

I've also added some more accessors to BlockGen. These are helpful when creating transactions:

- g.Signer returns a signer instance for the current block
- g.Difficulty returns the current block difficulty
- g.Gas returns the remaining gas amount

Another fix in this commit concerns the receipts returned by GenerateChain. The receipts now
have properly derived fields (BlockHash, etc.) and should generally match what would be
returned by the RPC API.
This commit is contained in:
Felix Lange 2023-10-31 12:39:25 +01:00 committed by GitHub
parent 447945e438
commit bc42e88415
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 295 additions and 150 deletions

View File

@ -84,7 +84,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
toaddr := common.Address{}
data := make([]byte, nbytes)
gas, _ := IntrinsicGas(data, nil, false, false, false, false)
signer := types.MakeSigner(gen.config, big.NewInt(int64(i)), gen.header.Time)
signer := gen.Signer()
gasPrice := big.NewInt(0)
if gen.header.BaseFee != nil {
gasPrice = gen.header.BaseFee
@ -128,7 +128,7 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
if gen.header.BaseFee != nil {
gasPrice = gen.header.BaseFee
}
signer := types.MakeSigner(gen.config, big.NewInt(int64(i)), gen.header.Time)
signer := gen.Signer()
for {
gas -= params.TxGas
if gas < params.TxGas {

View File

@ -38,8 +38,8 @@ import (
// See GenerateChain for a detailed explanation.
type BlockGen struct {
i int
cm *chainMaker
parent *types.Block
chain []*types.Block
header *types.Header
statedb *state.StateDB
@ -49,7 +49,6 @@ type BlockGen struct {
uncles []*types.Header
withdrawals []*types.Withdrawal
config *params.ChainConfig
engine consensus.Engine
}
@ -88,13 +87,18 @@ func (b *BlockGen) SetPoS() {
b.header.Difficulty = new(big.Int)
}
// Difficulty returns the currently calculated difficulty of the block.
func (b *BlockGen) Difficulty() *big.Int {
return new(big.Int).Set(b.header.Difficulty)
}
// SetParentBeaconRoot sets the parent beacon root field of the generated
// block.
func (b *BlockGen) SetParentBeaconRoot(root common.Hash) {
b.header.ParentBeaconRoot = &root
var (
blockContext = NewEVMBlockContext(b.header, nil, &b.header.Coinbase)
vmenv = vm.NewEVM(blockContext, vm.TxContext{}, b.statedb, b.config, vm.Config{})
blockContext = NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase)
vmenv = vm.NewEVM(blockContext, vm.TxContext{}, b.statedb, b.cm.config, vm.Config{})
)
ProcessBeaconBlockRoot(root, vmenv, b.statedb)
}
@ -111,7 +115,7 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
b.SetCoinbase(common.Address{})
}
b.statedb.SetTxContext(tx.Hash(), len(b.txs))
receipt, err := ApplyTransaction(b.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vmConfig)
receipt, err := ApplyTransaction(b.cm.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vmConfig)
if err != nil {
panic(err)
}
@ -125,11 +129,11 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
// AddTx adds a transaction to the generated block. If no coinbase has
// been set, the block's coinbase is set to the zero address.
//
// AddTx panics if the transaction cannot be executed. In addition to
// the protocol-imposed limitations (gas limit, etc.), there are some
// further limitations on the content of transactions that can be
// added. Notably, contract code relying on the BLOCKHASH instruction
// will panic during execution.
// AddTx panics if the transaction cannot be executed. In addition to the protocol-imposed
// limitations (gas limit, etc.), there are some further limitations on the content of
// transactions that can be added. Notably, contract code relying on the BLOCKHASH
// instruction will panic during execution if it attempts to access a block number outside
// of the range created by GenerateChain.
func (b *BlockGen) AddTx(tx *types.Transaction) {
b.addTx(nil, vm.Config{}, tx)
}
@ -137,11 +141,10 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
// AddTxWithChain adds a transaction to the generated block. If no coinbase has
// been set, the block's coinbase is set to the zero address.
//
// AddTxWithChain panics if the transaction cannot be executed. In addition to
// the protocol-imposed limitations (gas limit, etc.), there are some
// further limitations on the content of transactions that can be
// added. If contract code relies on the BLOCKHASH instruction,
// the block in chain will be returned.
// AddTxWithChain panics if the transaction cannot be executed. In addition to the
// protocol-imposed limitations (gas limit, etc.), there are some further limitations on
// the content of transactions that can be added. If contract code relies on the BLOCKHASH
// instruction, the block in chain will be returned.
func (b *BlockGen) AddTxWithChain(bc *BlockChain, tx *types.Transaction) {
b.addTx(bc, vm.Config{}, tx)
}
@ -158,8 +161,7 @@ func (b *BlockGen) GetBalance(addr common.Address) *big.Int {
return b.statedb.GetBalance(addr)
}
// AddUncheckedTx forcefully adds a transaction to the block without any
// validation.
// AddUncheckedTx forcefully adds a transaction to the block without any validation.
//
// AddUncheckedTx will cause consensus failures when used during real
// chain processing. This is best used in conjunction with raw block insertion.
@ -182,6 +184,16 @@ func (b *BlockGen) BaseFee() *big.Int {
return new(big.Int).Set(b.header.BaseFee)
}
// Gas returns the amount of gas left in the current block.
func (b *BlockGen) Gas() uint64 {
return b.header.GasLimit - b.header.GasUsed
}
// Signer returns a valid signer instance for the current block.
func (b *BlockGen) Signer() types.Signer {
return types.MakeSigner(b.cm.config, b.header.Number, b.header.Time)
}
// AddUncheckedReceipt forcefully adds a receipts to the block without a
// backing transaction.
//
@ -207,20 +219,19 @@ func (b *BlockGen) AddUncle(h *types.Header) {
var parent *types.Header
for i := b.i - 1; i >= 0; i-- {
if b.chain[i].Hash() == h.ParentHash {
parent = b.chain[i].Header()
if b.cm.chain[i].Hash() == h.ParentHash {
parent = b.cm.chain[i].Header()
break
}
}
chainreader := &fakeChainReader{config: b.config}
h.Difficulty = b.engine.CalcDifficulty(chainreader, b.header.Time, parent)
h.Difficulty = b.engine.CalcDifficulty(b.cm, b.header.Time, parent)
// The gas limit and price should be derived from the parent
h.GasLimit = parent.GasLimit
if b.config.IsLondon(h.Number) {
h.BaseFee = eip1559.CalcBaseFee(b.config, parent)
if !b.config.IsLondon(parent.Number) {
parentGasLimit := parent.GasLimit * b.config.ElasticityMultiplier()
if b.cm.config.IsLondon(h.Number) {
h.BaseFee = eip1559.CalcBaseFee(b.cm.config, parent)
if !b.cm.config.IsLondon(parent.Number) {
parentGasLimit := parent.GasLimit * b.cm.config.ElasticityMultiplier()
h.GasLimit = CalcGasLimit(parentGasLimit, parentGasLimit)
}
}
@ -242,12 +253,12 @@ func (b *BlockGen) nextWithdrawalIndex() uint64 {
return b.withdrawals[len(b.withdrawals)-1].Index + 1
}
for i := b.i - 1; i >= 0; i-- {
if wd := b.chain[i].Withdrawals(); len(wd) != 0 {
if wd := b.cm.chain[i].Withdrawals(); len(wd) != 0 {
return wd[len(wd)-1].Index + 1
}
if i == 0 {
// Correctly set the index if no parent had withdrawals.
if wd := b.parent.Withdrawals(); len(wd) != 0 {
if wd := b.cm.bottom.Withdrawals(); len(wd) != 0 {
return wd[len(wd)-1].Index + 1
}
}
@ -263,9 +274,9 @@ func (b *BlockGen) PrevBlock(index int) *types.Block {
panic(fmt.Errorf("block index %d out of range (%d,%d)", index, -1, b.i))
}
if index == -1 {
return b.parent
return b.cm.bottom
}
return b.chain[index]
return b.cm.chain[index]
}
// OffsetTime modifies the time instance of a block, implicitly changing its
@ -273,11 +284,10 @@ func (b *BlockGen) PrevBlock(index int) *types.Block {
// tied to chain length directly.
func (b *BlockGen) OffsetTime(seconds int64) {
b.header.Time += uint64(seconds)
if b.header.Time <= b.parent.Header().Time {
if b.header.Time <= b.cm.bottom.Header().Time {
panic("block time out of range")
}
chainreader := &fakeChainReader{config: b.config}
b.header.Difficulty = b.engine.CalcDifficulty(chainreader, b.header.Time, b.parent.Header())
b.header.Difficulty = b.engine.CalcDifficulty(b.cm, b.header.Time, b.parent.Header())
}
// GenerateChain creates a chain of n blocks. The first block's
@ -296,11 +306,14 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
if config == nil {
config = params.TestChainConfig
}
blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n)
chainreader := &fakeChainReader{config: config}
if engine == nil {
panic("nil consensus engine")
}
cm := newChainMaker(parent, config, engine)
genblock := func(i int, parent *types.Block, triedb *trie.Database, statedb *state.StateDB) (*types.Block, types.Receipts) {
b := &BlockGen{i: i, chain: blocks, parent: parent, statedb: statedb, config: config, engine: engine}
b.header = makeHeader(chainreader, parent, statedb, b.engine)
b := &BlockGen{i: i, cm: cm, parent: parent, statedb: statedb, engine: engine}
b.header = cm.makeHeader(parent, statedb, b.engine)
// Set the difficulty for clique block. The chain maker doesn't have access
// to a chain, so the difficulty will be left unset (nil). Set it here to the
@ -330,8 +343,8 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
if gen != nil {
gen(i, b)
}
if b.engine != nil {
block, err := b.engine.FinalizeAndAssemble(chainreader, b.header, statedb, b.txs, b.uncles, b.receipts, b.withdrawals)
block, err := b.engine.FinalizeAndAssemble(cm, b.header, statedb, b.txs, b.uncles, b.receipts, b.withdrawals)
if err != nil {
panic(err)
}
@ -346,8 +359,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
}
return block, b.receipts
}
return nil, nil
}
// Forcibly use hash-based state scheme for retaining all nodes in disk.
triedb := trie.NewDatabase(db, trie.HashDefaults)
defer triedb.Close()
@ -357,12 +369,36 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
if err != nil {
panic(err)
}
block, receipt := genblock(i, parent, triedb, statedb)
blocks[i] = block
receipts[i] = receipt
block, receipts := genblock(i, parent, triedb, statedb)
// Post-process the receipts.
// Here we assign the final block hash and other info into the receipt.
// In order for DeriveFields to work, the transaction and receipt lists need to be
// of equal length. If AddUncheckedTx or AddUncheckedReceipt are used, there will be
// extra ones, so we just trim the lists here.
receiptsCount := len(receipts)
txs := block.Transactions()
if len(receipts) > len(txs) {
receipts = receipts[:len(txs)]
} else if len(receipts) < len(txs) {
txs = txs[:len(receipts)]
}
var blobGasPrice *big.Int
if block.ExcessBlobGas() != nil {
blobGasPrice = eip4844.CalcBlobFee(*block.ExcessBlobGas())
}
if err := receipts.DeriveFields(config, block.Hash(), block.NumberU64(), block.Time(), block.BaseFee(), blobGasPrice, txs); err != nil {
panic(err)
}
// Re-expand to ensure all receipts are returned.
receipts = receipts[:receiptsCount]
// Advance the chain.
cm.add(block, receipts)
parent = block
}
return blocks, receipts
return cm.chain, cm.receipts
}
// GenerateChainWithGenesis is a wrapper of GenerateChain which will initialize
@ -380,35 +416,26 @@ func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int,
return db, blocks, receipts
}
func makeHeader(chain consensus.ChainReader, parent *types.Block, state *state.StateDB, engine consensus.Engine) *types.Header {
var time uint64
if parent.Time() == 0 {
time = 10
} else {
time = parent.Time() + 10 // block time is fixed at 10 seconds
}
func (cm *chainMaker) makeHeader(parent *types.Block, state *state.StateDB, engine consensus.Engine) *types.Header {
time := parent.Time() + 10 // block time is fixed at 10 seconds
header := &types.Header{
Root: state.IntermediateRoot(chain.Config().IsEIP158(parent.Number())),
Root: state.IntermediateRoot(cm.config.IsEIP158(parent.Number())),
ParentHash: parent.Hash(),
Coinbase: parent.Coinbase(),
Difficulty: engine.CalcDifficulty(chain, time, &types.Header{
Number: parent.Number(),
Time: time - 10,
Difficulty: parent.Difficulty(),
UncleHash: parent.UncleHash(),
}),
Difficulty: engine.CalcDifficulty(cm, time, parent.Header()),
GasLimit: parent.GasLimit(),
Number: new(big.Int).Add(parent.Number(), common.Big1),
Time: time,
}
if chain.Config().IsLondon(header.Number) {
header.BaseFee = eip1559.CalcBaseFee(chain.Config(), parent.Header())
if !chain.Config().IsLondon(parent.Number()) {
parentGasLimit := parent.GasLimit() * chain.Config().ElasticityMultiplier()
if cm.config.IsLondon(header.Number) {
header.BaseFee = eip1559.CalcBaseFee(cm.config, parent.Header())
if !cm.config.IsLondon(parent.Number()) {
parentGasLimit := parent.GasLimit() * cm.config.ElasticityMultiplier()
header.GasLimit = CalcGasLimit(parentGasLimit, parentGasLimit)
}
}
if chain.Config().IsCancun(header.Number, header.Time) {
if cm.config.IsCancun(header.Number, header.Time) {
var (
parentExcessBlobGas uint64
parentBlobGasUsed uint64
@ -461,18 +488,86 @@ func makeBlockChainWithGenesis(genesis *Genesis, n int, engine consensus.Engine,
return db, blocks
}
type fakeChainReader struct {
// chainMaker contains the state of chain generation.
type chainMaker struct {
bottom *types.Block
engine consensus.Engine
config *params.ChainConfig
chain []*types.Block
chainByHash map[common.Hash]*types.Block
receipts []types.Receipts
}
// Config returns the chain configuration.
func (cr *fakeChainReader) Config() *params.ChainConfig {
return cr.config
func newChainMaker(bottom *types.Block, config *params.ChainConfig, engine consensus.Engine) *chainMaker {
return &chainMaker{
bottom: bottom,
config: config,
engine: engine,
chainByHash: make(map[common.Hash]*types.Block),
}
}
func (cr *fakeChainReader) CurrentHeader() *types.Header { return nil }
func (cr *fakeChainReader) GetHeaderByNumber(number uint64) *types.Header { return nil }
func (cr *fakeChainReader) GetHeaderByHash(hash common.Hash) *types.Header { return nil }
func (cr *fakeChainReader) GetHeader(hash common.Hash, number uint64) *types.Header { return nil }
func (cr *fakeChainReader) GetBlock(hash common.Hash, number uint64) *types.Block { return nil }
func (cr *fakeChainReader) GetTd(hash common.Hash, number uint64) *big.Int { return nil }
func (cm *chainMaker) add(b *types.Block, r []*types.Receipt) {
cm.chain = append(cm.chain, b)
cm.chainByHash[b.Hash()] = b
cm.receipts = append(cm.receipts, r)
}
func (cm *chainMaker) blockByNumber(number uint64) *types.Block {
if number == cm.bottom.NumberU64() {
return cm.bottom
}
cur := cm.CurrentHeader().Number.Uint64()
lowest := cm.bottom.NumberU64() + 1
if number < lowest || number > cur {
return nil
}
return cm.chain[number-lowest]
}
// ChainReader/ChainContext implementation
// Config returns the chain configuration (for consensus.ChainReader).
func (cm *chainMaker) Config() *params.ChainConfig {
return cm.config
}
// Engine returns the consensus engine (for ChainContext).
func (cm *chainMaker) Engine() consensus.Engine {
return cm.engine
}
func (cm *chainMaker) CurrentHeader() *types.Header {
if len(cm.chain) == 0 {
return cm.bottom.Header()
}
return cm.chain[len(cm.chain)-1].Header()
}
func (cm *chainMaker) GetHeaderByNumber(number uint64) *types.Header {
b := cm.blockByNumber(number)
if b == nil {
return nil
}
return b.Header()
}
func (cm *chainMaker) GetHeaderByHash(hash common.Hash) *types.Header {
b := cm.chainByHash[hash]
if b == nil {
return nil
}
return b.Header()
}
func (cm *chainMaker) GetHeader(hash common.Hash, number uint64) *types.Header {
return cm.GetHeaderByNumber(number)
}
func (cm *chainMaker) GetBlock(hash common.Hash, number uint64) *types.Block {
return cm.blockByNumber(number)
}
func (cm *chainMaker) GetTd(hash common.Hash, number uint64) *big.Int {
return nil // not supported
}

View File

@ -19,8 +19,10 @@ package core
import (
"fmt"
"math/big"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash"
@ -53,7 +55,6 @@ func TestGeneratePOSChain(t *testing.T) {
GasLimit: 5_000_000,
}
gendb = rawdb.NewMemoryDatabase()
signer = types.LatestSigner(gspec.Config)
db = rawdb.NewMemoryDatabase()
)
@ -82,10 +83,20 @@ func TestGeneratePOSChain(t *testing.T) {
}
genesis := gspec.MustCommit(gendb, trie.NewDatabase(gendb, trie.HashDefaults))
chain, _ := GenerateChain(gspec.Config, genesis, beacon.NewFaker(), gendb, 4, func(i int, gen *BlockGen) {
genchain, genreceipts := GenerateChain(gspec.Config, genesis, beacon.NewFaker(), gendb, 4, func(i int, gen *BlockGen) {
gen.SetParentBeaconRoot(common.Hash{byte(i + 1)})
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(address), address, big.NewInt(1000), params.TxGas, new(big.Int).Add(gen.BaseFee(), common.Big1), nil), signer, key)
// Add value transfer tx.
tx := types.MustSignNewTx(key, gen.Signer(), &types.LegacyTx{
Nonce: gen.TxNonce(address),
To: &address,
Value: big.NewInt(1000),
Gas: params.TxGas,
GasPrice: new(big.Int).Add(gen.BaseFee(), common.Big1),
})
gen.AddTx(tx)
// Add withdrawals.
if i == 1 {
gen.AddWithdrawal(&types.Withdrawal{
Validator: 42,
@ -116,20 +127,39 @@ func TestGeneratePOSChain(t *testing.T) {
blockchain, _ := NewBlockChain(db, nil, gspec, nil, beacon.NewFaker(), vm.Config{}, nil, nil)
defer blockchain.Stop()
if i, err := blockchain.InsertChain(chain); err != nil {
fmt.Printf("insert error (block %d): %v\n", chain[i].NumberU64(), err)
return
if i, err := blockchain.InsertChain(genchain); err != nil {
t.Fatalf("insert error (block %d): %v\n", genchain[i].NumberU64(), err)
}
// enforce that withdrawal indexes are monotonically increasing from 0
var (
withdrawalIndex uint64
head = blockchain.CurrentBlock().Number.Uint64()
)
for i := 0; i < int(head); i++ {
block := blockchain.GetBlockByNumber(uint64(i))
for i := range genchain {
blocknum := genchain[i].NumberU64()
block := blockchain.GetBlockByNumber(blocknum)
if block == nil {
t.Fatalf("block %d not found", i)
t.Fatalf("block %d not found", blocknum)
}
// Verify receipts.
genBlockReceipts := genreceipts[i]
for _, r := range genBlockReceipts {
if r.BlockNumber.Cmp(block.Number()) != 0 {
t.Errorf("receipt has wrong block number %d, want %d", r.BlockNumber, block.Number())
}
if r.BlockHash != block.Hash() {
t.Errorf("receipt has wrong block hash %v, want %v", r.BlockHash, block.Hash())
}
// patch up empty logs list to make DeepEqual below work
if r.Logs == nil {
r.Logs = []*types.Log{}
}
}
blockchainReceipts := blockchain.GetReceiptsByHash(block.Hash())
if !reflect.DeepEqual(genBlockReceipts, blockchainReceipts) {
t.Fatalf("receipts mismatch\ngenerated: %s\nblockchain: %s", spew.Sdump(genBlockReceipts), spew.Sdump(blockchainReceipts))
}
// Verify withdrawals.
@ -144,7 +174,7 @@ func TestGeneratePOSChain(t *testing.T) {
}
// Verify parent beacon root.
want := common.Hash{byte(i)}
want := common.Hash{byte(blocknum)}
if got := block.BeaconRoot(); *got != want {
t.Fatalf("block %d, wrong parent beacon root: got %s, want %s", i, got, want)
}

View File

@ -165,7 +165,8 @@ func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *commo
}
// Create a new context to be used in the EVM environment
blockContext := NewEVMBlockContext(header, bc, author)
vmenv := vm.NewEVM(blockContext, vm.TxContext{BlobHashes: tx.BlobHashes()}, statedb, config, cfg)
txContext := NewEVMTxContext(msg)
vmenv := vm.NewEVM(blockContext, txContext, statedb, config, cfg)
return applyTransaction(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
}

View File

@ -359,7 +359,8 @@ func TestStateProcessorErrors(t *testing.T) {
func GenerateBadBlock(parent *types.Block, engine consensus.Engine, txs types.Transactions, config *params.ChainConfig) *types.Block {
difficulty := big.NewInt(0)
if !config.TerminalTotalDifficultyPassed {
difficulty = engine.CalcDifficulty(&fakeChainReader{config}, parent.Time()+10, &types.Header{
fakeChainReader := newChainMaker(nil, config, engine)
difficulty = engine.CalcDifficulty(fakeChainReader, parent.Time()+10, &types.Header{
Number: parent.Number(),
Time: parent.Time(),
Difficulty: parent.Difficulty(),

View File

@ -287,53 +287,71 @@ func TestFilters(t *testing.T) {
{
f: sys.NewBlockFilter(chain[2].Hash(), []common.Address{contract}, nil),
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false}]`,
}, {
},
{
f: sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), []common.Address{contract}, [][]common.Hash{{hash1, hash2, hash3, hash4}}),
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xa8028c655b6423204c8edfbc339f57b042d6bec2b6a61145d76b7c08b4cccd42","transactionIndex":"0x0","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false}]`,
}, {
},
{
f: sys.NewRangeFilter(900, 999, []common.Address{contract}, [][]common.Hash{{hash3}}),
}, {
},
{
f: sys.NewRangeFilter(990, int64(rpc.LatestBlockNumber), []common.Address{contract2}, [][]common.Hash{{hash3}}),
want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","logIndex":"0x0","removed":false}]`,
}, {
},
{
f: sys.NewRangeFilter(1, 10, []common.Address{contract}, [][]common.Hash{{hash2}, {hash1}}),
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false}]`,
}, {
},
{
f: sys.NewRangeFilter(1, 10, nil, [][]common.Hash{{hash1, hash2}}),
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xa8028c655b6423204c8edfbc339f57b042d6bec2b6a61145d76b7c08b4cccd42","transactionIndex":"0x0","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","logIndex":"0x0","removed":false},{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x2","transactionHash":"0xdba3e2ea9a7d690b722d70ee605fd67ba4c00d1d3aecd5cf187a7b92ad8eb3df","transactionIndex":"0x1","blockHash":"0x24417bb49ce44cfad65da68f33b510bf2a129c0d89ccf06acb6958b8585ccf34","logIndex":"0x1","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696332","0x0000000000000000000000000000000000000000000000000000746f70696331"],"data":"0x","blockNumber":"0x3","transactionHash":"0xdefe471992a07a02acdfbe33edaae22fbb86d7d3cec3f1b8e4e77702fb3acc1d","transactionIndex":"0x0","blockHash":"0x7a7556792ca7d37882882e2b001fe14833eaf81c2c7f865c9c771ec37a024f6b","logIndex":"0x0","removed":false}]`,
}, {
},
{
f: sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}}),
}, {
},
{
f: sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), []common.Address{common.BytesToAddress([]byte("failmenow"))}, nil),
}, {
},
{
f: sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}, {hash1}}),
}, {
},
{
f: sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false}]`,
}, {
},
{
f: sys.NewRangeFilter(int64(rpc.FinalizedBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false}]`,
}, {
},
{
f: sys.NewRangeFilter(int64(rpc.FinalizedBlockNumber), int64(rpc.FinalizedBlockNumber), nil, nil),
want: `[{"address":"0xff00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696333"],"data":"0x","blockNumber":"0x3e7","transactionHash":"0x53e3675800c6908424b61b35a44e51ca4c73ca603e58a65b32c67968b4f42200","transactionIndex":"0x0","blockHash":"0x2e4620a2b426b0612ec6cad9603f466723edaed87f98c9137405dd4f7a2409ff","logIndex":"0x0","removed":false}]`,
}, {
},
{
f: sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.FinalizedBlockNumber), nil, nil),
}, {
},
{
f: sys.NewRangeFilter(int64(rpc.SafeBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
err: "safe header not found",
}, {
},
{
f: sys.NewRangeFilter(int64(rpc.SafeBlockNumber), int64(rpc.SafeBlockNumber), nil, nil),
err: "safe header not found",
}, {
},
{
f: sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.SafeBlockNumber), nil, nil),
err: "safe header not found",
}, {
},
{
f: sys.NewRangeFilter(int64(rpc.PendingBlockNumber), int64(rpc.PendingBlockNumber), nil, nil),
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696335"],"data":"0x","blockNumber":"0x3e9","transactionHash":"0x4110587c1b8d86edc85dce929a34127f1cb8809515a9f177c91c866de3eb0638","transactionIndex":"0x0","blockHash":"0xc7245899e5817f16fa99cf5ad2d9c1e4b98443a565a673ec9c764640443ef037","logIndex":"0x0","removed":false}]`,
}, {
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696335"],"data":"0x","blockNumber":"0x3e9","transactionHash":"0x4110587c1b8d86edc85dce929a34127f1cb8809515a9f177c91c866de3eb0638","transactionIndex":"0x0","blockHash":"0xd5e8d4e4eb51a2a2a6ec20ef68a4c2801240743c8deb77a6a1d118ac3eefb725","logIndex":"0x0","removed":false}]`,
},
{
f: sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.PendingBlockNumber), nil, nil),
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696335"],"data":"0x","blockNumber":"0x3e9","transactionHash":"0x4110587c1b8d86edc85dce929a34127f1cb8809515a9f177c91c866de3eb0638","transactionIndex":"0x0","blockHash":"0xc7245899e5817f16fa99cf5ad2d9c1e4b98443a565a673ec9c764640443ef037","logIndex":"0x0","removed":false}]`,
}, {
want: `[{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696334"],"data":"0x","blockNumber":"0x3e8","transactionHash":"0x9a87842100a638dfa5da8842b4beda691d2fd77b0c84b57f24ecfa9fb208f747","transactionIndex":"0x0","blockHash":"0xb360bad5265261c075ece02d3bf0e39498a6a76310482cdfd90588748e6c5ee0","logIndex":"0x0","removed":false},{"address":"0xfe00000000000000000000000000000000000000","topics":["0x0000000000000000000000000000000000000000000000000000746f70696335"],"data":"0x","blockNumber":"0x3e9","transactionHash":"0x4110587c1b8d86edc85dce929a34127f1cb8809515a9f177c91c866de3eb0638","transactionIndex":"0x0","blockHash":"0xd5e8d4e4eb51a2a2a6ec20ef68a4c2801240743c8deb77a6a1d118ac3eefb725","logIndex":"0x0","removed":false}]`,
},
{
f: sys.NewRangeFilter(int64(rpc.PendingBlockNumber), int64(rpc.LatestBlockNumber), nil, nil),
err: "invalid block range",
},