mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
Merge tag 'v1.15.11' into release/geth-1.15.11-fh3.0
This commit is contained in:
commit
3334d91d3b
48 changed files with 5035 additions and 294 deletions
|
|
@ -131,7 +131,7 @@ type executionPayloadEnvelopeMarshaling struct {
|
||||||
|
|
||||||
type PayloadStatusV1 struct {
|
type PayloadStatusV1 struct {
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
Witness *hexutil.Bytes `json:"witness"`
|
Witness *hexutil.Bytes `json:"witness,omitempty"`
|
||||||
LatestValidHash *common.Hash `json:"latestValidHash"`
|
LatestValidHash *common.Hash `json:"latestValidHash"`
|
||||||
ValidationError *string `json:"validationError"`
|
ValidationError *string `json:"validationError"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ func (s *Suite) EthTests() []utesting.Test {
|
||||||
{Name: "Status", Fn: s.TestStatus},
|
{Name: "Status", Fn: s.TestStatus},
|
||||||
// get block headers
|
// get block headers
|
||||||
{Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders},
|
{Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders},
|
||||||
|
{Name: "GetNonexistentBlockHeaders", Fn: s.TestGetNonexistentBlockHeaders},
|
||||||
{Name: "SimultaneousRequests", Fn: s.TestSimultaneousRequests},
|
{Name: "SimultaneousRequests", Fn: s.TestSimultaneousRequests},
|
||||||
{Name: "SameRequestID", Fn: s.TestSameRequestID},
|
{Name: "SameRequestID", Fn: s.TestSameRequestID},
|
||||||
{Name: "ZeroRequestID", Fn: s.TestZeroRequestID},
|
{Name: "ZeroRequestID", Fn: s.TestZeroRequestID},
|
||||||
|
|
@ -158,6 +159,48 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Suite) TestGetNonexistentBlockHeaders(t *utesting.T) {
|
||||||
|
t.Log(`This test sends GetBlockHeaders requests for nonexistent blocks (using max uint64 value)
|
||||||
|
to check if the node disconnects after receiving multiple invalid requests.`)
|
||||||
|
|
||||||
|
conn, err := s.dial()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("dial failed: %v", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
if err := conn.peer(s.chain, nil); err != nil {
|
||||||
|
t.Fatalf("peering failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create request with max uint64 value for a nonexistent block
|
||||||
|
badReq := ð.GetBlockHeadersPacket{
|
||||||
|
GetBlockHeadersRequest: ð.GetBlockHeadersRequest{
|
||||||
|
Origin: eth.HashOrNumber{Number: ^uint64(0)},
|
||||||
|
Amount: 1,
|
||||||
|
Skip: 0,
|
||||||
|
Reverse: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send request 10 times. Some clients are lient on the first few invalids.
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
badReq.RequestId = uint64(i)
|
||||||
|
if err := conn.Write(ethProto, eth.GetBlockHeadersMsg, badReq); err != nil {
|
||||||
|
if err == errDisc {
|
||||||
|
t.Fatalf("peer disconnected after %d requests", i+1)
|
||||||
|
}
|
||||||
|
t.Fatalf("write failed: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if peer disconnects at the end.
|
||||||
|
code, _, err := conn.Read()
|
||||||
|
if err == errDisc || code == discMsg {
|
||||||
|
t.Fatal("peer improperly disconnected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Suite) TestSimultaneousRequests(t *utesting.T) {
|
func (s *Suite) TestSimultaneousRequests(t *utesting.T) {
|
||||||
t.Log(`This test requests blocks headers from the node, performing two requests
|
t.Log(`This test requests blocks headers from the node, performing two requests
|
||||||
concurrently, with different request IDs.`)
|
concurrently, with different request IDs.`)
|
||||||
|
|
|
||||||
|
|
@ -246,10 +246,13 @@ func initGenesis(ctx *cli.Context) error {
|
||||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
|
||||||
_, hash, _, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
_, hash, compatErr, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to write genesis block: %v", err)
|
utils.Fatalf("Failed to write genesis block: %v", err)
|
||||||
}
|
}
|
||||||
|
if compatErr != nil {
|
||||||
|
utils.Fatalf("Failed to write chain config: %v", compatErr)
|
||||||
|
}
|
||||||
log.Info("Successfully wrote genesis state", "database", "chaindata", "hash", hash)
|
log.Info("Successfully wrote genesis state", "database", "chaindata", "hash", hash)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -1855,10 +1855,16 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||||
if ctx.String(CryptoKZGFlag.Name) != "gokzg" && ctx.String(CryptoKZGFlag.Name) != "ckzg" {
|
if ctx.String(CryptoKZGFlag.Name) != "gokzg" && ctx.String(CryptoKZGFlag.Name) != "ckzg" {
|
||||||
Fatalf("--%s flag must be 'gokzg' or 'ckzg'", CryptoKZGFlag.Name)
|
Fatalf("--%s flag must be 'gokzg' or 'ckzg'", CryptoKZGFlag.Name)
|
||||||
}
|
}
|
||||||
|
// The initialization of the KZG library can take up to 2 seconds
|
||||||
|
// We start this here in parallel, so it should be available
|
||||||
|
// once we start executing blocks. It's threadsafe.
|
||||||
|
go func() {
|
||||||
log.Info("Initializing the KZG library", "backend", ctx.String(CryptoKZGFlag.Name))
|
log.Info("Initializing the KZG library", "backend", ctx.String(CryptoKZGFlag.Name))
|
||||||
if err := kzg4844.UseCKZG(ctx.String(CryptoKZGFlag.Name) == "ckzg"); err != nil {
|
if err := kzg4844.UseCKZG(ctx.String(CryptoKZGFlag.Name) == "ckzg"); err != nil {
|
||||||
Fatalf("Failed to set KZG library implementation to %s: %v", ctx.String(CryptoKZGFlag.Name), err)
|
Fatalf("Failed to set KZG library implementation to %s: %v", ctx.String(CryptoKZGFlag.Name), err)
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// VM tracing config.
|
// VM tracing config.
|
||||||
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
||||||
cfg.VMTrace = name
|
cfg.VMTrace = name
|
||||||
|
|
|
||||||
|
|
@ -234,6 +234,14 @@ func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||||
return receipts
|
return receipts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bc *BlockChain) GetRawReceiptsByHash(hash common.Hash) types.Receipts {
|
||||||
|
number := rawdb.ReadHeaderNumber(bc.db, hash)
|
||||||
|
if number == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return rawdb.ReadRawReceipts(bc.db, hash, *number)
|
||||||
|
}
|
||||||
|
|
||||||
// GetUnclesInChain retrieves all the uncles from a given block backwards until
|
// GetUnclesInChain retrieves all the uncles from a given block backwards until
|
||||||
// a specific distance is reached.
|
// a specific distance is reached.
|
||||||
func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header {
|
func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header {
|
||||||
|
|
@ -262,42 +270,20 @@ func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, max
|
||||||
// GetTransactionLookup retrieves the lookup along with the transaction
|
// GetTransactionLookup retrieves the lookup along with the transaction
|
||||||
// itself associate with the given transaction hash.
|
// itself associate with the given transaction hash.
|
||||||
//
|
//
|
||||||
// An error will be returned if the transaction is not found, and background
|
// A null will be returned if the transaction is not found. This can be due to
|
||||||
// indexing for transactions is still in progress. The transaction might be
|
// the transaction indexer not being finished. The caller must explicitly check
|
||||||
// reachable shortly once it's indexed.
|
// the indexer progress.
|
||||||
//
|
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction) {
|
||||||
// A null will be returned in the transaction is not found and background
|
|
||||||
// transaction indexing is already finished. The transaction is not existent
|
|
||||||
// from the node's perspective.
|
|
||||||
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction, error) {
|
|
||||||
bc.txLookupLock.RLock()
|
bc.txLookupLock.RLock()
|
||||||
defer bc.txLookupLock.RUnlock()
|
defer bc.txLookupLock.RUnlock()
|
||||||
|
|
||||||
// Short circuit if the txlookup already in the cache, retrieve otherwise
|
// Short circuit if the txlookup already in the cache, retrieve otherwise
|
||||||
if item, exist := bc.txLookupCache.Get(hash); exist {
|
if item, exist := bc.txLookupCache.Get(hash); exist {
|
||||||
return item.lookup, item.transaction, nil
|
return item.lookup, item.transaction
|
||||||
}
|
}
|
||||||
tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
|
tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
|
||||||
if tx == nil {
|
if tx == nil {
|
||||||
progress, err := bc.TxIndexProgress()
|
return nil, nil
|
||||||
if err != nil {
|
|
||||||
// No error is returned if the transaction indexing progress is unreachable
|
|
||||||
// due to unexpected internal errors. In such cases, it is impossible to
|
|
||||||
// determine whether the transaction does not exist or has simply not been
|
|
||||||
// indexed yet without a progress marker.
|
|
||||||
//
|
|
||||||
// In such scenarios, the transaction is treated as unreachable, though
|
|
||||||
// this is clearly an unintended and unexpected situation.
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
|
||||||
// The transaction indexing is not finished yet, returning an
|
|
||||||
// error to explicitly indicate it.
|
|
||||||
if !progress.Done() {
|
|
||||||
return nil, nil, errors.New("transaction indexing still in progress")
|
|
||||||
}
|
|
||||||
// The transaction is already indexed, the transaction is either
|
|
||||||
// not existent or not in the range of index, returning null.
|
|
||||||
return nil, nil, nil
|
|
||||||
}
|
}
|
||||||
lookup := &rawdb.LegacyTxLookupEntry{
|
lookup := &rawdb.LegacyTxLookupEntry{
|
||||||
BlockHash: blockHash,
|
BlockHash: blockHash,
|
||||||
|
|
@ -308,7 +294,23 @@ func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLoo
|
||||||
lookup: lookup,
|
lookup: lookup,
|
||||||
transaction: tx,
|
transaction: tx,
|
||||||
})
|
})
|
||||||
return lookup, tx, nil
|
return lookup, tx
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxIndexDone returns true if the transaction indexer has finished indexing.
|
||||||
|
func (bc *BlockChain) TxIndexDone() bool {
|
||||||
|
progress, err := bc.TxIndexProgress()
|
||||||
|
if err != nil {
|
||||||
|
// No error is returned if the transaction indexing progress is unreachable
|
||||||
|
// due to unexpected internal errors. In such cases, it is impossible to
|
||||||
|
// determine whether the transaction does not exist or has simply not been
|
||||||
|
// indexed yet without a progress marker.
|
||||||
|
//
|
||||||
|
// In such scenarios, the transaction is treated as unreachable, though
|
||||||
|
// this is clearly an unintended and unexpected situation.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return progress.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasState checks if state trie is fully present in the database or not.
|
// HasState checks if state trie is fully present in the database or not.
|
||||||
|
|
@ -404,7 +406,7 @@ func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) {
|
||||||
if bc.txIndexer == nil {
|
if bc.txIndexer == nil {
|
||||||
return TxIndexProgress{}, errors.New("tx indexer is not enabled")
|
return TxIndexProgress{}, errors.New("tx indexer is not enabled")
|
||||||
}
|
}
|
||||||
return bc.txIndexer.txIndexProgress()
|
return bc.txIndexer.txIndexProgress(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HistoryPruningCutoff returns the configured history pruning point.
|
// HistoryPruningCutoff returns the configured history pruning point.
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ type blockchain interface {
|
||||||
GetHeader(hash common.Hash, number uint64) *types.Header
|
GetHeader(hash common.Hash, number uint64) *types.Header
|
||||||
GetCanonicalHash(number uint64) common.Hash
|
GetCanonicalHash(number uint64) common.Hash
|
||||||
GetReceiptsByHash(hash common.Hash) types.Receipts
|
GetReceiptsByHash(hash common.Hash) types.Receipts
|
||||||
|
GetRawReceiptsByHash(hash common.Hash) types.Receipts
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChainView represents an immutable view of a chain with a block id and a set
|
// ChainView represents an immutable view of a chain with a block id and a set
|
||||||
|
|
@ -102,10 +103,23 @@ func (cv *ChainView) Receipts(number uint64) types.Receipts {
|
||||||
blockHash := cv.BlockHash(number)
|
blockHash := cv.BlockHash(number)
|
||||||
if blockHash == (common.Hash{}) {
|
if blockHash == (common.Hash{}) {
|
||||||
log.Error("Chain view: block hash unavailable", "number", number, "head", cv.headNumber)
|
log.Error("Chain view: block hash unavailable", "number", number, "head", cv.headNumber)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return cv.chain.GetReceiptsByHash(blockHash)
|
return cv.chain.GetReceiptsByHash(blockHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RawReceipts returns the set of receipts belonging to the block at the given
|
||||||
|
// block number. Does not derive the fields of the receipts, should only be
|
||||||
|
// used during creation of the filter maps, please use cv.Receipts during querying.
|
||||||
|
func (cv *ChainView) RawReceipts(number uint64) types.Receipts {
|
||||||
|
blockHash := cv.BlockHash(number)
|
||||||
|
if blockHash == (common.Hash{}) {
|
||||||
|
log.Error("Chain view: block hash unavailable", "number", number, "head", cv.headNumber)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return cv.chain.GetRawReceiptsByHash(blockHash)
|
||||||
|
}
|
||||||
|
|
||||||
// SharedRange returns the block range shared by two chain views.
|
// SharedRange returns the block range shared by two chain views.
|
||||||
func (cv *ChainView) SharedRange(cv2 *ChainView) common.Range[uint64] {
|
func (cv *ChainView) SharedRange(cv2 *ChainView) common.Range[uint64] {
|
||||||
cv.lock.Lock()
|
cv.lock.Lock()
|
||||||
|
|
@ -121,14 +135,6 @@ func (cv *ChainView) SharedRange(cv2 *ChainView) common.Range[uint64] {
|
||||||
return common.NewRange(0, sharedLen)
|
return common.NewRange(0, sharedLen)
|
||||||
}
|
}
|
||||||
|
|
||||||
// limitedView returns a new chain view that is a truncated version of the parent view.
|
|
||||||
func (cv *ChainView) limitedView(newHead uint64) *ChainView {
|
|
||||||
if newHead >= cv.headNumber {
|
|
||||||
return cv
|
|
||||||
}
|
|
||||||
return NewChainView(cv.chain, newHead, cv.BlockHash(newHead))
|
|
||||||
}
|
|
||||||
|
|
||||||
// equalViews returns true if the two chain views are equivalent.
|
// equalViews returns true if the two chain views are equivalent.
|
||||||
func equalViews(cv1, cv2 *ChainView) bool {
|
func equalViews(cv1, cv2 *ChainView) bool {
|
||||||
if cv1 == nil || cv2 == nil {
|
if cv1 == nil || cv2 == nil {
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
databaseVersion = 1 // reindexed if database version does not match
|
databaseVersion = 2 // reindexed if database version does not match
|
||||||
cachedLastBlocks = 1000 // last block of map pointers
|
cachedLastBlocks = 1000 // last block of map pointers
|
||||||
cachedLvPointers = 1000 // first log value pointer of block pointers
|
cachedLvPointers = 1000 // first log value pointer of block pointers
|
||||||
cachedBaseRows = 100 // groups of base layer filter row data
|
cachedBaseRows = 100 // groups of base layer filter row data
|
||||||
|
|
@ -244,6 +244,8 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f
|
||||||
disabledCh: make(chan struct{}),
|
disabledCh: make(chan struct{}),
|
||||||
exportFileName: config.ExportFileName,
|
exportFileName: config.ExportFileName,
|
||||||
Params: params,
|
Params: params,
|
||||||
|
targetView: initView,
|
||||||
|
indexedView: initView,
|
||||||
indexedRange: filterMapsRange{
|
indexedRange: filterMapsRange{
|
||||||
initialized: initialized,
|
initialized: initialized,
|
||||||
headIndexed: rs.HeadIndexed,
|
headIndexed: rs.HeadIndexed,
|
||||||
|
|
@ -265,16 +267,8 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f
|
||||||
baseRowsCache: lru.NewCache[uint64, [][]uint32](cachedBaseRows),
|
baseRowsCache: lru.NewCache[uint64, [][]uint32](cachedBaseRows),
|
||||||
renderSnapshots: lru.NewCache[uint64, *renderedMap](cachedRenderSnapshots),
|
renderSnapshots: lru.NewCache[uint64, *renderedMap](cachedRenderSnapshots),
|
||||||
}
|
}
|
||||||
|
f.checkRevertRange() // revert maps that are inconsistent with the current chain view
|
||||||
|
|
||||||
// Set initial indexer target.
|
|
||||||
f.targetView = initView
|
|
||||||
if f.indexedRange.initialized {
|
|
||||||
f.indexedView = f.initChainView(f.targetView)
|
|
||||||
f.indexedRange.headIndexed = f.indexedRange.blocks.AfterLast() == f.indexedView.HeadNumber()+1
|
|
||||||
if !f.indexedRange.headIndexed {
|
|
||||||
f.indexedRange.headDelimiter = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if f.indexedRange.hasIndexedBlocks() {
|
if f.indexedRange.hasIndexedBlocks() {
|
||||||
log.Info("Initialized log indexer",
|
log.Info("Initialized log indexer",
|
||||||
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
||||||
|
|
@ -303,29 +297,40 @@ func (f *FilterMaps) Stop() {
|
||||||
f.closeWg.Wait()
|
f.closeWg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
// initChainView returns a chain view consistent with both the current target
|
// checkRevertRange checks whether the existing index is consistent with the
|
||||||
// view and the current state of the log index as found in the database, based
|
// current indexed view and reverts inconsistent maps if necessary.
|
||||||
// on the last block of stored maps.
|
func (f *FilterMaps) checkRevertRange() {
|
||||||
// Note that the returned view might be shorter than the existing index if
|
if f.indexedRange.maps.Count() == 0 {
|
||||||
// the latest maps are not consistent with targetView.
|
return
|
||||||
func (f *FilterMaps) initChainView(chainView *ChainView) *ChainView {
|
|
||||||
mapIndex := f.indexedRange.maps.AfterLast()
|
|
||||||
for {
|
|
||||||
var ok bool
|
|
||||||
mapIndex, ok = f.lastMapBoundaryBefore(mapIndex)
|
|
||||||
if !ok {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
lastBlockNumber, lastBlockId, err := f.getLastBlockOfMap(mapIndex)
|
lastMap := f.indexedRange.maps.Last()
|
||||||
|
lastBlockNumber, lastBlockId, err := f.getLastBlockOfMap(lastMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Could not initialize indexed chain view", "error", err)
|
log.Error("Error initializing log index database; resetting log index", "error", err)
|
||||||
break
|
f.reset()
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if lastBlockNumber <= chainView.HeadNumber() && chainView.BlockId(lastBlockNumber) == lastBlockId {
|
for lastBlockNumber > f.indexedView.HeadNumber() || f.indexedView.BlockId(lastBlockNumber) != lastBlockId {
|
||||||
return chainView.limitedView(lastBlockNumber)
|
// revert last map
|
||||||
|
if f.indexedRange.maps.Count() == 1 {
|
||||||
|
f.reset() // reset database if no rendered maps remained
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
lastMap--
|
||||||
|
newRange := f.indexedRange
|
||||||
|
newRange.maps.SetLast(lastMap)
|
||||||
|
lastBlockNumber, lastBlockId, err = f.getLastBlockOfMap(lastMap)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error initializing log index database; resetting log index", "error", err)
|
||||||
|
f.reset()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
newRange.blocks.SetAfterLast(lastBlockNumber) // lastBlockNumber is probably partially indexed
|
||||||
|
newRange.headIndexed = false
|
||||||
|
newRange.headDelimiter = 0
|
||||||
|
// only shorten range and leave map data; next head render will overwrite it
|
||||||
|
f.setRange(f.db, f.indexedView, newRange, false)
|
||||||
}
|
}
|
||||||
return chainView.limitedView(0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset un-initializes the FilterMaps structure and removes all related data from
|
// reset un-initializes the FilterMaps structure and removes all related data from
|
||||||
|
|
@ -662,15 +667,11 @@ func (f *FilterMaps) mapRowIndex(mapIndex, rowIndex uint32) uint64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// getBlockLvPointer returns the starting log value index where the log values
|
// getBlockLvPointer returns the starting log value index where the log values
|
||||||
// generated by the given block are located. If blockNumber is beyond the current
|
// generated by the given block are located.
|
||||||
// head then the first unoccupied log value index is returned.
|
|
||||||
//
|
//
|
||||||
// Note that this function assumes that the indexer read lock is being held when
|
// Note that this function assumes that the indexer read lock is being held when
|
||||||
// called from outside the indexerLoop goroutine.
|
// called from outside the indexerLoop goroutine.
|
||||||
func (f *FilterMaps) getBlockLvPointer(blockNumber uint64) (uint64, error) {
|
func (f *FilterMaps) getBlockLvPointer(blockNumber uint64) (uint64, error) {
|
||||||
if blockNumber >= f.indexedRange.blocks.AfterLast() && f.indexedRange.headIndexed {
|
|
||||||
return f.indexedRange.headDelimiter + 1, nil
|
|
||||||
}
|
|
||||||
if lvPointer, ok := f.lvPointerCache.Get(blockNumber); ok {
|
if lvPointer, ok := f.lvPointerCache.Get(blockNumber); ok {
|
||||||
return lvPointer, nil
|
return lvPointer, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -515,6 +515,13 @@ func (tc *testChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||||
return tc.receipts[hash]
|
return tc.receipts[hash]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tc *testChain) GetRawReceiptsByHash(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) {
|
func (tc *testChain) addBlocks(count, maxTxPerBlock, maxLogsPerReceipt, maxTopicsPerLog int, random bool) {
|
||||||
tc.lock.Lock()
|
tc.lock.Lock()
|
||||||
blockGen := func(i int, gen *core.BlockGen) {
|
blockGen := func(i int, gen *core.BlockGen) {
|
||||||
|
|
|
||||||
|
|
@ -468,15 +468,25 @@ func (r *mapRenderer) writeFinishedMaps(pauseCb func() bool) error {
|
||||||
r.f.filterMapCache.Remove(mapIndex)
|
r.f.filterMapCache.Remove(mapIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var blockNumber uint64
|
||||||
|
if r.finished.First() > 0 {
|
||||||
|
// in order to always ensure continuous block pointers, initialize
|
||||||
|
// blockNumber based on the last block of the previous map, then verify
|
||||||
|
// against the first block associated with each rendered map
|
||||||
|
lastBlock, _, err := r.f.getLastBlockOfMap(r.finished.First() - 1)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get last block of previous map %d: %v", r.finished.First()-1, err)
|
||||||
|
}
|
||||||
|
blockNumber = lastBlock + 1
|
||||||
|
}
|
||||||
// add or update block pointers
|
// add or update block pointers
|
||||||
blockNumber := r.finishedMaps[r.finished.First()].firstBlock()
|
|
||||||
for mapIndex := range r.finished.Iter() {
|
for mapIndex := range r.finished.Iter() {
|
||||||
renderedMap := r.finishedMaps[mapIndex]
|
renderedMap := r.finishedMaps[mapIndex]
|
||||||
|
if blockNumber != renderedMap.firstBlock() {
|
||||||
|
return fmt.Errorf("non-continuous block numbers in rendered map %d (next expected: %d first rendered: %d)", mapIndex, blockNumber, renderedMap.firstBlock())
|
||||||
|
}
|
||||||
r.f.storeLastBlockOfMap(batch, mapIndex, renderedMap.lastBlock, renderedMap.lastBlockId)
|
r.f.storeLastBlockOfMap(batch, mapIndex, renderedMap.lastBlock, renderedMap.lastBlockId)
|
||||||
checkWriteCnt()
|
checkWriteCnt()
|
||||||
if blockNumber != renderedMap.firstBlock() {
|
|
||||||
panic("non-continuous block numbers")
|
|
||||||
}
|
|
||||||
for _, lvPtr := range renderedMap.blockLvPtrs {
|
for _, lvPtr := range renderedMap.blockLvPtrs {
|
||||||
r.f.storeBlockLvPointer(batch, blockNumber, lvPtr)
|
r.f.storeBlockLvPointer(batch, blockNumber, lvPtr)
|
||||||
checkWriteCnt()
|
checkWriteCnt()
|
||||||
|
|
@ -693,7 +703,7 @@ func (f *FilterMaps) newLogIteratorFromMapBoundary(mapIndex uint32, startBlock,
|
||||||
return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", startBlock, f.targetView.HeadNumber())
|
return nil, fmt.Errorf("iterator entry point %d after target chain head block %d", startBlock, f.targetView.HeadNumber())
|
||||||
}
|
}
|
||||||
// get block receipts
|
// get block receipts
|
||||||
receipts := f.targetView.Receipts(startBlock)
|
receipts := f.targetView.RawReceipts(startBlock)
|
||||||
if receipts == nil {
|
if receipts == nil {
|
||||||
return nil, fmt.Errorf("receipts not found for start block %d", startBlock)
|
return nil, fmt.Errorf("receipts not found for start block %d", startBlock)
|
||||||
}
|
}
|
||||||
|
|
@ -760,7 +770,7 @@ func (l *logIterator) next() error {
|
||||||
if l.delimiter {
|
if l.delimiter {
|
||||||
l.delimiter = false
|
l.delimiter = false
|
||||||
l.blockNumber++
|
l.blockNumber++
|
||||||
l.receipts = l.chainView.Receipts(l.blockNumber)
|
l.receipts = l.chainView.RawReceipts(l.blockNumber)
|
||||||
if l.receipts == nil {
|
if l.receipts == nil {
|
||||||
return fmt.Errorf("receipts not found for block %d", l.blockNumber)
|
return fmt.Errorf("receipts not found for block %d", l.blockNumber)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,13 +82,26 @@ func (fm *FilterMapsMatcherBackend) GetFilterMapRow(ctx context.Context, mapInde
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlockLvPointer returns the starting log value index where the log values
|
// GetBlockLvPointer returns the starting log value index where the log values
|
||||||
// generated by the given block are located. If blockNumber is beyond the current
|
// generated by the given block are located. If blockNumber is beyond the last
|
||||||
// head then the first unoccupied log value index is returned.
|
// indexed block then the pointer will point right after this block, ensuring
|
||||||
|
// that the matcher does not fail and can return a set of results where the
|
||||||
|
// valid range is correct.
|
||||||
// GetBlockLvPointer implements MatcherBackend.
|
// GetBlockLvPointer implements MatcherBackend.
|
||||||
func (fm *FilterMapsMatcherBackend) GetBlockLvPointer(ctx context.Context, blockNumber uint64) (uint64, error) {
|
func (fm *FilterMapsMatcherBackend) GetBlockLvPointer(ctx context.Context, blockNumber uint64) (uint64, error) {
|
||||||
fm.f.indexLock.RLock()
|
fm.f.indexLock.RLock()
|
||||||
defer fm.f.indexLock.RUnlock()
|
defer fm.f.indexLock.RUnlock()
|
||||||
|
|
||||||
|
if blockNumber >= fm.f.indexedRange.blocks.AfterLast() {
|
||||||
|
if fm.f.indexedRange.headIndexed {
|
||||||
|
// return index after head block
|
||||||
|
return fm.f.indexedRange.headDelimiter + 1, nil
|
||||||
|
}
|
||||||
|
if fm.f.indexedRange.blocks.Count() > 0 {
|
||||||
|
// return index at the beginning of the last, partially indexed
|
||||||
|
// block (after the last fully indexed one)
|
||||||
|
blockNumber = fm.f.indexedRange.blocks.Last()
|
||||||
|
}
|
||||||
|
}
|
||||||
return fm.f.getBlockLvPointer(blockNumber)
|
return fm.f.getBlockLvPointer(blockNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -365,6 +365,9 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
|
||||||
return nil, common.Hash{}, nil, errors.New("missing head header")
|
return nil, common.Hash{}, nil, errors.New("missing head header")
|
||||||
}
|
}
|
||||||
newCfg := genesis.chainConfigOrDefault(ghash, storedCfg)
|
newCfg := genesis.chainConfigOrDefault(ghash, storedCfg)
|
||||||
|
if err := overrides.apply(newCfg); err != nil {
|
||||||
|
return nil, common.Hash{}, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// Sanity-check the new configuration.
|
// Sanity-check the new configuration.
|
||||||
if err := newCfg.CheckConfigForkOrder(); err != nil {
|
if err := newCfg.CheckConfigForkOrder(); err != nil {
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ import (
|
||||||
// message no matter the execution itself is successful or not.
|
// message no matter the execution itself is successful or not.
|
||||||
type ExecutionResult struct {
|
type ExecutionResult struct {
|
||||||
UsedGas uint64 // Total used gas, not including the refunded gas
|
UsedGas uint64 // Total used gas, not including the refunded gas
|
||||||
RefundedGas uint64 // Total gas refunded after execution
|
MaxUsedGas uint64 // Maximum gas consumed during execution, excluding gas refunds.
|
||||||
Err error // Any error encountered during the execution(listed in core/vm/errors.go)
|
Err error // Any error encountered during the execution(listed in core/vm/errors.go)
|
||||||
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
|
ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode)
|
||||||
}
|
}
|
||||||
|
|
@ -509,9 +509,12 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
ret, st.gasRemaining, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, value)
|
ret, st.gasRemaining, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Record the gas used excluding gas refunds. This value represents the actual
|
||||||
|
// gas allowance required to complete execution.
|
||||||
|
peakGasUsed := st.gasUsed()
|
||||||
|
|
||||||
// Compute refund counter, capped to a refund quotient.
|
// Compute refund counter, capped to a refund quotient.
|
||||||
gasRefund := st.calcRefund()
|
st.gasRemaining += st.calcRefund()
|
||||||
st.gasRemaining += gasRefund
|
|
||||||
if rules.IsPrague {
|
if rules.IsPrague {
|
||||||
// After EIP-7623: Data-heavy transactions pay the floor gas.
|
// After EIP-7623: Data-heavy transactions pay the floor gas.
|
||||||
if st.gasUsed() < floorDataGas {
|
if st.gasUsed() < floorDataGas {
|
||||||
|
|
@ -521,6 +524,9 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
t.OnGasChange(prev, st.gasRemaining, tracing.GasChangeTxDataFloor)
|
t.OnGasChange(prev, st.gasRemaining, tracing.GasChangeTxDataFloor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if peakGasUsed < floorDataGas {
|
||||||
|
peakGasUsed = floorDataGas
|
||||||
|
}
|
||||||
}
|
}
|
||||||
st.returnGas()
|
st.returnGas()
|
||||||
|
|
||||||
|
|
@ -550,7 +556,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
|
|
||||||
return &ExecutionResult{
|
return &ExecutionResult{
|
||||||
UsedGas: st.gasUsed(),
|
UsedGas: st.gasUsed(),
|
||||||
RefundedGas: gasRefund,
|
MaxUsedGas: peakGasUsed,
|
||||||
Err: vmerr,
|
Err: vmerr,
|
||||||
ReturnData: ret,
|
ReturnData: ret,
|
||||||
}, nil
|
}, nil
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
|
@ -47,11 +47,21 @@ type txIndexer struct {
|
||||||
// and all others shouldn't.
|
// and all others shouldn't.
|
||||||
limit uint64
|
limit uint64
|
||||||
|
|
||||||
|
// The current head of blockchain for transaction indexing. This field
|
||||||
|
// is accessed by both the indexer and the indexing progress queries.
|
||||||
|
head atomic.Uint64
|
||||||
|
|
||||||
|
// The current tail of the indexed transactions, null indicates
|
||||||
|
// that no transactions have been indexed yet.
|
||||||
|
//
|
||||||
|
// This field is accessed by both the indexer and the indexing
|
||||||
|
// progress queries.
|
||||||
|
tail atomic.Pointer[uint64]
|
||||||
|
|
||||||
// cutoff denotes the block number before which the chain segment should
|
// cutoff denotes the block number before which the chain segment should
|
||||||
// be pruned and not available locally.
|
// be pruned and not available locally.
|
||||||
cutoff uint64
|
cutoff uint64
|
||||||
db ethdb.Database
|
db ethdb.Database
|
||||||
progress chan chan TxIndexProgress
|
|
||||||
term chan chan struct{}
|
term chan chan struct{}
|
||||||
closed chan struct{}
|
closed chan struct{}
|
||||||
}
|
}
|
||||||
|
|
@ -63,10 +73,12 @@ func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer {
|
||||||
limit: limit,
|
limit: limit,
|
||||||
cutoff: cutoff,
|
cutoff: cutoff,
|
||||||
db: chain.db,
|
db: chain.db,
|
||||||
progress: make(chan chan TxIndexProgress),
|
|
||||||
term: make(chan chan struct{}),
|
term: make(chan chan struct{}),
|
||||||
closed: make(chan struct{}),
|
closed: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
indexer.head.Store(indexer.resolveHead())
|
||||||
|
indexer.tail.Store(rawdb.ReadTxIndexTail(chain.db))
|
||||||
|
|
||||||
go indexer.loop(chain)
|
go indexer.loop(chain)
|
||||||
|
|
||||||
var msg string
|
var msg string
|
||||||
|
|
@ -154,6 +166,7 @@ func (indexer *txIndexer) repair(head uint64) {
|
||||||
// A crash may occur between the two delete operations,
|
// A crash may occur between the two delete operations,
|
||||||
// potentially leaving dangling indexes in the database.
|
// potentially leaving dangling indexes in the database.
|
||||||
// However, this is considered acceptable.
|
// However, this is considered acceptable.
|
||||||
|
indexer.tail.Store(nil)
|
||||||
rawdb.DeleteTxIndexTail(indexer.db)
|
rawdb.DeleteTxIndexTail(indexer.db)
|
||||||
rawdb.DeleteAllTxLookupEntries(indexer.db, nil)
|
rawdb.DeleteAllTxLookupEntries(indexer.db, nil)
|
||||||
log.Warn("Purge transaction indexes", "head", head, "tail", *tail)
|
log.Warn("Purge transaction indexes", "head", head, "tail", *tail)
|
||||||
|
|
@ -174,6 +187,7 @@ func (indexer *txIndexer) repair(head uint64) {
|
||||||
// Traversing the database directly within the transaction
|
// Traversing the database directly within the transaction
|
||||||
// index namespace might be slow and expensive, but we
|
// index namespace might be slow and expensive, but we
|
||||||
// have no choice.
|
// have no choice.
|
||||||
|
indexer.tail.Store(nil)
|
||||||
rawdb.DeleteTxIndexTail(indexer.db)
|
rawdb.DeleteTxIndexTail(indexer.db)
|
||||||
rawdb.DeleteAllTxLookupEntries(indexer.db, nil)
|
rawdb.DeleteAllTxLookupEntries(indexer.db, nil)
|
||||||
log.Warn("Purge transaction indexes", "head", head, "cutoff", indexer.cutoff)
|
log.Warn("Purge transaction indexes", "head", head, "cutoff", indexer.cutoff)
|
||||||
|
|
@ -187,6 +201,7 @@ func (indexer *txIndexer) repair(head uint64) {
|
||||||
// A crash may occur between the two delete operations,
|
// A crash may occur between the two delete operations,
|
||||||
// potentially leaving dangling indexes in the database.
|
// potentially leaving dangling indexes in the database.
|
||||||
// However, this is considered acceptable.
|
// However, this is considered acceptable.
|
||||||
|
indexer.tail.Store(&indexer.cutoff)
|
||||||
rawdb.WriteTxIndexTail(indexer.db, indexer.cutoff)
|
rawdb.WriteTxIndexTail(indexer.db, indexer.cutoff)
|
||||||
rawdb.DeleteAllTxLookupEntries(indexer.db, func(txhash common.Hash, blob []byte) bool {
|
rawdb.DeleteAllTxLookupEntries(indexer.db, func(txhash common.Hash, blob []byte) bool {
|
||||||
n := rawdb.DecodeTxLookupEntry(blob, indexer.db)
|
n := rawdb.DecodeTxLookupEntry(blob, indexer.db)
|
||||||
|
|
@ -218,14 +233,13 @@ func (indexer *txIndexer) loop(chain *BlockChain) {
|
||||||
var (
|
var (
|
||||||
stop chan struct{} // Non-nil if background routine is active
|
stop chan struct{} // Non-nil if background routine is active
|
||||||
done chan struct{} // Non-nil if background routine is active
|
done chan struct{} // Non-nil if background routine is active
|
||||||
head = indexer.resolveHead() // The latest announced chain head
|
|
||||||
|
|
||||||
headCh = make(chan ChainHeadEvent)
|
headCh = make(chan ChainHeadEvent)
|
||||||
sub = chain.SubscribeChainHeadEvent(headCh)
|
sub = chain.SubscribeChainHeadEvent(headCh)
|
||||||
)
|
)
|
||||||
defer sub.Unsubscribe()
|
defer sub.Unsubscribe()
|
||||||
|
|
||||||
// Validate the transaction indexes and repair if necessary
|
// Validate the transaction indexes and repair if necessary
|
||||||
|
head := indexer.head.Load()
|
||||||
indexer.repair(head)
|
indexer.repair(head)
|
||||||
|
|
||||||
// Launch the initial processing if chain is not empty (head != genesis).
|
// Launch the initial processing if chain is not empty (head != genesis).
|
||||||
|
|
@ -238,17 +252,18 @@ func (indexer *txIndexer) loop(chain *BlockChain) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case h := <-headCh:
|
case h := <-headCh:
|
||||||
|
indexer.head.Store(h.Header.Number.Uint64())
|
||||||
if done == nil {
|
if done == nil {
|
||||||
stop = make(chan struct{})
|
stop = make(chan struct{})
|
||||||
done = make(chan struct{})
|
done = make(chan struct{})
|
||||||
go indexer.run(h.Header.Number.Uint64(), stop, done)
|
go indexer.run(h.Header.Number.Uint64(), stop, done)
|
||||||
}
|
}
|
||||||
head = h.Header.Number.Uint64()
|
|
||||||
case <-done:
|
case <-done:
|
||||||
stop = nil
|
stop = nil
|
||||||
done = nil
|
done = nil
|
||||||
case ch := <-indexer.progress:
|
indexer.tail.Store(rawdb.ReadTxIndexTail(indexer.db))
|
||||||
ch <- indexer.report(head)
|
|
||||||
case ch := <-indexer.term:
|
case ch := <-indexer.term:
|
||||||
if stop != nil {
|
if stop != nil {
|
||||||
close(stop)
|
close(stop)
|
||||||
|
|
@ -264,7 +279,7 @@ func (indexer *txIndexer) loop(chain *BlockChain) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// report returns the tx indexing progress.
|
// report returns the tx indexing progress.
|
||||||
func (indexer *txIndexer) report(head uint64) TxIndexProgress {
|
func (indexer *txIndexer) report(head uint64, tail *uint64) TxIndexProgress {
|
||||||
// Special case if the head is even below the cutoff,
|
// Special case if the head is even below the cutoff,
|
||||||
// nothing to index.
|
// nothing to index.
|
||||||
if head < indexer.cutoff {
|
if head < indexer.cutoff {
|
||||||
|
|
@ -284,7 +299,6 @@ func (indexer *txIndexer) report(head uint64) TxIndexProgress {
|
||||||
}
|
}
|
||||||
// Compute how many blocks have been indexed
|
// Compute how many blocks have been indexed
|
||||||
var indexed uint64
|
var indexed uint64
|
||||||
tail := rawdb.ReadTxIndexTail(indexer.db)
|
|
||||||
if tail != nil {
|
if tail != nil {
|
||||||
indexed = head - *tail + 1
|
indexed = head - *tail + 1
|
||||||
}
|
}
|
||||||
|
|
@ -300,16 +314,12 @@ func (indexer *txIndexer) report(head uint64) TxIndexProgress {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// txIndexProgress retrieves the tx indexing progress, or an error if the
|
// txIndexProgress retrieves the transaction indexing progress. The reported
|
||||||
// background tx indexer is already stopped.
|
// progress may slightly lag behind the actual indexing state, as the tail is
|
||||||
func (indexer *txIndexer) txIndexProgress() (TxIndexProgress, error) {
|
// only updated at the end of each indexing operation. However, this delay is
|
||||||
ch := make(chan TxIndexProgress, 1)
|
// considered acceptable.
|
||||||
select {
|
func (indexer *txIndexer) txIndexProgress() TxIndexProgress {
|
||||||
case indexer.progress <- ch:
|
return indexer.report(indexer.head.Load(), indexer.tail.Load())
|
||||||
return <-ch, nil
|
|
||||||
case <-indexer.closed:
|
|
||||||
return TxIndexProgress{}, errors.New("indexer is closed")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// close shutdown the indexer. Safe to be called for multiple times.
|
// close shutdown the indexer. Safe to be called for multiple times.
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,6 @@ func TestTxIndexer(t *testing.T) {
|
||||||
indexer := &txIndexer{
|
indexer := &txIndexer{
|
||||||
limit: 0,
|
limit: 0,
|
||||||
db: db,
|
db: db,
|
||||||
progress: make(chan chan TxIndexProgress),
|
|
||||||
}
|
}
|
||||||
for i, limit := range c.limits {
|
for i, limit := range c.limits {
|
||||||
indexer.limit = limit
|
indexer.limit = limit
|
||||||
|
|
@ -243,7 +242,6 @@ func TestTxIndexerRepair(t *testing.T) {
|
||||||
indexer := &txIndexer{
|
indexer := &txIndexer{
|
||||||
limit: c.limit,
|
limit: c.limit,
|
||||||
db: db,
|
db: db,
|
||||||
progress: make(chan chan TxIndexProgress),
|
|
||||||
}
|
}
|
||||||
indexer.run(chainHead, make(chan struct{}), make(chan struct{}))
|
indexer.run(chainHead, make(chan struct{}), make(chan struct{}))
|
||||||
|
|
||||||
|
|
@ -435,12 +433,8 @@ func TestTxIndexerReport(t *testing.T) {
|
||||||
limit: c.limit,
|
limit: c.limit,
|
||||||
cutoff: c.cutoff,
|
cutoff: c.cutoff,
|
||||||
db: db,
|
db: db,
|
||||||
progress: make(chan chan TxIndexProgress),
|
|
||||||
}
|
}
|
||||||
if c.tail != nil {
|
p := indexer.report(c.head, c.tail)
|
||||||
rawdb.WriteTxIndexTail(db, *c.tail)
|
|
||||||
}
|
|
||||||
p := indexer.report(c.head)
|
|
||||||
if p.Indexed != c.expIndexed {
|
if p.Indexed != c.expIndexed {
|
||||||
t.Fatalf("Unexpected indexed: %d, expected: %d", p.Indexed, c.expIndexed)
|
t.Fatalf("Unexpected indexed: %d, expected: %d", p.Indexed, c.expIndexed)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1494,22 +1494,22 @@ func (pool *LegacyPool) promoteExecutables(accounts []common.Address) []*types.T
|
||||||
// equal number for all for accounts with many pending transactions.
|
// equal number for all for accounts with many pending transactions.
|
||||||
func (pool *LegacyPool) truncatePending() {
|
func (pool *LegacyPool) truncatePending() {
|
||||||
pending := uint64(0)
|
pending := uint64(0)
|
||||||
for _, list := range pool.pending {
|
|
||||||
pending += uint64(list.Len())
|
// Assemble a spam order to penalize large transactors first
|
||||||
|
spammers := prque.New[uint64, common.Address](nil)
|
||||||
|
for addr, list := range pool.pending {
|
||||||
|
// Only evict transactions from high rollers
|
||||||
|
length := uint64(list.Len())
|
||||||
|
pending += length
|
||||||
|
if length > pool.config.AccountSlots {
|
||||||
|
spammers.Push(addr, length)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if pending <= pool.config.GlobalSlots {
|
if pending <= pool.config.GlobalSlots {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pendingBeforeCap := pending
|
pendingBeforeCap := pending
|
||||||
// Assemble a spam order to penalize large transactors first
|
|
||||||
spammers := prque.New[int64, common.Address](nil)
|
|
||||||
for addr, list := range pool.pending {
|
|
||||||
// Only evict transactions from high rollers
|
|
||||||
if uint64(list.Len()) > pool.config.AccountSlots {
|
|
||||||
spammers.Push(addr, int64(list.Len()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Gradually drop transactions from offenders
|
// Gradually drop transactions from offenders
|
||||||
offenders := []common.Address{}
|
offenders := []common.Address{}
|
||||||
for pending > pool.config.GlobalSlots && !spammers.Empty() {
|
for pending > pool.config.GlobalSlots && !spammers.Empty() {
|
||||||
|
|
@ -1934,7 +1934,7 @@ func (pool *LegacyPool) Clear() {
|
||||||
pool.reserver.Release(addr)
|
pool.reserver.Release(addr)
|
||||||
}
|
}
|
||||||
pool.all.Clear()
|
pool.all.Clear()
|
||||||
pool.priced = newPricedList(pool.all)
|
pool.priced.Reheap()
|
||||||
pool.pending = make(map[common.Address]*list)
|
pool.pending = make(map[common.Address]*list)
|
||||||
pool.queue = make(map[common.Address]*list)
|
pool.queue = make(map[common.Address]*list)
|
||||||
pool.pendingNonces = newNoncer(pool.currentState)
|
pool.pendingNonces = newNoncer(pool.currentState)
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,17 @@ func VerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error {
|
||||||
return gokzgVerifyBlobProof(blob, commitment, proof)
|
return gokzgVerifyBlobProof(blob, commitment, proof)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ComputeCellProofs returns the KZG cell proofs that are used to verify the blob against
|
||||||
|
// the commitment.
|
||||||
|
//
|
||||||
|
// This method does not verify that the commitment is correct with respect to blob.
|
||||||
|
func ComputeCellProofs(blob *Blob) ([]Proof, error) {
|
||||||
|
if useCKZG.Load() {
|
||||||
|
return ckzgComputeCellProofs(blob)
|
||||||
|
}
|
||||||
|
return gokzgComputeCellProofs(blob)
|
||||||
|
}
|
||||||
|
|
||||||
// CalcBlobHashV1 calculates the 'versioned blob hash' of a commitment.
|
// CalcBlobHashV1 calculates the 'versioned blob hash' of a commitment.
|
||||||
// The given hasher must be a sha256 hash instance, otherwise the result will be invalid!
|
// The given hasher must be a sha256 hash instance, otherwise the result will be invalid!
|
||||||
func CalcBlobHashV1(hasher hash.Hash, commit *Commitment) (vh [32]byte) {
|
func CalcBlobHashV1(hasher hash.Hash, commit *Commitment) (vh [32]byte) {
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
gokzg4844 "github.com/crate-crypto/go-kzg-4844"
|
gokzg4844 "github.com/crate-crypto/go-eth-kzg"
|
||||||
ckzg4844 "github.com/ethereum/c-kzg-4844/bindings/go"
|
ckzg4844 "github.com/ethereum/c-kzg-4844/v2/bindings/go"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -47,15 +47,21 @@ func ckzgInit() {
|
||||||
if err = gokzg4844.CheckTrustedSetupIsWellFormed(params); err != nil {
|
if err = gokzg4844.CheckTrustedSetupIsWellFormed(params); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
g1s := make([]byte, len(params.SetupG1Lagrange)*(len(params.SetupG1Lagrange[0])-2)/2)
|
g1Lag := make([]byte, len(params.SetupG1Lagrange)*(len(params.SetupG1Lagrange[0])-2)/2)
|
||||||
for i, g1 := range params.SetupG1Lagrange {
|
for i, g1 := range params.SetupG1Lagrange {
|
||||||
|
copy(g1Lag[i*(len(g1)-2)/2:], hexutil.MustDecode(g1))
|
||||||
|
}
|
||||||
|
g1s := make([]byte, len(params.SetupG1Monomial)*(len(params.SetupG1Monomial[0])-2)/2)
|
||||||
|
for i, g1 := range params.SetupG1Monomial {
|
||||||
copy(g1s[i*(len(g1)-2)/2:], hexutil.MustDecode(g1))
|
copy(g1s[i*(len(g1)-2)/2:], hexutil.MustDecode(g1))
|
||||||
}
|
}
|
||||||
g2s := make([]byte, len(params.SetupG2)*(len(params.SetupG2[0])-2)/2)
|
g2s := make([]byte, len(params.SetupG2)*(len(params.SetupG2[0])-2)/2)
|
||||||
for i, g2 := range params.SetupG2 {
|
for i, g2 := range params.SetupG2 {
|
||||||
copy(g2s[i*(len(g2)-2)/2:], hexutil.MustDecode(g2))
|
copy(g2s[i*(len(g2)-2)/2:], hexutil.MustDecode(g2))
|
||||||
}
|
}
|
||||||
if err = ckzg4844.LoadTrustedSetup(g1s, g2s); err != nil {
|
// The last parameter determines the multiplication table, see https://notes.ethereum.org/@jtraglia/windowed_multiplications
|
||||||
|
// I think 6 is an decent compromise between size and speed
|
||||||
|
if err = ckzg4844.LoadTrustedSetup(g1s, g1Lag, g2s, 6); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -125,3 +131,21 @@ func ckzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ckzgComputeCellProofs returns the KZG cell proofs that are used to verify the blob against
|
||||||
|
// the commitment.
|
||||||
|
//
|
||||||
|
// This method does not verify that the commitment is correct with respect to blob.
|
||||||
|
func ckzgComputeCellProofs(blob *Blob) ([]Proof, error) {
|
||||||
|
ckzgIniter.Do(ckzgInit)
|
||||||
|
|
||||||
|
_, proofs, err := ckzg4844.ComputeCellsAndKZGProofs((*ckzg4844.Blob)(blob))
|
||||||
|
if err != nil {
|
||||||
|
return []Proof{}, err
|
||||||
|
}
|
||||||
|
var p []Proof
|
||||||
|
for _, proof := range proofs {
|
||||||
|
p = append(p, (Proof)(proof))
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,3 +60,11 @@ func ckzgComputeBlobProof(blob *Blob, commitment Commitment) (Proof, error) {
|
||||||
func ckzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error {
|
func ckzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error {
|
||||||
panic("unsupported platform")
|
panic("unsupported platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ckzgComputeCellProofs returns the KZG cell proofs that are used to verify the blob against
|
||||||
|
// the commitment.
|
||||||
|
//
|
||||||
|
// This method does not verify that the commitment is correct with respect to blob.
|
||||||
|
func ckzgComputeCellProofs(blob *Blob) ([]Proof, error) {
|
||||||
|
panic("unsupported platform")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
gokzg4844 "github.com/crate-crypto/go-kzg-4844"
|
gokzg4844 "github.com/crate-crypto/go-eth-kzg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// context is the crypto primitive pre-seeded with the trusted setup parameters.
|
// context is the crypto primitive pre-seeded with the trusted setup parameters.
|
||||||
|
|
@ -96,3 +96,21 @@ func gokzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error
|
||||||
|
|
||||||
return context.VerifyBlobKZGProof((*gokzg4844.Blob)(blob), (gokzg4844.KZGCommitment)(commitment), (gokzg4844.KZGProof)(proof))
|
return context.VerifyBlobKZGProof((*gokzg4844.Blob)(blob), (gokzg4844.KZGCommitment)(commitment), (gokzg4844.KZGProof)(proof))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// gokzgComputeCellProofs returns the KZG cell proofs that are used to verify the blob against
|
||||||
|
// the commitment.
|
||||||
|
//
|
||||||
|
// This method does not verify that the commitment is correct with respect to blob.
|
||||||
|
func gokzgComputeCellProofs(blob *Blob) ([]Proof, error) {
|
||||||
|
gokzgIniter.Do(gokzgInit)
|
||||||
|
|
||||||
|
_, proofs, err := context.ComputeCellsAndKZGProofs((*gokzg4844.Blob)(blob), 0)
|
||||||
|
if err != nil {
|
||||||
|
return []Proof{}, err
|
||||||
|
}
|
||||||
|
var p []Proof
|
||||||
|
for _, proof := range proofs {
|
||||||
|
p = append(p, (Proof)(proof))
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -349,22 +349,20 @@ func (b *EthAPIBackend) GetPoolTransaction(hash common.Hash) *types.Transaction
|
||||||
// GetTransaction retrieves the lookup along with the transaction itself associate
|
// GetTransaction retrieves the lookup along with the transaction itself associate
|
||||||
// with the given transaction hash.
|
// with the given transaction hash.
|
||||||
//
|
//
|
||||||
// An error will be returned if the transaction is not found, and background
|
// A null will be returned if the transaction is not found. The transaction is not
|
||||||
// indexing for transactions is still in progress. The error is used to indicate the
|
// existent from the node's perspective. This can be due to the transaction indexer
|
||||||
// scenario explicitly that the transaction might be reachable shortly.
|
// not being finished. The caller must explicitly check the indexer progress.
|
||||||
//
|
func (b *EthAPIBackend) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) {
|
||||||
// A null will be returned in the transaction is not found and background transaction
|
lookup, tx := b.eth.blockchain.GetTransactionLookup(txHash)
|
||||||
// indexing is already finished. The transaction is not existent from the perspective
|
|
||||||
// of node.
|
|
||||||
func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) {
|
|
||||||
lookup, tx, err := b.eth.blockchain.GetTransactionLookup(txHash)
|
|
||||||
if err != nil {
|
|
||||||
return false, nil, common.Hash{}, 0, 0, err
|
|
||||||
}
|
|
||||||
if lookup == nil || tx == nil {
|
if lookup == nil || tx == nil {
|
||||||
return false, nil, common.Hash{}, 0, 0, nil
|
return false, nil, common.Hash{}, 0, 0
|
||||||
}
|
}
|
||||||
return true, tx, lookup.BlockHash, lookup.BlockIndex, lookup.Index, nil
|
return true, tx, lookup.BlockHash, lookup.BlockIndex, lookup.Index
|
||||||
|
}
|
||||||
|
|
||||||
|
// TxIndexDone returns true if the transaction indexer has finished indexing.
|
||||||
|
func (b *EthAPIBackend) TxIndexDone() bool {
|
||||||
|
return b.eth.blockchain.TxIndexDone()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
||||||
|
|
@ -391,7 +389,7 @@ func (b *EthAPIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.S
|
||||||
return b.eth.txPool.SubscribeTransactions(ch, true)
|
return b.eth.txPool.SubscribeTransactions(ch, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthAPIBackend) SyncProgress() ethereum.SyncProgress {
|
func (b *EthAPIBackend) SyncProgress(ctx context.Context) ethereum.SyncProgress {
|
||||||
prog := b.eth.Downloader().Progress()
|
prog := b.eth.Downloader().Progress()
|
||||||
if txProg, err := b.eth.blockchain.TxIndexProgress(); err == nil {
|
if txProg, err := b.eth.blockchain.TxIndexProgress(); err == nil {
|
||||||
prog.TxIndexFinishedBlocks = txProg.Indexed
|
prog.TxIndexFinishedBlocks = txProg.Indexed
|
||||||
|
|
|
||||||
|
|
@ -373,7 +373,10 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
||||||
}
|
}
|
||||||
valid := func(id *engine.PayloadID) engine.ForkChoiceResponse {
|
valid := func(id *engine.PayloadID) engine.ForkChoiceResponse {
|
||||||
return engine.ForkChoiceResponse{
|
return engine.ForkChoiceResponse{
|
||||||
PayloadStatus: engine.PayloadStatusV1{Status: engine.VALID, LatestValidHash: &update.HeadBlockHash},
|
PayloadStatus: engine.PayloadStatusV1{
|
||||||
|
Status: engine.VALID,
|
||||||
|
LatestValidHash: &update.HeadBlockHash,
|
||||||
|
},
|
||||||
PayloadID: id,
|
PayloadID: id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,9 @@ const (
|
||||||
// txGatherSlack is the interval used to collate almost-expired announces
|
// txGatherSlack is the interval used to collate almost-expired announces
|
||||||
// with network fetches.
|
// with network fetches.
|
||||||
txGatherSlack = 100 * time.Millisecond
|
txGatherSlack = 100 * time.Millisecond
|
||||||
|
|
||||||
|
// addTxsBatchSize it the max number of transactions to add in a single batch from a peer.
|
||||||
|
addTxsBatchSize = 128
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -329,8 +332,8 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool)
|
||||||
metas = make([]txMetadata, 0, len(txs))
|
metas = make([]txMetadata, 0, len(txs))
|
||||||
)
|
)
|
||||||
// proceed in batches
|
// proceed in batches
|
||||||
for i := 0; i < len(txs); i += 128 {
|
for i := 0; i < len(txs); i += addTxsBatchSize {
|
||||||
end := i + 128
|
end := i + addTxsBatchSize
|
||||||
if end > len(txs) {
|
if end > len(txs) {
|
||||||
end = len(txs)
|
end = len(txs)
|
||||||
}
|
}
|
||||||
|
|
@ -372,7 +375,7 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool)
|
||||||
otherRejectMeter.Mark(otherreject)
|
otherRejectMeter.Mark(otherreject)
|
||||||
|
|
||||||
// If 'other reject' is >25% of the deliveries in any batch, sleep a bit.
|
// If 'other reject' is >25% of the deliveries in any batch, sleep a bit.
|
||||||
if otherreject > 128/4 {
|
if otherreject > addTxsBatchSize/4 {
|
||||||
time.Sleep(200 * time.Millisecond)
|
time.Sleep(200 * time.Millisecond)
|
||||||
log.Debug("Peer delivering stale transactions", "peer", peer, "rejected", otherreject)
|
log.Debug("Peer delivering stale transactions", "peer", peer, "rejected", otherreject)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,13 @@ func (b *testBackend) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *testBackend) GetRawReceiptsByHash(hash common.Hash) types.Receipts {
|
||||||
|
if number := rawdb.ReadHeaderNumber(b.db, hash); number != nil {
|
||||||
|
return rawdb.ReadRawReceipts(b.db, hash, *number)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) {
|
func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) {
|
||||||
var (
|
var (
|
||||||
hash common.Hash
|
hash common.Hash
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin
|
||||||
// There's a fairly high chance for the transaction to execute successfully
|
// There's a fairly high chance for the transaction to execute successfully
|
||||||
// with gasLimit set to the first execution's usedGas + gasRefund. Explicitly
|
// with gasLimit set to the first execution's usedGas + gasRefund. Explicitly
|
||||||
// check that gas amount and use as a limit for the binary search.
|
// check that gas amount and use as a limit for the binary search.
|
||||||
optimisticGasLimit := (result.UsedGas + result.RefundedGas + params.CallStipend) * 64 / 63
|
optimisticGasLimit := (result.MaxUsedGas + params.CallStipend) * 64 / 63
|
||||||
if optimisticGasLimit < hi {
|
if optimisticGasLimit < hi {
|
||||||
failed, _, err = execute(ctx, call, opts, optimisticGasLimit)
|
failed, _, err = execute(ctx, call, opts, optimisticGasLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,8 @@ type Backend interface {
|
||||||
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
||||||
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
|
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
|
||||||
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
|
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
|
||||||
GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error)
|
GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64)
|
||||||
|
TxIndexDone() bool
|
||||||
RPCGasCap() uint64
|
RPCGasCap() uint64
|
||||||
ChainConfig() *params.ChainConfig
|
ChainConfig() *params.ChainConfig
|
||||||
Engine() consensus.Engine
|
Engine() consensus.Engine
|
||||||
|
|
@ -777,6 +778,7 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
|
||||||
// Note: This copies the config, to not screw up the main config
|
// Note: This copies the config, to not screw up the main config
|
||||||
chainConfig, canon = overrideConfig(chainConfig, config.Overrides)
|
chainConfig, canon = overrideConfig(chainConfig, config.Overrides)
|
||||||
}
|
}
|
||||||
|
|
||||||
evm := vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{})
|
evm := vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{})
|
||||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||||
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
|
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
|
||||||
|
|
@ -786,42 +788,45 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
|
||||||
}
|
}
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
// Prepare the transaction for un-traced execution
|
// Prepare the transaction for un-traced execution
|
||||||
var (
|
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
|
||||||
msg, _ = core.TransactionToMessage(tx, signer, block.BaseFee())
|
if txHash != (common.Hash{}) && tx.Hash() != txHash {
|
||||||
vmConf vm.Config
|
// Process the tx to update state, but don't trace it.
|
||||||
dump *os.File
|
_, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit))
|
||||||
writer *bufio.Writer
|
if err != nil {
|
||||||
err error
|
return dumps, err
|
||||||
)
|
}
|
||||||
// If the transaction needs tracing, swap out the configs
|
// Finalize the state so any modifications are written to the trie
|
||||||
if tx.Hash() == txHash || txHash == (common.Hash{}) {
|
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
||||||
// Generate a unique temporary file to dump it into
|
statedb.Finalise(evm.ChainConfig().IsEIP158(block.Number()))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// The transaction should be traced.
|
||||||
|
// Generate a unique temporary file to dump it into.
|
||||||
prefix := fmt.Sprintf("block_%#x-%d-%#x-", block.Hash().Bytes()[:4], i, tx.Hash().Bytes()[:4])
|
prefix := fmt.Sprintf("block_%#x-%d-%#x-", block.Hash().Bytes()[:4], i, tx.Hash().Bytes()[:4])
|
||||||
if !canon {
|
if !canon {
|
||||||
prefix = fmt.Sprintf("%valt-", prefix)
|
prefix = fmt.Sprintf("%valt-", prefix)
|
||||||
}
|
}
|
||||||
dump, err = os.CreateTemp(os.TempDir(), prefix)
|
var dump *os.File
|
||||||
|
dump, err := os.CreateTemp(os.TempDir(), prefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
dumps = append(dumps, dump.Name())
|
dumps = append(dumps, dump.Name())
|
||||||
|
// Set up the tracer and EVM for the transaction.
|
||||||
// Swap out the noop logger to the standard tracer
|
var (
|
||||||
writer = bufio.NewWriter(dump)
|
writer = bufio.NewWriter(dump)
|
||||||
vmConf = vm.Config{
|
tracer = logger.NewJSONLogger(&logConfig, writer)
|
||||||
Tracer: logger.NewJSONLogger(&logConfig, writer),
|
evm = vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{
|
||||||
EnablePreimageRecording: true,
|
Tracer: tracer,
|
||||||
}
|
NoBaseFee: true,
|
||||||
}
|
})
|
||||||
|
)
|
||||||
// Execute the transaction and flush any traces to disk
|
// Execute the transaction and flush any traces to disk
|
||||||
statedb.SetTxContext(tx.Hash(), i)
|
statedb.SetTxContext(tx.Hash(), i)
|
||||||
if vmConf.Tracer.OnTxStart != nil {
|
if tracer.OnTxStart != nil {
|
||||||
vmConf.Tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||||
}
|
|
||||||
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit))
|
|
||||||
if vmConf.Tracer.OnTxEnd != nil {
|
|
||||||
vmConf.Tracer.OnTxEnd(&types.Receipt{GasUsed: vmRet.UsedGas}, err)
|
|
||||||
}
|
}
|
||||||
|
_, err = core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit))
|
||||||
if writer != nil {
|
if writer != nil {
|
||||||
writer.Flush()
|
writer.Flush()
|
||||||
}
|
}
|
||||||
|
|
@ -858,12 +863,13 @@ func containsTx(block *types.Block, hash common.Hash) bool {
|
||||||
// TraceTransaction returns the structured logs created during the execution of EVM
|
// TraceTransaction returns the structured logs created during the execution of EVM
|
||||||
// and returns them as a JSON object.
|
// and returns them as a JSON object.
|
||||||
func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
|
func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
|
||||||
found, _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash)
|
found, _, blockHash, blockNumber, index := api.backend.GetTransaction(hash)
|
||||||
if err != nil {
|
if !found {
|
||||||
|
// Warn in case tx indexer is not done.
|
||||||
|
if !api.backend.TxIndexDone() {
|
||||||
return nil, ethapi.NewTxIndexingError()
|
return nil, ethapi.NewTxIndexingError()
|
||||||
}
|
}
|
||||||
// Only mined txes are supported
|
// Only mined txes are supported
|
||||||
if !found {
|
|
||||||
return nil, errTxNotFound
|
return nil, errTxNotFound
|
||||||
}
|
}
|
||||||
// It shouldn't happen in practice.
|
// It shouldn't happen in practice.
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"slices"
|
"slices"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
@ -116,9 +117,13 @@ func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber)
|
||||||
return b.chain.GetBlockByNumber(uint64(number)), nil
|
return b.chain.GetBlockByNumber(uint64(number)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) {
|
func (b *testBackend) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) {
|
||||||
tx, hash, blockNumber, index := rawdb.ReadTransaction(b.chaindb, txHash)
|
tx, hash, blockNumber, index := rawdb.ReadTransaction(b.chaindb, txHash)
|
||||||
return tx != nil, tx, hash, blockNumber, index, nil
|
return tx != nil, tx, hash, blockNumber, index
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *testBackend) TxIndexDone() bool {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *testBackend) RPCGasCap() uint64 {
|
func (b *testBackend) RPCGasCap() uint64 {
|
||||||
|
|
@ -1214,3 +1219,118 @@ func TestTraceBlockWithBasefee(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStandardTraceBlockToFile(t *testing.T) {
|
||||||
|
var (
|
||||||
|
// A sender who makes transactions, has some funds
|
||||||
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
funds = big.NewInt(1000000000000000)
|
||||||
|
|
||||||
|
// first contract the sender transacts with
|
||||||
|
aa = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f43")
|
||||||
|
aaCode = []byte{byte(vm.PUSH1), 0x00, byte(vm.POP)}
|
||||||
|
|
||||||
|
// second contract the sender transacts with
|
||||||
|
bb = common.HexToAddress("0x7217d81b76bdd8707601e959454e3d776aee5f44")
|
||||||
|
bbCode = []byte{byte(vm.PUSH2), 0x00, 0x01, byte(vm.POP)}
|
||||||
|
)
|
||||||
|
|
||||||
|
genesis := &core.Genesis{
|
||||||
|
Config: params.TestChainConfig,
|
||||||
|
Alloc: types.GenesisAlloc{
|
||||||
|
address: {Balance: funds},
|
||||||
|
aa: {
|
||||||
|
Code: aaCode,
|
||||||
|
Nonce: 1,
|
||||||
|
Balance: big.NewInt(0),
|
||||||
|
},
|
||||||
|
bb: {
|
||||||
|
Code: bbCode,
|
||||||
|
Nonce: 1,
|
||||||
|
Balance: big.NewInt(0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
txHashs := make([]common.Hash, 0, 2)
|
||||||
|
backend := newTestBackend(t, 1, genesis, func(i int, b *core.BlockGen) {
|
||||||
|
b.SetCoinbase(common.Address{1})
|
||||||
|
// first tx to aa
|
||||||
|
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{
|
||||||
|
Nonce: 0,
|
||||||
|
To: &aa,
|
||||||
|
Value: big.NewInt(0),
|
||||||
|
Gas: 50000,
|
||||||
|
GasPrice: b.BaseFee(),
|
||||||
|
Data: nil,
|
||||||
|
}), types.HomesteadSigner{}, key)
|
||||||
|
b.AddTx(tx)
|
||||||
|
txHashs = append(txHashs, tx.Hash())
|
||||||
|
// second tx to bb
|
||||||
|
tx, _ = types.SignTx(types.NewTx(&types.LegacyTx{
|
||||||
|
Nonce: 1,
|
||||||
|
To: &bb,
|
||||||
|
Value: big.NewInt(1),
|
||||||
|
Gas: 100000,
|
||||||
|
GasPrice: b.BaseFee(),
|
||||||
|
Data: nil,
|
||||||
|
}), types.HomesteadSigner{}, key)
|
||||||
|
b.AddTx(tx)
|
||||||
|
txHashs = append(txHashs, tx.Hash())
|
||||||
|
})
|
||||||
|
defer backend.chain.Stop()
|
||||||
|
|
||||||
|
var testSuite = []struct {
|
||||||
|
blockNumber rpc.BlockNumber
|
||||||
|
config *StdTraceConfig
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
// test that all traces in the block were outputted if no trace config is specified
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
config: nil,
|
||||||
|
want: []string{
|
||||||
|
`{"pc":0,"op":96,"gas":"0x7148","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"}
|
||||||
|
{"pc":2,"op":80,"gas":"0x7145","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"POP"}
|
||||||
|
{"pc":3,"op":0,"gas":"0x7143","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP"}
|
||||||
|
{"output":"","gasUsed":"0x5"}
|
||||||
|
`,
|
||||||
|
`{"pc":0,"op":97,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH2"}
|
||||||
|
{"pc":3,"op":80,"gas":"0x13495","gasCost":"0x2","memSize":0,"stack":["0x1"],"depth":1,"refund":0,"opName":"POP"}
|
||||||
|
{"pc":4,"op":0,"gas":"0x13493","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP"}
|
||||||
|
{"output":"","gasUsed":"0x5"}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// test that only a specific tx is traced if specified
|
||||||
|
blockNumber: rpc.LatestBlockNumber,
|
||||||
|
config: &StdTraceConfig{TxHash: txHashs[1]},
|
||||||
|
want: []string{
|
||||||
|
`{"pc":0,"op":97,"gas":"0x13498","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH2"}
|
||||||
|
{"pc":3,"op":80,"gas":"0x13495","gasCost":"0x2","memSize":0,"stack":["0x1"],"depth":1,"refund":0,"opName":"POP"}
|
||||||
|
{"pc":4,"op":0,"gas":"0x13493","gasCost":"0x0","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP"}
|
||||||
|
{"output":"","gasUsed":"0x5"}
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
api := NewAPI(backend)
|
||||||
|
for i, tc := range testSuite {
|
||||||
|
block, _ := api.blockByNumber(context.Background(), tc.blockNumber)
|
||||||
|
txTraces, err := api.StandardTraceBlockToFile(context.Background(), block.Hash(), tc.config)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("test index %d received error %v", i, err)
|
||||||
|
}
|
||||||
|
for j, traceFileName := range txTraces {
|
||||||
|
traceReceived, err := os.ReadFile(traceFileName)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not read trace file: %v", err)
|
||||||
|
}
|
||||||
|
if tc.want[j] != string(traceReceived) {
|
||||||
|
t.Fatalf("unexpected trace result. expected\n'%s'\n\nreceived\n'%s'\n", tc.want[j], string(traceReceived))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ func (ec *Client) BlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumb
|
||||||
}
|
}
|
||||||
|
|
||||||
type rpcBlock struct {
|
type rpcBlock struct {
|
||||||
Hash common.Hash `json:"hash"`
|
Hash *common.Hash `json:"hash"`
|
||||||
Transactions []rpcTransaction `json:"transactions"`
|
Transactions []rpcTransaction `json:"transactions"`
|
||||||
UncleHashes []common.Hash `json:"uncles"`
|
UncleHashes []common.Hash `json:"uncles"`
|
||||||
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
Withdrawals []*types.Withdrawal `json:"withdrawals,omitempty"`
|
||||||
|
|
@ -158,6 +158,12 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface
|
||||||
if err := json.Unmarshal(raw, &body); err != nil {
|
if err := json.Unmarshal(raw, &body); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
// Pending blocks don't return a block hash, compute it for sender caching.
|
||||||
|
if body.Hash == nil {
|
||||||
|
tmp := head.Hash()
|
||||||
|
body.Hash = &tmp
|
||||||
|
}
|
||||||
|
|
||||||
// Quick-verify transaction and uncle lists. This mostly helps with debugging the server.
|
// Quick-verify transaction and uncle lists. This mostly helps with debugging the server.
|
||||||
if head.UncleHash == types.EmptyUncleHash && len(body.UncleHashes) > 0 {
|
if head.UncleHash == types.EmptyUncleHash && len(body.UncleHashes) > 0 {
|
||||||
return nil, errors.New("server returned non-empty uncle list but block header indicates no uncles")
|
return nil, errors.New("server returned non-empty uncle list but block header indicates no uncles")
|
||||||
|
|
@ -199,7 +205,7 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface
|
||||||
txs := make([]*types.Transaction, len(body.Transactions))
|
txs := make([]*types.Transaction, len(body.Transactions))
|
||||||
for i, tx := range body.Transactions {
|
for i, tx := range body.Transactions {
|
||||||
if tx.From != nil {
|
if tx.From != nil {
|
||||||
setSenderFromServer(tx.tx, *tx.From, body.Hash)
|
setSenderFromServer(tx.tx, *tx.From, *body.Hash)
|
||||||
}
|
}
|
||||||
txs[i] = tx.tx
|
txs[i] = tx.tx
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -307,6 +307,12 @@ func testTransactionInBlock(t *testing.T, client *rpc.Client) {
|
||||||
if tx.Hash() != testTx2.Hash() {
|
if tx.Hash() != testTx2.Hash() {
|
||||||
t.Fatalf("unexpected transaction: %v", tx)
|
t.Fatalf("unexpected transaction: %v", tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test pending block
|
||||||
|
_, err = ec.BlockByNumber(context.Background(), big.NewInt(int64(rpc.PendingBlockNumber)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testChainID(t *testing.T, client *rpc.Client) {
|
func testChainID(t *testing.T, client *rpc.Client) {
|
||||||
|
|
@ -619,6 +625,21 @@ func testAtFunctions(t *testing.T, client *rpc.Client) {
|
||||||
if gas != 21000 {
|
if gas != 21000 {
|
||||||
t.Fatalf("unexpected gas limit: %v", gas)
|
t.Fatalf("unexpected gas limit: %v", gas)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify that sender address of pending transaction is saved in cache.
|
||||||
|
pendingBlock, err := ec.BlockByNumber(context.Background(), big.NewInt(int64(rpc.PendingBlockNumber)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
// No additional RPC should be required, ensure the server is not asked by
|
||||||
|
// canceling the context.
|
||||||
|
sender, err := ec.TransactionSender(newCanceledContext(), pendingBlock.Transactions()[0], pendingBlock.Hash(), 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("unable to recover sender:", err)
|
||||||
|
}
|
||||||
|
if sender != testAddr {
|
||||||
|
t.Fatal("wrong sender:", sender)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTransactionSender(t *testing.T, client *rpc.Client) {
|
func testTransactionSender(t *testing.T, client *rpc.Client) {
|
||||||
|
|
@ -640,10 +661,7 @@ func testTransactionSender(t *testing.T, client *rpc.Client) {
|
||||||
|
|
||||||
// The sender address is cached in tx1, so no additional RPC should be required in
|
// The sender address is cached in tx1, so no additional RPC should be required in
|
||||||
// TransactionSender. Ensure the server is not asked by canceling the context here.
|
// TransactionSender. Ensure the server is not asked by canceling the context here.
|
||||||
canceledCtx, cancel := context.WithCancel(context.Background())
|
sender1, err := ec.TransactionSender(newCanceledContext(), tx1, block2.Hash(), 0)
|
||||||
cancel()
|
|
||||||
<-canceledCtx.Done() // Ensure the close of the Done channel
|
|
||||||
sender1, err := ec.TransactionSender(canceledCtx, tx1, block2.Hash(), 0)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -662,6 +680,13 @@ func testTransactionSender(t *testing.T, client *rpc.Client) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newCanceledContext() context.Context {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
<-ctx.Done() // Ensure the close of the Done channel
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
func sendTransaction(ec *ethclient.Client) error {
|
func sendTransaction(ec *ethclient.Client) error {
|
||||||
chainID, err := ec.ChainID(context.Background())
|
chainID, err := ec.ChainID(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ type backend interface {
|
||||||
CurrentHeader() *types.Header
|
CurrentHeader() *types.Header
|
||||||
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
||||||
Stats() (pending int, queued int)
|
Stats() (pending int, queued int)
|
||||||
SyncProgress() ethereum.SyncProgress
|
SyncProgress(ctx context.Context) ethereum.SyncProgress
|
||||||
}
|
}
|
||||||
|
|
||||||
// fullNodeBackend encompasses the functionality necessary for a full node
|
// fullNodeBackend encompasses the functionality necessary for a full node
|
||||||
|
|
@ -766,7 +766,7 @@ func (s *Service) reportStats(conn *connWrapper) error {
|
||||||
)
|
)
|
||||||
// check if backend is a full node
|
// check if backend is a full node
|
||||||
if fullBackend, ok := s.backend.(fullNodeBackend); ok {
|
if fullBackend, ok := s.backend.(fullNodeBackend); ok {
|
||||||
sync := fullBackend.SyncProgress()
|
sync := fullBackend.SyncProgress(context.Background())
|
||||||
syncing = !sync.Done()
|
syncing = !sync.Done()
|
||||||
|
|
||||||
price, _ := fullBackend.SuggestGasTipCap(context.Background())
|
price, _ := fullBackend.SuggestGasTipCap(context.Background())
|
||||||
|
|
@ -775,7 +775,7 @@ func (s *Service) reportStats(conn *connWrapper) error {
|
||||||
gasprice += int(basefee.Uint64())
|
gasprice += int(basefee.Uint64())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sync := s.backend.SyncProgress()
|
sync := s.backend.SyncProgress(context.Background())
|
||||||
syncing = !sync.Done()
|
syncing = !sync.Done()
|
||||||
}
|
}
|
||||||
// Assemble the node stats and send it to the server
|
// Assemble the node stats and send it to the server
|
||||||
|
|
|
||||||
9
go.mod
9
go.mod
|
|
@ -13,7 +13,8 @@ require (
|
||||||
github.com/cespare/cp v0.1.0
|
github.com/cespare/cp v0.1.0
|
||||||
github.com/cloudflare/cloudflare-go v0.114.0
|
github.com/cloudflare/cloudflare-go v0.114.0
|
||||||
github.com/cockroachdb/pebble v1.1.2
|
github.com/cockroachdb/pebble v1.1.2
|
||||||
github.com/consensys/gnark-crypto v0.14.0
|
github.com/consensys/gnark-crypto v0.16.0
|
||||||
|
github.com/crate-crypto/go-eth-kzg v1.3.0
|
||||||
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a
|
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a
|
||||||
github.com/crate-crypto/go-kzg-4844 v1.1.0
|
github.com/crate-crypto/go-kzg-4844 v1.1.0
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
|
|
@ -21,7 +22,7 @@ require (
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1
|
||||||
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0
|
github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0
|
||||||
github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3
|
github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3
|
||||||
github.com/ethereum/c-kzg-4844 v1.0.0
|
github.com/ethereum/c-kzg-4844/v2 v2.1.0
|
||||||
github.com/ethereum/go-verkle v0.2.2
|
github.com/ethereum/go-verkle v0.2.2
|
||||||
github.com/fatih/color v1.16.0
|
github.com/fatih/color v1.16.0
|
||||||
github.com/ferranbt/fastssz v0.1.2
|
github.com/ferranbt/fastssz v0.1.2
|
||||||
|
|
@ -92,14 +93,14 @@ require (
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 // indirect
|
github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 // indirect
|
||||||
github.com/aws/smithy-go v1.15.0 // indirect
|
github.com/aws/smithy-go v1.15.0 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bits-and-blooms/bitset v1.17.0 // indirect
|
github.com/bits-and-blooms/bitset v1.20.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/cockroachdb/errors v1.11.3 // indirect
|
github.com/cockroachdb/errors v1.11.3 // indirect
|
||||||
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect
|
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect
|
||||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
||||||
github.com/cockroachdb/redact v1.1.5 // indirect
|
github.com/cockroachdb/redact v1.1.5 // indirect
|
||||||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
|
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
|
||||||
github.com/consensys/bavard v0.1.22 // indirect
|
github.com/consensys/bavard v0.1.27 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||||
github.com/deepmap/oapi-codegen v1.6.0 // indirect
|
github.com/deepmap/oapi-codegen v1.6.0 // indirect
|
||||||
github.com/dlclark/regexp2 v1.7.0 // indirect
|
github.com/dlclark/regexp2 v1.7.0 // indirect
|
||||||
|
|
|
||||||
18
go.sum
18
go.sum
|
|
@ -90,8 +90,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bits-and-blooms/bitset v1.17.0 h1:1X2TS7aHz1ELcC0yU1y2stUs/0ig5oMU6STFZGrhvHI=
|
github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU=
|
||||||
github.com/bits-and-blooms/bitset v1.17.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
github.com/bits-and-blooms/bitset v1.20.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
|
github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
|
||||||
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
||||||
|
|
@ -124,12 +124,14 @@ github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwP
|
||||||
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
|
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
|
||||||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
|
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
|
||||||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
|
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
|
||||||
github.com/consensys/bavard v0.1.22 h1:Uw2CGvbXSZWhqK59X0VG/zOjpTFuOMcPLStrp1ihI0A=
|
github.com/consensys/bavard v0.1.27 h1:j6hKUrGAy/H+gpNrpLU3I26n1yc+VMGmd6ID5+gAhOs=
|
||||||
github.com/consensys/bavard v0.1.22/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs=
|
github.com/consensys/bavard v0.1.27/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs=
|
||||||
github.com/consensys/gnark-crypto v0.14.0 h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E=
|
github.com/consensys/gnark-crypto v0.16.0 h1:8Dl4eYmUWK9WmlP1Bj6je688gBRJCJbT8Mw4KoTAawo=
|
||||||
github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCeWHHrb1li/n1XoU0=
|
github.com/consensys/gnark-crypto v0.16.0/go.mod h1:Ke3j06ndtPTVvo++PhGNgvm+lgpLvzbcE2MqljY7diU=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/crate-crypto/go-eth-kzg v1.3.0 h1:05GrhASN9kDAidaFJOda6A4BEvgvuXbazXg/0E3OOdI=
|
||||||
|
github.com/crate-crypto/go-eth-kzg v1.3.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
|
||||||
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg=
|
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg=
|
||||||
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM=
|
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM=
|
||||||
github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4=
|
github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4=
|
||||||
|
|
@ -164,8 +166,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA=
|
github.com/ethereum/c-kzg-4844/v2 v2.1.0 h1:gQropX9YFBhl3g4HYhwE70zq3IHFRgbbNPw0Shwzf5w=
|
||||||
github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
|
github.com/ethereum/c-kzg-4844/v2 v2.1.0/go.mod h1:TC48kOKjJKPbN7C++qIgt0TJzZ70QznYR7Ob+WXl57E=
|
||||||
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
|
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
|
||||||
github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
|
github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
|
||||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||||
|
|
|
||||||
|
|
@ -229,7 +229,7 @@ func (t *Transaction) resolve(ctx context.Context) (*types.Transaction, *Block)
|
||||||
return t.tx, t.block
|
return t.tx, t.block
|
||||||
}
|
}
|
||||||
// Try to return an already finalized transaction
|
// Try to return an already finalized transaction
|
||||||
found, tx, blockHash, _, index, _ := t.r.backend.GetTransaction(ctx, t.hash)
|
found, tx, blockHash, _, index := t.r.backend.GetTransaction(t.hash)
|
||||||
if found {
|
if found {
|
||||||
t.tx = tx
|
t.tx = tx
|
||||||
blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHash, false)
|
blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHash, false)
|
||||||
|
|
@ -1530,8 +1530,8 @@ func (s *SyncState) TxIndexRemainingBlocks() hexutil.Uint64 {
|
||||||
// - healingBytecode: number of bytecodes pending
|
// - healingBytecode: number of bytecodes pending
|
||||||
// - txIndexFinishedBlocks: number of blocks whose transactions are indexed
|
// - txIndexFinishedBlocks: number of blocks whose transactions are indexed
|
||||||
// - txIndexRemainingBlocks: number of blocks whose transactions are not indexed yet
|
// - txIndexRemainingBlocks: number of blocks whose transactions are not indexed yet
|
||||||
func (r *Resolver) Syncing() (*SyncState, error) {
|
func (r *Resolver) Syncing(ctx context.Context) (*SyncState, error) {
|
||||||
progress := r.backend.SyncProgress()
|
progress := r.backend.SyncProgress(ctx)
|
||||||
|
|
||||||
// Return not syncing if the synchronisation already completed
|
// Return not syncing if the synchronisation already completed
|
||||||
if progress.Done() {
|
if progress.Done() {
|
||||||
|
|
|
||||||
|
|
@ -144,8 +144,8 @@ func (api *EthereumAPI) BlobBaseFee(ctx context.Context) *hexutil.Big {
|
||||||
// - highestBlock: block number of the highest block header this node has received from peers
|
// - highestBlock: block number of the highest block header this node has received from peers
|
||||||
// - pulledStates: number of state entries processed until now
|
// - pulledStates: number of state entries processed until now
|
||||||
// - knownStates: number of known state entries that still need to be pulled
|
// - knownStates: number of known state entries that still need to be pulled
|
||||||
func (api *EthereumAPI) Syncing() (interface{}, error) {
|
func (api *EthereumAPI) Syncing(ctx context.Context) (interface{}, error) {
|
||||||
progress := api.b.SyncProgress()
|
progress := api.b.SyncProgress(ctx)
|
||||||
|
|
||||||
// Return not syncing if the synchronisation already completed
|
// Return not syncing if the synchronisation already completed
|
||||||
if progress.Done() {
|
if progress.Done() {
|
||||||
|
|
@ -1333,17 +1333,19 @@ func (api *TransactionAPI) GetTransactionCount(ctx context.Context, address comm
|
||||||
// GetTransactionByHash returns the transaction for the given hash
|
// GetTransactionByHash returns the transaction for the given hash
|
||||||
func (api *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) {
|
func (api *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) {
|
||||||
// Try to return an already finalized transaction
|
// Try to return an already finalized transaction
|
||||||
found, tx, blockHash, blockNumber, index, err := api.b.GetTransaction(ctx, hash)
|
found, tx, blockHash, blockNumber, index := api.b.GetTransaction(hash)
|
||||||
if !found {
|
if !found {
|
||||||
// No finalized transaction, try to retrieve it from the pool
|
// No finalized transaction, try to retrieve it from the pool
|
||||||
if tx := api.b.GetPoolTransaction(hash); tx != nil {
|
if tx := api.b.GetPoolTransaction(hash); tx != nil {
|
||||||
return NewRPCPendingTransaction(tx, api.b.CurrentHeader(), api.b.ChainConfig()), nil
|
return NewRPCPendingTransaction(tx, api.b.CurrentHeader(), api.b.ChainConfig()), nil
|
||||||
}
|
}
|
||||||
if err == nil {
|
// If also not in the pool there is a chance the tx indexer is still in progress.
|
||||||
return nil, nil
|
if !api.b.TxIndexDone() {
|
||||||
}
|
|
||||||
return nil, NewTxIndexingError()
|
return nil, NewTxIndexingError()
|
||||||
}
|
}
|
||||||
|
// If the transaction is not found in the pool and the indexer is done, return nil
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
header, err := api.b.HeaderByHash(ctx, blockHash)
|
header, err := api.b.HeaderByHash(ctx, blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -1354,27 +1356,31 @@ func (api *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common
|
||||||
// GetRawTransactionByHash returns the bytes of the transaction for the given hash.
|
// GetRawTransactionByHash returns the bytes of the transaction for the given hash.
|
||||||
func (api *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
|
func (api *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
|
||||||
// Retrieve a finalized transaction, or a pooled otherwise
|
// Retrieve a finalized transaction, or a pooled otherwise
|
||||||
found, tx, _, _, _, err := api.b.GetTransaction(ctx, hash)
|
found, tx, _, _, _ := api.b.GetTransaction(hash)
|
||||||
if !found {
|
if !found {
|
||||||
if tx = api.b.GetPoolTransaction(hash); tx != nil {
|
if tx = api.b.GetPoolTransaction(hash); tx != nil {
|
||||||
return tx.MarshalBinary()
|
return tx.MarshalBinary()
|
||||||
}
|
}
|
||||||
if err == nil {
|
// If also not in the pool there is a chance the tx indexer is still in progress.
|
||||||
return nil, nil
|
if !api.b.TxIndexDone() {
|
||||||
}
|
|
||||||
return nil, NewTxIndexingError()
|
return nil, NewTxIndexingError()
|
||||||
}
|
}
|
||||||
|
// If the transaction is not found in the pool and the indexer is done, return nil
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
return tx.MarshalBinary()
|
return tx.MarshalBinary()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
|
// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
|
||||||
func (api *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
|
func (api *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
|
||||||
found, tx, blockHash, blockNumber, index, err := api.b.GetTransaction(ctx, hash)
|
found, tx, blockHash, blockNumber, index := api.b.GetTransaction(hash)
|
||||||
if err != nil {
|
|
||||||
return nil, NewTxIndexingError() // transaction is not fully indexed
|
|
||||||
}
|
|
||||||
if !found {
|
if !found {
|
||||||
return nil, nil // transaction is not existent or reachable
|
// Make sure indexer is done.
|
||||||
|
if !api.b.TxIndexDone() {
|
||||||
|
return nil, NewTxIndexingError()
|
||||||
|
}
|
||||||
|
// No such tx.
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
header, err := api.b.HeaderByHash(ctx, blockHash)
|
header, err := api.b.HeaderByHash(ctx, blockHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1774,16 +1780,18 @@ func (api *DebugAPI) GetRawReceipts(ctx context.Context, blockNrOrHash rpc.Block
|
||||||
// GetRawTransaction returns the bytes of the transaction for the given hash.
|
// GetRawTransaction returns the bytes of the transaction for the given hash.
|
||||||
func (api *DebugAPI) GetRawTransaction(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
|
func (api *DebugAPI) GetRawTransaction(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
|
||||||
// Retrieve a finalized transaction, or a pooled otherwise
|
// Retrieve a finalized transaction, or a pooled otherwise
|
||||||
found, tx, _, _, _, err := api.b.GetTransaction(ctx, hash)
|
found, tx, _, _, _ := api.b.GetTransaction(hash)
|
||||||
if !found {
|
if !found {
|
||||||
if tx = api.b.GetPoolTransaction(hash); tx != nil {
|
if tx = api.b.GetPoolTransaction(hash); tx != nil {
|
||||||
return tx.MarshalBinary()
|
return tx.MarshalBinary()
|
||||||
}
|
}
|
||||||
if err == nil {
|
// If also not in the pool there is a chance the tx indexer is still in progress.
|
||||||
return nil, nil
|
if !api.b.TxIndexDone() {
|
||||||
}
|
|
||||||
return nil, NewTxIndexingError()
|
return nil, NewTxIndexingError()
|
||||||
}
|
}
|
||||||
|
// Transaction is not found in the pool and the indexer is done.
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
return tx.MarshalBinary()
|
return tx.MarshalBinary()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -472,7 +472,9 @@ func (b *testBackend) setPendingBlock(block *types.Block) {
|
||||||
b.pending = block
|
b.pending = block
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b testBackend) SyncProgress() ethereum.SyncProgress { return ethereum.SyncProgress{} }
|
func (b testBackend) SyncProgress(ctx context.Context) ethereum.SyncProgress {
|
||||||
|
return ethereum.SyncProgress{}
|
||||||
|
}
|
||||||
func (b testBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
func (b testBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
|
||||||
return big.NewInt(0), nil
|
return big.NewInt(0), nil
|
||||||
}
|
}
|
||||||
|
|
@ -589,9 +591,12 @@ func (b testBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) even
|
||||||
func (b testBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
func (b testBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
func (b testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) {
|
func (b testBackend) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) {
|
||||||
tx, blockHash, blockNumber, index := rawdb.ReadTransaction(b.db, txHash)
|
tx, blockHash, blockNumber, index := rawdb.ReadTransaction(b.db, txHash)
|
||||||
return true, tx, blockHash, blockNumber, index, nil
|
return true, tx, blockHash, blockNumber, index
|
||||||
|
}
|
||||||
|
func (b testBackend) TxIndexDone() bool {
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
func (b testBackend) GetPoolTransactions() (types.Transactions, error) { panic("implement me") }
|
func (b testBackend) GetPoolTransactions() (types.Transactions, error) { panic("implement me") }
|
||||||
func (b testBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { panic("implement me") }
|
func (b testBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { panic("implement me") }
|
||||||
|
|
@ -2483,6 +2488,77 @@ func TestSimulateV1ChainLinkage(t *testing.T) {
|
||||||
require.Equal(t, block2.Hash().Bytes(), []byte(results[2].Calls[1].ReturnValue), "returned blockhash for block2 does not match")
|
require.Equal(t, block2.Hash().Bytes(), []byte(results[2].Calls[1].ReturnValue), "returned blockhash for block2 does not match")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSimulateV1TxSender(t *testing.T) {
|
||||||
|
var (
|
||||||
|
sender = common.Address{0xaa, 0xaa}
|
||||||
|
sender2 = common.Address{0xaa, 0xab}
|
||||||
|
sender3 = common.Address{0xaa, 0xac}
|
||||||
|
recipient = common.Address{0xbb, 0xbb}
|
||||||
|
gspec = &core.Genesis{
|
||||||
|
Config: params.MergedTestChainConfig,
|
||||||
|
Alloc: types.GenesisAlloc{
|
||||||
|
sender: {Balance: big.NewInt(params.Ether)},
|
||||||
|
sender2: {Balance: big.NewInt(params.Ether)},
|
||||||
|
sender3: {Balance: big.NewInt(params.Ether)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
ctx = context.Background()
|
||||||
|
)
|
||||||
|
backend := newTestBackend(t, 0, gspec, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) {})
|
||||||
|
stateDB, baseHeader, err := backend.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to get state and header: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sim := &simulator{
|
||||||
|
b: backend,
|
||||||
|
state: stateDB,
|
||||||
|
base: baseHeader,
|
||||||
|
chainConfig: backend.ChainConfig(),
|
||||||
|
gp: new(core.GasPool).AddGas(math.MaxUint64),
|
||||||
|
traceTransfers: false,
|
||||||
|
validate: false,
|
||||||
|
fullTx: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
results, err := sim.execute(ctx, []simBlock{
|
||||||
|
{Calls: []TransactionArgs{
|
||||||
|
{From: &sender, To: &recipient, Value: (*hexutil.Big)(big.NewInt(1000))},
|
||||||
|
{From: &sender2, To: &recipient, Value: (*hexutil.Big)(big.NewInt(2000))},
|
||||||
|
{From: &sender3, To: &recipient, Value: (*hexutil.Big)(big.NewInt(3000))},
|
||||||
|
}},
|
||||||
|
{Calls: []TransactionArgs{
|
||||||
|
{From: &sender2, To: &recipient, Value: (*hexutil.Big)(big.NewInt(4000))},
|
||||||
|
}},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("simulation execution failed: %v", err)
|
||||||
|
}
|
||||||
|
require.Len(t, results, 2, "expected 2 simulated blocks")
|
||||||
|
require.Len(t, results[0].Block.Transactions(), 3, "expected 3 transaction in simulated block")
|
||||||
|
require.Len(t, results[1].Block.Transactions(), 1, "expected 1 transaction in 2nd simulated block")
|
||||||
|
enc, err := json.Marshal(results)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to marshal results: %v", err)
|
||||||
|
}
|
||||||
|
type resultType struct {
|
||||||
|
Transactions []struct {
|
||||||
|
From common.Address `json:"from"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var summary []resultType
|
||||||
|
if err := json.Unmarshal(enc, &summary); err != nil {
|
||||||
|
t.Fatalf("failed to unmarshal results: %v", err)
|
||||||
|
}
|
||||||
|
require.Len(t, summary, 2, "expected 2 simulated blocks")
|
||||||
|
require.Len(t, summary[0].Transactions, 3, "expected 3 transaction in simulated block")
|
||||||
|
require.Equal(t, sender, summary[0].Transactions[0].From, "sender address mismatch")
|
||||||
|
require.Equal(t, sender2, summary[0].Transactions[1].From, "sender address mismatch")
|
||||||
|
require.Equal(t, sender3, summary[0].Transactions[2].From, "sender address mismatch")
|
||||||
|
require.Len(t, summary[1].Transactions, 1, "expected 1 transaction in simulated block")
|
||||||
|
require.Equal(t, sender2, summary[1].Transactions[0].From, "sender address mismatch")
|
||||||
|
}
|
||||||
|
|
||||||
func TestSignTransaction(t *testing.T) {
|
func TestSignTransaction(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
// Initialize test accounts
|
// Initialize test accounts
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ import (
|
||||||
// both full and light clients) with access to necessary functions.
|
// both full and light clients) with access to necessary functions.
|
||||||
type Backend interface {
|
type Backend interface {
|
||||||
// General Ethereum API
|
// General Ethereum API
|
||||||
SyncProgress() ethereum.SyncProgress
|
SyncProgress(ctx context.Context) ethereum.SyncProgress
|
||||||
|
|
||||||
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
|
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
|
||||||
FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error)
|
FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error)
|
||||||
|
|
@ -74,7 +74,8 @@ type Backend interface {
|
||||||
|
|
||||||
// Transaction pool API
|
// Transaction pool API
|
||||||
SendTx(ctx context.Context, signedTx *types.Transaction) error
|
SendTx(ctx context.Context, signedTx *types.Transaction) error
|
||||||
GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error)
|
GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64)
|
||||||
|
TxIndexDone() bool
|
||||||
GetPoolTransactions() (types.Transactions, error)
|
GetPoolTransactions() (types.Transactions, error)
|
||||||
GetPoolTransaction(txHash common.Hash) *types.Transaction
|
GetPoolTransaction(txHash common.Hash) *types.Transaction
|
||||||
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
|
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
|
||||||
|
|
|
||||||
|
|
@ -78,11 +78,25 @@ type simBlockResult struct {
|
||||||
chainConfig *params.ChainConfig
|
chainConfig *params.ChainConfig
|
||||||
Block *types.Block
|
Block *types.Block
|
||||||
Calls []simCallResult
|
Calls []simCallResult
|
||||||
|
// senders is a map of transaction hashes to their senders.
|
||||||
|
senders map[common.Hash]common.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *simBlockResult) MarshalJSON() ([]byte, error) {
|
func (r *simBlockResult) MarshalJSON() ([]byte, error) {
|
||||||
blockData := RPCMarshalBlock(r.Block, true, r.fullTx, r.chainConfig)
|
blockData := RPCMarshalBlock(r.Block, true, r.fullTx, r.chainConfig)
|
||||||
blockData["calls"] = r.Calls
|
blockData["calls"] = r.Calls
|
||||||
|
// Set tx sender if user requested full tx objects.
|
||||||
|
if r.fullTx {
|
||||||
|
if raw, ok := blockData["transactions"].([]any); ok {
|
||||||
|
for _, tx := range raw {
|
||||||
|
if tx, ok := tx.(*RPCTransaction); ok {
|
||||||
|
tx.From = r.senders[tx.Hash]
|
||||||
|
} else {
|
||||||
|
return nil, errors.New("simulated transaction result has invalid type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return json.Marshal(blockData)
|
return json.Marshal(blockData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -181,18 +195,18 @@ func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]*simBlo
|
||||||
parent = sim.base
|
parent = sim.base
|
||||||
)
|
)
|
||||||
for bi, block := range blocks {
|
for bi, block := range blocks {
|
||||||
result, callResults, err := sim.processBlock(ctx, &block, headers[bi], parent, headers[:bi], timeout)
|
result, callResults, senders, err := sim.processBlock(ctx, &block, headers[bi], parent, headers[:bi], timeout)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
headers[bi] = result.Header()
|
headers[bi] = result.Header()
|
||||||
results[bi] = &simBlockResult{fullTx: sim.fullTx, chainConfig: sim.chainConfig, Block: result, Calls: callResults}
|
results[bi] = &simBlockResult{fullTx: sim.fullTx, chainConfig: sim.chainConfig, Block: result, Calls: callResults, senders: senders}
|
||||||
parent = result.Header()
|
parent = result.Header()
|
||||||
}
|
}
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, parent *types.Header, headers []*types.Header, timeout time.Duration) (*types.Block, []simCallResult, error) {
|
func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, parent *types.Header, headers []*types.Header, timeout time.Duration) (*types.Block, []simCallResult, map[common.Hash]common.Address, error) {
|
||||||
// Set header fields that depend only on parent block.
|
// Set header fields that depend only on parent block.
|
||||||
// Parent hash is needed for evm.GetHashFn to work.
|
// Parent hash is needed for evm.GetHashFn to work.
|
||||||
header.ParentHash = parent.Hash()
|
header.ParentHash = parent.Hash()
|
||||||
|
|
@ -222,7 +236,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
precompiles := sim.activePrecompiles(sim.base)
|
precompiles := sim.activePrecompiles(sim.base)
|
||||||
// State overrides are applied prior to execution of a block
|
// State overrides are applied prior to execution of a block
|
||||||
if err := block.StateOverrides.Apply(sim.state, precompiles); err != nil {
|
if err := block.StateOverrides.Apply(sim.state, precompiles); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
gasUsed, blobGasUsed uint64
|
gasUsed, blobGasUsed uint64
|
||||||
|
|
@ -235,6 +249,10 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
NoBaseFee: !sim.validate,
|
NoBaseFee: !sim.validate,
|
||||||
Tracer: tracer.Hooks(),
|
Tracer: tracer.Hooks(),
|
||||||
}
|
}
|
||||||
|
// senders is a map of transaction hashes to their senders.
|
||||||
|
// Transaction objects contain only the signature, and we lose track
|
||||||
|
// of the sender when translating the arguments into a transaction object.
|
||||||
|
senders = make(map[common.Hash]common.Address)
|
||||||
)
|
)
|
||||||
tracingStateDB := vm.StateDB(sim.state)
|
tracingStateDB := vm.StateDB(sim.state)
|
||||||
if hooks := tracer.Hooks(); hooks != nil {
|
if hooks := tracer.Hooks(); hooks != nil {
|
||||||
|
|
@ -255,16 +273,17 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
var allLogs []*types.Log
|
var allLogs []*types.Log
|
||||||
for i, call := range block.Calls {
|
for i, call := range block.Calls {
|
||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
if err := sim.sanitizeCall(&call, sim.state, header, blockContext, &gasUsed); err != nil {
|
if err := sim.sanitizeCall(&call, sim.state, header, blockContext, &gasUsed); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
tx = call.ToTransaction(types.DynamicFeeTxType)
|
tx = call.ToTransaction(types.DynamicFeeTxType)
|
||||||
txHash = tx.Hash()
|
txHash = tx.Hash()
|
||||||
)
|
)
|
||||||
txes[i] = tx
|
txes[i] = tx
|
||||||
|
senders[txHash] = call.from()
|
||||||
tracer.reset(txHash, uint(i))
|
tracer.reset(txHash, uint(i))
|
||||||
sim.state.SetTxContext(txHash, i)
|
sim.state.SetTxContext(txHash, i)
|
||||||
// EoA check is always skipped, even in validation mode.
|
// EoA check is always skipped, even in validation mode.
|
||||||
|
|
@ -272,7 +291,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp)
|
result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
txErr := txValidationError(err)
|
txErr := txValidationError(err)
|
||||||
return nil, nil, txErr
|
return nil, nil, nil, txErr
|
||||||
}
|
}
|
||||||
// Update the state with pending changes.
|
// Update the state with pending changes.
|
||||||
var root []byte
|
var root []byte
|
||||||
|
|
@ -311,15 +330,15 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
requests = [][]byte{}
|
requests = [][]byte{}
|
||||||
// EIP-6110
|
// EIP-6110
|
||||||
if err := core.ParseDepositLogs(&requests, allLogs, sim.chainConfig); err != nil {
|
if err := core.ParseDepositLogs(&requests, allLogs, sim.chainConfig); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
// EIP-7002
|
// EIP-7002
|
||||||
if err := core.ProcessWithdrawalQueue(&requests, evm); err != nil {
|
if err := core.ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
// EIP-7251
|
// EIP-7251
|
||||||
if err := core.ProcessConsolidationQueue(&requests, evm); err != nil {
|
if err := core.ProcessConsolidationQueue(&requests, evm); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if requests != nil {
|
if requests != nil {
|
||||||
|
|
@ -330,10 +349,10 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
||||||
chainHeadReader := &simChainHeadReader{ctx, sim.b}
|
chainHeadReader := &simChainHeadReader{ctx, sim.b}
|
||||||
b, err := sim.b.Engine().FinalizeAndAssemble(chainHeadReader, header, sim.state, blockBody, receipts)
|
b, err := sim.b.Engine().FinalizeAndAssemble(chainHeadReader, header, sim.state, blockBody, receipts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
repairLogs(callResults, b.Hash())
|
repairLogs(callResults, b.Hash())
|
||||||
return b, callResults, nil
|
return b, callResults, senders, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// repairLogs updates the block hash in the logs present in the result of
|
// repairLogs updates the block hash in the logs present in the result of
|
||||||
|
|
|
||||||
|
|
@ -323,7 +323,9 @@ func (b *backendMock) CurrentHeader() *types.Header { return b.current }
|
||||||
func (b *backendMock) ChainConfig() *params.ChainConfig { return b.config }
|
func (b *backendMock) ChainConfig() *params.ChainConfig { return b.config }
|
||||||
|
|
||||||
// Other methods needed to implement Backend interface.
|
// Other methods needed to implement Backend interface.
|
||||||
func (b *backendMock) SyncProgress() ethereum.SyncProgress { return ethereum.SyncProgress{} }
|
func (b *backendMock) SyncProgress(ctx context.Context) ethereum.SyncProgress {
|
||||||
|
return ethereum.SyncProgress{}
|
||||||
|
}
|
||||||
func (b *backendMock) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error) {
|
func (b *backendMock) FeeHistory(ctx context.Context, blockCount uint64, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error) {
|
||||||
return nil, nil, nil, nil, nil, nil, nil
|
return nil, nil, nil, nil, nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
@ -378,9 +380,10 @@ func (b *backendMock) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) eve
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (b *backendMock) SendTx(ctx context.Context, signedTx *types.Transaction) error { return nil }
|
func (b *backendMock) SendTx(ctx context.Context, signedTx *types.Transaction) error { return nil }
|
||||||
func (b *backendMock) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) {
|
func (b *backendMock) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) {
|
||||||
return false, nil, [32]byte{}, 0, 0, nil
|
return false, nil, [32]byte{}, 0, 0
|
||||||
}
|
}
|
||||||
|
func (b *backendMock) TxIndexDone() bool { return true }
|
||||||
func (b *backendMock) GetPoolTransactions() (types.Transactions, error) { return nil, nil }
|
func (b *backendMock) GetPoolTransactions() (types.Transactions, error) { return nil, nil }
|
||||||
func (b *backendMock) GetPoolTransaction(txHash common.Hash) *types.Transaction { return nil }
|
func (b *backendMock) GetPoolTransaction(txHash common.Hash) *types.Transaction { return nil }
|
||||||
func (b *backendMock) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
func (b *backendMock) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
||||||
|
|
|
||||||
|
|
@ -570,7 +570,6 @@ func TestHTTPWriteTimeout(t *testing.T) {
|
||||||
// Send normal request
|
// Send normal request
|
||||||
t.Run("message", func(t *testing.T) {
|
t.Run("message", func(t *testing.T) {
|
||||||
resp := rpcRequest(t, url, "test_sleep")
|
resp := rpcRequest(t, url, "test_sleep")
|
||||||
defer resp.Body.Close()
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -584,7 +583,6 @@ func TestHTTPWriteTimeout(t *testing.T) {
|
||||||
t.Run("batch", func(t *testing.T) {
|
t.Run("batch", func(t *testing.T) {
|
||||||
want := fmt.Sprintf("[%s,%s,%s]", greetRes, timeoutRes, timeoutRes)
|
want := fmt.Sprintf("[%s,%s,%s]", greetRes, timeoutRes, timeoutRes)
|
||||||
resp := batchRpcRequest(t, url, []string{"test_greet", "test_sleep", "test_greet"})
|
resp := batchRpcRequest(t, url, []string{"test_greet", "test_sleep", "test_greet"})
|
||||||
defer resp.Body.Close()
|
|
||||||
body, err := io.ReadAll(resp.Body)
|
body, err := io.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -358,7 +358,7 @@ var (
|
||||||
Max: 9,
|
Max: 9,
|
||||||
UpdateFraction: 5007716,
|
UpdateFraction: 5007716,
|
||||||
}
|
}
|
||||||
// DefaultBlobSchedule is the latest configured blob schedule for test chains.
|
// DefaultBlobSchedule is the latest configured blob schedule for Ethereum mainnet.
|
||||||
DefaultBlobSchedule = &BlobScheduleConfig{
|
DefaultBlobSchedule = &BlobScheduleConfig{
|
||||||
Cancun: DefaultCancunBlobConfig,
|
Cancun: DefaultCancunBlobConfig,
|
||||||
Prague: DefaultPragueBlobConfig,
|
Prague: DefaultPragueBlobConfig,
|
||||||
|
|
|
||||||
|
|
@ -501,6 +501,10 @@ func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage
|
||||||
if msg.isUnsubscribe() {
|
if msg.isUnsubscribe() {
|
||||||
callb = h.unsubscribeCb
|
callb = h.unsubscribeCb
|
||||||
} else {
|
} else {
|
||||||
|
// Check method name length
|
||||||
|
if len(msg.Method) > maxMethodNameLength {
|
||||||
|
return msg.errorResponse(&invalidRequestError{fmt.Sprintf("method name too long: %d > %d", len(msg.Method), maxMethodNameLength)})
|
||||||
|
}
|
||||||
callb = h.reg.callback(msg.Method)
|
callb = h.reg.callback(msg.Method)
|
||||||
}
|
}
|
||||||
if callb == nil {
|
if callb == nil {
|
||||||
|
|
@ -536,6 +540,11 @@ func (h *handler) handleSubscribe(cp *callProc, msg *jsonrpcMessage) *jsonrpcMes
|
||||||
return msg.errorResponse(ErrNotificationsUnsupported)
|
return msg.errorResponse(ErrNotificationsUnsupported)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check method name length
|
||||||
|
if len(msg.Method) > maxMethodNameLength {
|
||||||
|
return msg.errorResponse(&invalidRequestError{fmt.Sprintf("subscription name too long: %d > %d", len(msg.Method), maxMethodNameLength)})
|
||||||
|
}
|
||||||
|
|
||||||
// Subscription method name is first argument.
|
// Subscription method name is first argument.
|
||||||
name, err := parseSubscriptionName(msg.Params)
|
name, err := parseSubscriptionName(msg.Params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ const (
|
||||||
subscribeMethodSuffix = "_subscribe"
|
subscribeMethodSuffix = "_subscribe"
|
||||||
unsubscribeMethodSuffix = "_unsubscribe"
|
unsubscribeMethodSuffix = "_unsubscribe"
|
||||||
notificationMethodSuffix = "_subscription"
|
notificationMethodSuffix = "_subscription"
|
||||||
|
maxMethodNameLength = 2048
|
||||||
|
|
||||||
defaultWriteTimeout = 10 * time.Second // used if context has no deadline
|
defaultWriteTimeout = 10 * time.Second // used if context has no deadline
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -391,3 +391,77 @@ func wsPingTestHandler(t *testing.T, conn *websocket.Conn, shutdown, sendPing <-
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWebsocketMethodNameLengthLimit(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var (
|
||||||
|
srv = newTestServer()
|
||||||
|
httpsrv = httptest.NewServer(srv.WebsocketHandler([]string{"*"}))
|
||||||
|
wsURL = "ws:" + strings.TrimPrefix(httpsrv.URL, "http:")
|
||||||
|
)
|
||||||
|
defer srv.Stop()
|
||||||
|
defer httpsrv.Close()
|
||||||
|
|
||||||
|
client, err := DialWebsocket(context.Background(), wsURL, "")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("can't dial: %v", err)
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
// Test cases
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
method string
|
||||||
|
params []interface{}
|
||||||
|
expectedError string
|
||||||
|
isSubscription bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid method name",
|
||||||
|
method: "test_echo",
|
||||||
|
params: []interface{}{"test", 1},
|
||||||
|
expectedError: "",
|
||||||
|
isSubscription: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "method name too long",
|
||||||
|
method: "test_" + string(make([]byte, maxMethodNameLength+1)),
|
||||||
|
params: []interface{}{"test", 1},
|
||||||
|
expectedError: "method name too long",
|
||||||
|
isSubscription: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid subscription",
|
||||||
|
method: "nftest_subscribe",
|
||||||
|
params: []interface{}{"someSubscription", 1, 2},
|
||||||
|
expectedError: "",
|
||||||
|
isSubscription: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "subscription name too long",
|
||||||
|
method: string(make([]byte, maxMethodNameLength+1)) + "_subscribe",
|
||||||
|
params: []interface{}{"newHeads"},
|
||||||
|
expectedError: "subscription name too long",
|
||||||
|
isSubscription: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var result interface{}
|
||||||
|
err := client.Call(&result, tt.method, tt.params...)
|
||||||
|
if tt.expectedError == "" {
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error, got nil")
|
||||||
|
} else if !strings.Contains(err.Error(), tt.expectedError) {
|
||||||
|
t.Errorf("expected error containing %q, got %q", tt.expectedError, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -485,11 +485,19 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, keys [][]byte, valu
|
||||||
if len(keys) != len(values) {
|
if len(keys) != len(values) {
|
||||||
return false, fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values))
|
return false, fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values))
|
||||||
}
|
}
|
||||||
// Ensure the received batch is monotonic increasing and contains no deletions
|
// Ensure the received batch is
|
||||||
|
// - monotonically increasing,
|
||||||
|
// - not expanding down prefix-paths
|
||||||
|
// - and contains no deletions
|
||||||
for i := 0; i < len(keys); i++ {
|
for i := 0; i < len(keys); i++ {
|
||||||
if i < len(keys)-1 && bytes.Compare(keys[i], keys[i+1]) >= 0 {
|
if i < len(keys)-1 {
|
||||||
|
if bytes.Compare(keys[i], keys[i+1]) >= 0 {
|
||||||
return false, errors.New("range is not monotonically increasing")
|
return false, errors.New("range is not monotonically increasing")
|
||||||
}
|
}
|
||||||
|
if bytes.HasPrefix(keys[i+1], keys[i]) {
|
||||||
|
return false, errors.New("range contains path prefixes")
|
||||||
|
}
|
||||||
|
}
|
||||||
if len(values[i]) == 0 {
|
if len(values[i]) == 0 {
|
||||||
return false, errors.New("range contains deletion")
|
return false, errors.New("range contains deletion")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1000,3 +1000,35 @@ func TestRangeProofKeysWithSharedPrefix(t *testing.T) {
|
||||||
t.Error("expected more to be false")
|
t.Error("expected more to be false")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestRangeProofErrors tests a few cases where the prover is supposed
|
||||||
|
// to exit with errors
|
||||||
|
func TestRangeProofErrors(t *testing.T) {
|
||||||
|
// Different number of keys to values
|
||||||
|
_, err := VerifyRangeProof((common.Hash{}), []byte{}, make([][]byte, 5), make([][]byte, 4), nil)
|
||||||
|
if have, want := err.Error(), "inconsistent proof data, keys: 5, values: 4"; have != want {
|
||||||
|
t.Fatalf("wrong error, have %q, want %q", err.Error(), want)
|
||||||
|
}
|
||||||
|
// Non-increasing paths
|
||||||
|
_, err = VerifyRangeProof((common.Hash{}), []byte{},
|
||||||
|
[][]byte{[]byte{2, 1}, []byte{2, 1}}, make([][]byte, 2), nil)
|
||||||
|
if have, want := err.Error(), "range is not monotonically increasing"; have != want {
|
||||||
|
t.Fatalf("wrong error, have %q, want %q", err.Error(), want)
|
||||||
|
}
|
||||||
|
// A prefixed path is never motivated. Inserting the second element will
|
||||||
|
// require rewriting/overwriting the previous value-node, thus can only
|
||||||
|
// happen if the data is corrupt.
|
||||||
|
_, err = VerifyRangeProof((common.Hash{}), []byte{},
|
||||||
|
[][]byte{[]byte{2, 1}, []byte{2, 1, 2}},
|
||||||
|
[][]byte{[]byte{1}, []byte{1}}, nil)
|
||||||
|
if have, want := err.Error(), "range contains path prefixes"; have != want {
|
||||||
|
t.Fatalf("wrong error, have %q, want %q", err.Error(), want)
|
||||||
|
}
|
||||||
|
// Empty values (deletions)
|
||||||
|
_, err = VerifyRangeProof((common.Hash{}), []byte{},
|
||||||
|
[][]byte{[]byte{2, 1}, []byte{2, 2}},
|
||||||
|
[][]byte{[]byte{1}, []byte{}}, nil)
|
||||||
|
if have, want := err.Error(), "range contains deletion"; have != want {
|
||||||
|
t.Fatalf("wrong error, have %q, want %q", err.Error(), want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
78
triedb/preimages_test.go
Normal file
78
triedb/preimages_test.go
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
// Copyright 2023 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package triedb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
"github.com/ethereum/go-ethereum/triedb/hashdb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestDatabasePreimages tests the preimage functionality of the trie database.
|
||||||
|
func TestDatabasePreimages(t *testing.T) {
|
||||||
|
// Create a database with preimages enabled
|
||||||
|
memDB := rawdb.NewMemoryDatabase()
|
||||||
|
config := &Config{
|
||||||
|
Preimages: true,
|
||||||
|
HashDB: hashdb.Defaults,
|
||||||
|
}
|
||||||
|
db := NewDatabase(memDB, config)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
// Test inserting and retrieving preimages
|
||||||
|
preimages := make(map[common.Hash][]byte)
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
data := []byte{byte(i), byte(i + 1), byte(i + 2)}
|
||||||
|
hash := common.BytesToHash(data)
|
||||||
|
preimages[hash] = data
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert preimages into the database
|
||||||
|
db.InsertPreimage(preimages)
|
||||||
|
|
||||||
|
// Verify all preimages are retrievable
|
||||||
|
for hash, data := range preimages {
|
||||||
|
retrieved := db.Preimage(hash)
|
||||||
|
if retrieved == nil {
|
||||||
|
t.Errorf("Preimage for %x not found", hash)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(retrieved, data) {
|
||||||
|
t.Errorf("Preimage data mismatch: got %x want %x", retrieved, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test non-existent preimage
|
||||||
|
nonExistentHash := common.HexToHash("deadbeef")
|
||||||
|
if data := db.Preimage(nonExistentHash); data != nil {
|
||||||
|
t.Errorf("Unexpected preimage data for non-existent hash: %x", data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force preimage commit and verify again
|
||||||
|
db.WritePreimages()
|
||||||
|
for hash, data := range preimages {
|
||||||
|
retrieved := db.Preimage(hash)
|
||||||
|
if retrieved == nil {
|
||||||
|
t.Errorf("Preimage for %x not found after forced commit", hash)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(retrieved, data) {
|
||||||
|
t.Errorf("Preimage data mismatch after forced commit: got %x want %x", retrieved, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,6 @@ package version
|
||||||
const (
|
const (
|
||||||
Major = 1 // Major version component of the current release
|
Major = 1 // Major version component of the current release
|
||||||
Minor = 15 // Minor version component of the current release
|
Minor = 15 // Minor version component of the current release
|
||||||
Patch = 10 // Patch version component of the current release
|
Patch = 11 // Patch version component of the current release
|
||||||
Meta = "stable" // Version metadata to append to the version string
|
Meta = "stable" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue