forked from cerc-io/plugeth
eth/gasprice: feeHistory improvements (#23422)
* eth/gasprice: cache feeHistory results * eth/gasprice: changed feeHistory block count limitation * eth/gasprice: do not use embedded struct in blockFees * eth/gasprice: fee processing logic cleanup * eth/gasprice: purge feeHistory cache at chain reorgs
This commit is contained in:
parent
dfeb2f7e80
commit
f38abc55f1
@ -43,8 +43,8 @@ import (
|
|||||||
var FullNodeGPO = gasprice.Config{
|
var FullNodeGPO = gasprice.Config{
|
||||||
Blocks: 20,
|
Blocks: 20,
|
||||||
Percentile: 60,
|
Percentile: 60,
|
||||||
MaxHeaderHistory: 0,
|
MaxHeaderHistory: 1024,
|
||||||
MaxBlockHistory: 0,
|
MaxBlockHistory: 1024,
|
||||||
MaxPrice: gasprice.DefaultMaxPrice,
|
MaxPrice: gasprice.DefaultMaxPrice,
|
||||||
IgnorePrice: gasprice.DefaultIgnorePrice,
|
IgnorePrice: gasprice.DefaultIgnorePrice,
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,10 @@ package gasprice
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sort"
|
"sort"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@ -37,10 +39,6 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// maxFeeHistory is the maximum number of blocks that can be retrieved for a
|
|
||||||
// fee history request.
|
|
||||||
maxFeeHistory = 1024
|
|
||||||
|
|
||||||
// maxBlockFetchers is the max number of goroutines to spin up to pull blocks
|
// maxBlockFetchers is the max number of goroutines to spin up to pull blocks
|
||||||
// for the fee history calculation (mostly relevant for LES).
|
// for the fee history calculation (mostly relevant for LES).
|
||||||
maxBlockFetchers = 4
|
maxBlockFetchers = 4
|
||||||
@ -54,10 +52,15 @@ type blockFees struct {
|
|||||||
block *types.Block // only set if reward percentiles are requested
|
block *types.Block // only set if reward percentiles are requested
|
||||||
receipts types.Receipts
|
receipts types.Receipts
|
||||||
// filled by processBlock
|
// filled by processBlock
|
||||||
|
results processedFees
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
// processedFees contains the results of a processed block and is also used for caching
|
||||||
|
type processedFees struct {
|
||||||
reward []*big.Int
|
reward []*big.Int
|
||||||
baseFee, nextBaseFee *big.Int
|
baseFee, nextBaseFee *big.Int
|
||||||
gasUsedRatio float64
|
gasUsedRatio float64
|
||||||
err error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// txGasAndReward is sorted in ascending order based on reward
|
// txGasAndReward is sorted in ascending order based on reward
|
||||||
@ -82,15 +85,15 @@ func (s sortGasAndReward) Less(i, j int) bool {
|
|||||||
// fills in the rest of the fields.
|
// fills in the rest of the fields.
|
||||||
func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
|
func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
|
||||||
chainconfig := oracle.backend.ChainConfig()
|
chainconfig := oracle.backend.ChainConfig()
|
||||||
if bf.baseFee = bf.header.BaseFee; bf.baseFee == nil {
|
if bf.results.baseFee = bf.header.BaseFee; bf.results.baseFee == nil {
|
||||||
bf.baseFee = new(big.Int)
|
bf.results.baseFee = new(big.Int)
|
||||||
}
|
}
|
||||||
if chainconfig.IsLondon(big.NewInt(int64(bf.blockNumber + 1))) {
|
if chainconfig.IsLondon(big.NewInt(int64(bf.blockNumber + 1))) {
|
||||||
bf.nextBaseFee = misc.CalcBaseFee(chainconfig, bf.header)
|
bf.results.nextBaseFee = misc.CalcBaseFee(chainconfig, bf.header)
|
||||||
} else {
|
} else {
|
||||||
bf.nextBaseFee = new(big.Int)
|
bf.results.nextBaseFee = new(big.Int)
|
||||||
}
|
}
|
||||||
bf.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit)
|
bf.results.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit)
|
||||||
if len(percentiles) == 0 {
|
if len(percentiles) == 0 {
|
||||||
// rewards were not requested, return null
|
// rewards were not requested, return null
|
||||||
return
|
return
|
||||||
@ -100,11 +103,11 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bf.reward = make([]*big.Int, len(percentiles))
|
bf.results.reward = make([]*big.Int, len(percentiles))
|
||||||
if len(bf.block.Transactions()) == 0 {
|
if len(bf.block.Transactions()) == 0 {
|
||||||
// return an all zero row if there are no transactions to gather data from
|
// return an all zero row if there are no transactions to gather data from
|
||||||
for i := range bf.reward {
|
for i := range bf.results.reward {
|
||||||
bf.reward[i] = new(big.Int)
|
bf.results.reward[i] = new(big.Int)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -125,7 +128,7 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
|
|||||||
txIndex++
|
txIndex++
|
||||||
sumGasUsed += sorter[txIndex].gasUsed
|
sumGasUsed += sorter[txIndex].gasUsed
|
||||||
}
|
}
|
||||||
bf.reward[i] = sorter[txIndex].reward
|
bf.results.reward[i] = sorter[txIndex].reward
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +137,7 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
|
|||||||
// also returned if requested and available.
|
// also returned if requested and available.
|
||||||
// Note: an error is only returned if retrieving the head header has failed. If there are no
|
// Note: an error is only returned if retrieving the head header has failed. If there are no
|
||||||
// retrievable blocks in the specified range then zero block count is returned with no error.
|
// retrievable blocks in the specified range then zero block count is returned with no error.
|
||||||
func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.BlockNumber, blocks, maxHistory int) (*types.Block, []*types.Receipt, uint64, int, error) {
|
func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.BlockNumber, blocks int) (*types.Block, []*types.Receipt, uint64, int, error) {
|
||||||
var (
|
var (
|
||||||
headBlock rpc.BlockNumber
|
headBlock rpc.BlockNumber
|
||||||
pendingBlock *types.Block
|
pendingBlock *types.Block
|
||||||
@ -167,17 +170,6 @@ func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.Block
|
|||||||
} else if pendingBlock == nil && lastBlock > headBlock {
|
} else if pendingBlock == nil && lastBlock > headBlock {
|
||||||
return nil, nil, 0, 0, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, lastBlock, headBlock)
|
return nil, nil, 0, 0, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, lastBlock, headBlock)
|
||||||
}
|
}
|
||||||
if maxHistory != 0 {
|
|
||||||
// limit retrieval to the given number of latest blocks
|
|
||||||
if tooOldCount := int64(headBlock) - int64(maxHistory) - int64(lastBlock) + int64(blocks); tooOldCount > 0 {
|
|
||||||
// tooOldCount is the number of requested blocks that are too old to be served
|
|
||||||
if int64(blocks) > tooOldCount {
|
|
||||||
blocks -= int(tooOldCount)
|
|
||||||
} else {
|
|
||||||
return nil, nil, 0, 0, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ensure not trying to retrieve before genesis
|
// ensure not trying to retrieve before genesis
|
||||||
if rpc.BlockNumber(blocks) > lastBlock+1 {
|
if rpc.BlockNumber(blocks) > lastBlock+1 {
|
||||||
blocks = int(lastBlock + 1)
|
blocks = int(lastBlock + 1)
|
||||||
@ -202,6 +194,10 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast
|
|||||||
if blocks < 1 {
|
if blocks < 1 {
|
||||||
return common.Big0, nil, nil, nil, nil // returning with no data and no error means there are no retrievable blocks
|
return common.Big0, nil, nil, nil, nil // returning with no data and no error means there are no retrievable blocks
|
||||||
}
|
}
|
||||||
|
maxFeeHistory := oracle.maxHeaderHistory
|
||||||
|
if len(rewardPercentiles) != 0 {
|
||||||
|
maxFeeHistory = oracle.maxBlockHistory
|
||||||
|
}
|
||||||
if blocks > maxFeeHistory {
|
if blocks > maxFeeHistory {
|
||||||
log.Warn("Sanitizing fee history length", "requested", blocks, "truncated", maxFeeHistory)
|
log.Warn("Sanitizing fee history length", "requested", blocks, "truncated", maxFeeHistory)
|
||||||
blocks = maxFeeHistory
|
blocks = maxFeeHistory
|
||||||
@ -214,17 +210,12 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast
|
|||||||
return common.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p)
|
return common.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Only process blocks if reward percentiles were requested
|
|
||||||
maxHistory := oracle.maxHeaderHistory
|
|
||||||
if len(rewardPercentiles) != 0 {
|
|
||||||
maxHistory = oracle.maxBlockHistory
|
|
||||||
}
|
|
||||||
var (
|
var (
|
||||||
pendingBlock *types.Block
|
pendingBlock *types.Block
|
||||||
pendingReceipts []*types.Receipt
|
pendingReceipts []*types.Receipt
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
pendingBlock, pendingReceipts, lastBlock, blocks, err := oracle.resolveBlockRange(ctx, unresolvedLastBlock, blocks, maxHistory)
|
pendingBlock, pendingReceipts, lastBlock, blocks, err := oracle.resolveBlockRange(ctx, unresolvedLastBlock, blocks)
|
||||||
if err != nil || blocks == 0 {
|
if err != nil || blocks == 0 {
|
||||||
return common.Big0, nil, nil, nil, err
|
return common.Big0, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
@ -234,6 +225,10 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast
|
|||||||
next = oldestBlock
|
next = oldestBlock
|
||||||
results = make(chan *blockFees, blocks)
|
results = make(chan *blockFees, blocks)
|
||||||
)
|
)
|
||||||
|
percentileKey := make([]byte, 8*len(rewardPercentiles))
|
||||||
|
for i, p := range rewardPercentiles {
|
||||||
|
binary.LittleEndian.PutUint64(percentileKey[i*8:(i+1)*8], math.Float64bits(p))
|
||||||
|
}
|
||||||
for i := 0; i < maxBlockFetchers && i < blocks; i++ {
|
for i := 0; i < maxBlockFetchers && i < blocks; i++ {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
@ -246,25 +241,39 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast
|
|||||||
fees := &blockFees{blockNumber: blockNumber}
|
fees := &blockFees{blockNumber: blockNumber}
|
||||||
if pendingBlock != nil && blockNumber >= pendingBlock.NumberU64() {
|
if pendingBlock != nil && blockNumber >= pendingBlock.NumberU64() {
|
||||||
fees.block, fees.receipts = pendingBlock, pendingReceipts
|
fees.block, fees.receipts = pendingBlock, pendingReceipts
|
||||||
|
fees.header = fees.block.Header()
|
||||||
|
oracle.processBlock(fees, rewardPercentiles)
|
||||||
|
results <- fees
|
||||||
|
} else {
|
||||||
|
cacheKey := struct {
|
||||||
|
number uint64
|
||||||
|
percentiles string
|
||||||
|
}{blockNumber, string(percentileKey)}
|
||||||
|
|
||||||
|
if p, ok := oracle.historyCache.Get(cacheKey); ok {
|
||||||
|
fees.results = p.(processedFees)
|
||||||
|
results <- fees
|
||||||
} else {
|
} else {
|
||||||
if len(rewardPercentiles) != 0 {
|
if len(rewardPercentiles) != 0 {
|
||||||
fees.block, fees.err = oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNumber))
|
fees.block, fees.err = oracle.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNumber))
|
||||||
if fees.block != nil && fees.err == nil {
|
if fees.block != nil && fees.err == nil {
|
||||||
fees.receipts, fees.err = oracle.backend.GetReceipts(ctx, fees.block.Hash())
|
fees.receipts, fees.err = oracle.backend.GetReceipts(ctx, fees.block.Hash())
|
||||||
|
fees.header = fees.block.Header()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fees.header, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber))
|
fees.header, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber))
|
||||||
}
|
}
|
||||||
}
|
if fees.header != nil && fees.err == nil {
|
||||||
if fees.block != nil {
|
|
||||||
fees.header = fees.block.Header()
|
|
||||||
}
|
|
||||||
if fees.header != nil {
|
|
||||||
oracle.processBlock(fees, rewardPercentiles)
|
oracle.processBlock(fees, rewardPercentiles)
|
||||||
|
if fees.err == nil {
|
||||||
|
oracle.historyCache.Add(cacheKey, fees.results)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// send to results even if empty to guarantee that blocks items are sent in total
|
// send to results even if empty to guarantee that blocks items are sent in total
|
||||||
results <- fees
|
results <- fees
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
@ -279,8 +288,8 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast
|
|||||||
return common.Big0, nil, nil, nil, fees.err
|
return common.Big0, nil, nil, nil, fees.err
|
||||||
}
|
}
|
||||||
i := int(fees.blockNumber - oldestBlock)
|
i := int(fees.blockNumber - oldestBlock)
|
||||||
if fees.header != nil {
|
if fees.results.baseFee != nil {
|
||||||
reward[i], baseFee[i], baseFee[i+1], gasUsedRatio[i] = fees.reward, fees.baseFee, fees.nextBaseFee, fees.gasUsedRatio
|
reward[i], baseFee[i], baseFee[i+1], gasUsedRatio[i] = fees.results.reward, fees.results.baseFee, fees.results.nextBaseFee, fees.results.gasUsedRatio
|
||||||
} else {
|
} else {
|
||||||
// getting no block and no error means we are requesting into the future (might happen because of a reorg)
|
// getting no block and no error means we are requesting into the future (might happen because of a reorg)
|
||||||
if i < firstMissing {
|
if i < firstMissing {
|
||||||
|
@ -36,20 +36,20 @@ func TestFeeHistory(t *testing.T) {
|
|||||||
expCount int
|
expCount int
|
||||||
expErr error
|
expErr error
|
||||||
}{
|
}{
|
||||||
{false, 0, 0, 10, 30, nil, 21, 10, nil},
|
{false, 1000, 1000, 10, 30, nil, 21, 10, nil},
|
||||||
{false, 0, 0, 10, 30, []float64{0, 10}, 21, 10, nil},
|
{false, 1000, 1000, 10, 30, []float64{0, 10}, 21, 10, nil},
|
||||||
{false, 0, 0, 10, 30, []float64{20, 10}, 0, 0, errInvalidPercentile},
|
{false, 1000, 1000, 10, 30, []float64{20, 10}, 0, 0, errInvalidPercentile},
|
||||||
{false, 0, 0, 1000000000, 30, nil, 0, 31, nil},
|
{false, 1000, 1000, 1000000000, 30, nil, 0, 31, nil},
|
||||||
{false, 0, 0, 1000000000, rpc.LatestBlockNumber, nil, 0, 33, nil},
|
{false, 1000, 1000, 1000000000, rpc.LatestBlockNumber, nil, 0, 33, nil},
|
||||||
{false, 0, 0, 10, 40, nil, 0, 0, errRequestBeyondHead},
|
{false, 1000, 1000, 10, 40, nil, 0, 0, errRequestBeyondHead},
|
||||||
{true, 0, 0, 10, 40, nil, 0, 0, errRequestBeyondHead},
|
{true, 1000, 1000, 10, 40, nil, 0, 0, errRequestBeyondHead},
|
||||||
{false, 20, 2, 100, rpc.LatestBlockNumber, nil, 13, 20, nil},
|
{false, 20, 2, 100, rpc.LatestBlockNumber, nil, 13, 20, nil},
|
||||||
{false, 20, 2, 100, rpc.LatestBlockNumber, []float64{0, 10}, 31, 2, nil},
|
{false, 20, 2, 100, rpc.LatestBlockNumber, []float64{0, 10}, 31, 2, nil},
|
||||||
{false, 20, 2, 100, 32, []float64{0, 10}, 31, 2, nil},
|
{false, 20, 2, 100, 32, []float64{0, 10}, 31, 2, nil},
|
||||||
{false, 0, 0, 1, rpc.PendingBlockNumber, nil, 0, 0, nil},
|
{false, 1000, 1000, 1, rpc.PendingBlockNumber, nil, 0, 0, nil},
|
||||||
{false, 0, 0, 2, rpc.PendingBlockNumber, nil, 32, 1, nil},
|
{false, 1000, 1000, 2, rpc.PendingBlockNumber, nil, 32, 1, nil},
|
||||||
{true, 0, 0, 2, rpc.PendingBlockNumber, nil, 32, 2, nil},
|
{true, 1000, 1000, 2, rpc.PendingBlockNumber, nil, 32, 2, nil},
|
||||||
{true, 0, 0, 2, rpc.PendingBlockNumber, []float64{0, 10}, 32, 2, nil},
|
{true, 1000, 1000, 2, rpc.PendingBlockNumber, []float64{0, 10}, 32, 2, nil},
|
||||||
}
|
}
|
||||||
for i, c := range cases {
|
for i, c := range cases {
|
||||||
config := Config{
|
config := Config{
|
||||||
|
@ -23,10 +23,13 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
lru "github.com/hashicorp/golang-lru"
|
||||||
)
|
)
|
||||||
|
|
||||||
const sampleNumber = 3 // Number of transactions sampled in a block
|
const sampleNumber = 3 // Number of transactions sampled in a block
|
||||||
@ -53,6 +56,7 @@ type OracleBackend interface {
|
|||||||
GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
|
GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error)
|
||||||
PendingBlockAndReceipts() (*types.Block, types.Receipts)
|
PendingBlockAndReceipts() (*types.Block, types.Receipts)
|
||||||
ChainConfig() *params.ChainConfig
|
ChainConfig() *params.ChainConfig
|
||||||
|
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||||
}
|
}
|
||||||
|
|
||||||
// Oracle recommends gas prices based on the content of recent
|
// Oracle recommends gas prices based on the content of recent
|
||||||
@ -68,6 +72,7 @@ type Oracle struct {
|
|||||||
|
|
||||||
checkBlocks, percentile int
|
checkBlocks, percentile int
|
||||||
maxHeaderHistory, maxBlockHistory int
|
maxHeaderHistory, maxBlockHistory int
|
||||||
|
historyCache *lru.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOracle returns a new gasprice oracle which can recommend suitable
|
// NewOracle returns a new gasprice oracle which can recommend suitable
|
||||||
@ -99,6 +104,20 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
|
|||||||
} else if ignorePrice.Int64() > 0 {
|
} else if ignorePrice.Int64() > 0 {
|
||||||
log.Info("Gasprice oracle is ignoring threshold set", "threshold", ignorePrice)
|
log.Info("Gasprice oracle is ignoring threshold set", "threshold", ignorePrice)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cache, _ := lru.New(2048)
|
||||||
|
headEvent := make(chan core.ChainHeadEvent, 1)
|
||||||
|
backend.SubscribeChainHeadEvent(headEvent)
|
||||||
|
go func() {
|
||||||
|
var lastHead common.Hash
|
||||||
|
for ev := range headEvent {
|
||||||
|
if ev.Block.ParentHash() != lastHead {
|
||||||
|
cache.Purge()
|
||||||
|
}
|
||||||
|
lastHead = ev.Block.Hash()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
return &Oracle{
|
return &Oracle{
|
||||||
backend: backend,
|
backend: backend,
|
||||||
lastPrice: params.Default,
|
lastPrice: params.Default,
|
||||||
@ -108,6 +127,7 @@ func NewOracle(backend OracleBackend, params Config) *Oracle {
|
|||||||
percentile: percent,
|
percentile: percent,
|
||||||
maxHeaderHistory: params.MaxHeaderHistory,
|
maxHeaderHistory: params.MaxHeaderHistory,
|
||||||
maxBlockHistory: params.MaxBlockHistory,
|
maxBlockHistory: params.MaxBlockHistory,
|
||||||
|
historyCache: cache,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
@ -90,6 +91,10 @@ func (b *testBackend) ChainConfig() *params.ChainConfig {
|
|||||||
return b.chain.Config()
|
return b.chain.Config()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBackend {
|
func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBackend {
|
||||||
var (
|
var (
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
Loading…
Reference in New Issue
Block a user