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 {
|
||||
Status string `json:"status"`
|
||||
Witness *hexutil.Bytes `json:"witness"`
|
||||
Witness *hexutil.Bytes `json:"witness,omitempty"`
|
||||
LatestValidHash *common.Hash `json:"latestValidHash"`
|
||||
ValidationError *string `json:"validationError"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ func (s *Suite) EthTests() []utesting.Test {
|
|||
{Name: "Status", Fn: s.TestStatus},
|
||||
// get block headers
|
||||
{Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders},
|
||||
{Name: "GetNonexistentBlockHeaders", Fn: s.TestGetNonexistentBlockHeaders},
|
||||
{Name: "SimultaneousRequests", Fn: s.TestSimultaneousRequests},
|
||||
{Name: "SameRequestID", Fn: s.TestSameRequestID},
|
||||
{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) {
|
||||
t.Log(`This test requests blocks headers from the node, performing two requests
|
||||
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())
|
||||
defer triedb.Close()
|
||||
|
||||
_, hash, _, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
||||
_, hash, compatErr, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
||||
if err != nil {
|
||||
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)
|
||||
|
||||
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" {
|
||||
Fatalf("--%s flag must be 'gokzg' or 'ckzg'", CryptoKZGFlag.Name)
|
||||
}
|
||||
log.Info("Initializing the KZG library", "backend", ctx.String(CryptoKZGFlag.Name))
|
||||
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)
|
||||
}
|
||||
// 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))
|
||||
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)
|
||||
}
|
||||
}()
|
||||
|
||||
// VM tracing config.
|
||||
if name := ctx.String(VMTraceFlag.Name); name != "" {
|
||||
cfg.VMTrace = name
|
||||
|
|
|
|||
|
|
@ -234,6 +234,14 @@ func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.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
|
||||
// a specific distance is reached.
|
||||
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
|
||||
// itself associate with the given transaction hash.
|
||||
//
|
||||
// An error will be returned if the transaction is not found, and background
|
||||
// indexing for transactions is still in progress. The transaction might be
|
||||
// reachable shortly once it's indexed.
|
||||
//
|
||||
// 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) {
|
||||
// A null will be returned if the transaction is not found. This can be due to
|
||||
// the transaction indexer not being finished. The caller must explicitly check
|
||||
// the indexer progress.
|
||||
func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLookupEntry, *types.Transaction) {
|
||||
bc.txLookupLock.RLock()
|
||||
defer bc.txLookupLock.RUnlock()
|
||||
|
||||
// Short circuit if the txlookup already in the cache, retrieve otherwise
|
||||
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)
|
||||
if tx == nil {
|
||||
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 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
|
||||
return nil, nil
|
||||
}
|
||||
lookup := &rawdb.LegacyTxLookupEntry{
|
||||
BlockHash: blockHash,
|
||||
|
|
@ -308,7 +294,23 @@ func (bc *BlockChain) GetTransactionLookup(hash common.Hash) (*rawdb.LegacyTxLoo
|
|||
lookup: lookup,
|
||||
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.
|
||||
|
|
@ -404,7 +406,7 @@ func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) {
|
|||
if bc.txIndexer == nil {
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ type blockchain interface {
|
|||
GetHeader(hash common.Hash, number uint64) *types.Header
|
||||
GetCanonicalHash(number uint64) common.Hash
|
||||
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
|
||||
|
|
@ -102,10 +103,23 @@ func (cv *ChainView) Receipts(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.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.
|
||||
func (cv *ChainView) SharedRange(cv2 *ChainView) common.Range[uint64] {
|
||||
cv.lock.Lock()
|
||||
|
|
@ -121,14 +135,6 @@ func (cv *ChainView) SharedRange(cv2 *ChainView) common.Range[uint64] {
|
|||
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.
|
||||
func equalViews(cv1, cv2 *ChainView) bool {
|
||||
if cv1 == nil || cv2 == nil {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ var (
|
|||
)
|
||||
|
||||
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
|
||||
cachedLvPointers = 1000 // first log value pointer of block pointers
|
||||
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{}),
|
||||
exportFileName: config.ExportFileName,
|
||||
Params: params,
|
||||
targetView: initView,
|
||||
indexedView: initView,
|
||||
indexedRange: filterMapsRange{
|
||||
initialized: initialized,
|
||||
headIndexed: rs.HeadIndexed,
|
||||
|
|
@ -265,16 +267,8 @@ func NewFilterMaps(db ethdb.KeyValueStore, initView *ChainView, historyCutoff, f
|
|||
baseRowsCache: lru.NewCache[uint64, [][]uint32](cachedBaseRows),
|
||||
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() {
|
||||
log.Info("Initialized log indexer",
|
||||
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
||||
|
|
@ -303,29 +297,40 @@ func (f *FilterMaps) Stop() {
|
|||
f.closeWg.Wait()
|
||||
}
|
||||
|
||||
// initChainView returns a chain view consistent with both the current target
|
||||
// view and the current state of the log index as found in the database, based
|
||||
// on the last block of stored maps.
|
||||
// Note that the returned view might be shorter than the existing index if
|
||||
// the latest maps are not consistent with targetView.
|
||||
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)
|
||||
if err != nil {
|
||||
log.Error("Could not initialize indexed chain view", "error", err)
|
||||
break
|
||||
}
|
||||
if lastBlockNumber <= chainView.HeadNumber() && chainView.BlockId(lastBlockNumber) == lastBlockId {
|
||||
return chainView.limitedView(lastBlockNumber)
|
||||
}
|
||||
// checkRevertRange checks whether the existing index is consistent with the
|
||||
// current indexed view and reverts inconsistent maps if necessary.
|
||||
func (f *FilterMaps) checkRevertRange() {
|
||||
if f.indexedRange.maps.Count() == 0 {
|
||||
return
|
||||
}
|
||||
lastMap := f.indexedRange.maps.Last()
|
||||
lastBlockNumber, lastBlockId, err := f.getLastBlockOfMap(lastMap)
|
||||
if err != nil {
|
||||
log.Error("Error initializing log index database; resetting log index", "error", err)
|
||||
f.reset()
|
||||
return
|
||||
}
|
||||
for lastBlockNumber > f.indexedView.HeadNumber() || f.indexedView.BlockId(lastBlockNumber) != lastBlockId {
|
||||
// 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
|
||||
|
|
@ -662,15 +667,11 @@ func (f *FilterMaps) mapRowIndex(mapIndex, rowIndex uint32) uint64 {
|
|||
}
|
||||
|
||||
// getBlockLvPointer returns the starting log value index where the log values
|
||||
// generated by the given block are located. If blockNumber is beyond the current
|
||||
// head then the first unoccupied log value index is returned.
|
||||
// generated by the given block are located.
|
||||
//
|
||||
// Note that this function assumes that the indexer read lock is being held when
|
||||
// called from outside the indexerLoop goroutine.
|
||||
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 {
|
||||
return lvPointer, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ func (f *FilterMaps) tryIndexHead() error {
|
|||
((!f.loggedHeadIndex && time.Since(f.startedHeadIndexAt) > headLogDelay) ||
|
||||
time.Since(f.lastLogHeadIndex) > logFrequency) {
|
||||
log.Info("Log index head rendering in progress",
|
||||
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
||||
"firstblock", f.indexedRange.blocks.First(), "lastblock", f.indexedRange.blocks.Last(),
|
||||
"processed", f.indexedRange.blocks.AfterLast()-f.ptrHeadIndex,
|
||||
"remaining", f.indexedView.HeadNumber()-f.indexedRange.blocks.Last(),
|
||||
"elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt)))
|
||||
|
|
@ -266,7 +266,7 @@ func (f *FilterMaps) tryIndexHead() error {
|
|||
}
|
||||
if f.loggedHeadIndex && f.indexedRange.hasIndexedBlocks() {
|
||||
log.Info("Log index head rendering finished",
|
||||
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
||||
"firstblock", f.indexedRange.blocks.First(), "lastblock", f.indexedRange.blocks.Last(),
|
||||
"processed", f.indexedRange.blocks.AfterLast()-f.ptrHeadIndex,
|
||||
"elapsed", common.PrettyDuration(time.Since(f.startedHeadIndexAt)))
|
||||
}
|
||||
|
|
@ -323,7 +323,7 @@ func (f *FilterMaps) tryIndexTail() (bool, error) {
|
|||
if f.indexedRange.hasIndexedBlocks() && f.ptrTailIndex >= f.indexedRange.blocks.First() &&
|
||||
(!f.loggedTailIndex || time.Since(f.lastLogTailIndex) > logFrequency) {
|
||||
log.Info("Log index tail rendering in progress",
|
||||
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
||||
"firstblock", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
||||
"processed", f.ptrTailIndex-f.indexedRange.blocks.First()+tpb,
|
||||
"remaining", remaining,
|
||||
"next tail epoch percentage", f.indexedRange.tailPartialEpoch*100/f.mapsPerEpoch,
|
||||
|
|
@ -346,7 +346,7 @@ func (f *FilterMaps) tryIndexTail() (bool, error) {
|
|||
}
|
||||
if f.loggedTailIndex && f.indexedRange.hasIndexedBlocks() {
|
||||
log.Info("Log index tail rendering finished",
|
||||
"first block", f.indexedRange.blocks.First(), "last block", f.indexedRange.blocks.Last(),
|
||||
"firstblock", f.indexedRange.blocks.First(), "lastblock", f.indexedRange.blocks.Last(),
|
||||
"processed", f.ptrTailIndex-f.indexedRange.blocks.First(),
|
||||
"elapsed", common.PrettyDuration(time.Since(f.startedTailIndexAt)))
|
||||
f.loggedTailIndex = false
|
||||
|
|
|
|||
|
|
@ -515,6 +515,13 @@ func (tc *testChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
|||
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) {
|
||||
tc.lock.Lock()
|
||||
blockGen := func(i int, gen *core.BlockGen) {
|
||||
|
|
|
|||
|
|
@ -468,15 +468,25 @@ func (r *mapRenderer) writeFinishedMaps(pauseCb func() bool) error {
|
|||
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
|
||||
blockNumber := r.finishedMaps[r.finished.First()].firstBlock()
|
||||
for mapIndex := range r.finished.Iter() {
|
||||
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)
|
||||
checkWriteCnt()
|
||||
if blockNumber != renderedMap.firstBlock() {
|
||||
panic("non-continuous block numbers")
|
||||
}
|
||||
for _, lvPtr := range renderedMap.blockLvPtrs {
|
||||
r.f.storeBlockLvPointer(batch, blockNumber, lvPtr)
|
||||
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())
|
||||
}
|
||||
// get block receipts
|
||||
receipts := f.targetView.Receipts(startBlock)
|
||||
receipts := f.targetView.RawReceipts(startBlock)
|
||||
if receipts == nil {
|
||||
return nil, fmt.Errorf("receipts not found for start block %d", startBlock)
|
||||
}
|
||||
|
|
@ -760,7 +770,7 @@ func (l *logIterator) next() error {
|
|||
if l.delimiter {
|
||||
l.delimiter = false
|
||||
l.blockNumber++
|
||||
l.receipts = l.chainView.Receipts(l.blockNumber)
|
||||
l.receipts = l.chainView.RawReceipts(l.blockNumber)
|
||||
if l.receipts == nil {
|
||||
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
|
||||
// generated by the given block are located. If blockNumber is beyond the current
|
||||
// head then the first unoccupied log value index is returned.
|
||||
// generated by the given block are located. If blockNumber is beyond the last
|
||||
// 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.
|
||||
func (fm *FilterMapsMatcherBackend) GetBlockLvPointer(ctx context.Context, blockNumber uint64) (uint64, error) {
|
||||
fm.f.indexLock.RLock()
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -365,6 +365,9 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, g
|
|||
return nil, common.Hash{}, nil, errors.New("missing head header")
|
||||
}
|
||||
newCfg := genesis.chainConfigOrDefault(ghash, storedCfg)
|
||||
if err := overrides.apply(newCfg); err != nil {
|
||||
return nil, common.Hash{}, nil, err
|
||||
}
|
||||
|
||||
// Sanity-check the new configuration.
|
||||
if err := newCfg.CheckConfigForkOrder(); err != nil {
|
||||
|
|
|
|||
|
|
@ -34,10 +34,10 @@ import (
|
|||
// ExecutionResult includes all output after executing given evm
|
||||
// message no matter the execution itself is successful or not.
|
||||
type ExecutionResult struct {
|
||||
UsedGas uint64 // Total used gas, not including the refunded gas
|
||||
RefundedGas uint64 // Total gas refunded after execution
|
||||
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)
|
||||
UsedGas uint64 // Total used gas, not including the refunded gas
|
||||
MaxUsedGas uint64 // Maximum gas consumed during execution, excluding gas refunds.
|
||||
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)
|
||||
}
|
||||
|
||||
// Unwrap returns the internal evm error which allows us for further
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
gasRefund := st.calcRefund()
|
||||
st.gasRemaining += gasRefund
|
||||
st.gasRemaining += st.calcRefund()
|
||||
if rules.IsPrague {
|
||||
// After EIP-7623: Data-heavy transactions pay the floor gas.
|
||||
if st.gasUsed() < floorDataGas {
|
||||
|
|
@ -521,6 +524,9 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
t.OnGasChange(prev, st.gasRemaining, tracing.GasChangeTxDataFloor)
|
||||
}
|
||||
}
|
||||
if peakGasUsed < floorDataGas {
|
||||
peakGasUsed = floorDataGas
|
||||
}
|
||||
}
|
||||
st.returnGas()
|
||||
|
||||
|
|
@ -549,10 +555,10 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
}
|
||||
|
||||
return &ExecutionResult{
|
||||
UsedGas: st.gasUsed(),
|
||||
RefundedGas: gasRefund,
|
||||
Err: vmerr,
|
||||
ReturnData: ret,
|
||||
UsedGas: st.gasUsed(),
|
||||
MaxUsedGas: peakGasUsed,
|
||||
Err: vmerr,
|
||||
ReturnData: ret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
|
|
@ -47,26 +47,38 @@ type txIndexer struct {
|
|||
// and all others shouldn't.
|
||||
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
|
||||
// be pruned and not available locally.
|
||||
cutoff uint64
|
||||
db ethdb.Database
|
||||
progress chan chan TxIndexProgress
|
||||
term chan chan struct{}
|
||||
closed chan struct{}
|
||||
cutoff uint64
|
||||
db ethdb.Database
|
||||
term chan chan struct{}
|
||||
closed chan struct{}
|
||||
}
|
||||
|
||||
// newTxIndexer initializes the transaction indexer.
|
||||
func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer {
|
||||
cutoff, _ := chain.HistoryPruningCutoff()
|
||||
indexer := &txIndexer{
|
||||
limit: limit,
|
||||
cutoff: cutoff,
|
||||
db: chain.db,
|
||||
progress: make(chan chan TxIndexProgress),
|
||||
term: make(chan chan struct{}),
|
||||
closed: make(chan struct{}),
|
||||
limit: limit,
|
||||
cutoff: cutoff,
|
||||
db: chain.db,
|
||||
term: make(chan chan struct{}),
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
indexer.head.Store(indexer.resolveHead())
|
||||
indexer.tail.Store(rawdb.ReadTxIndexTail(chain.db))
|
||||
|
||||
go indexer.loop(chain)
|
||||
|
||||
var msg string
|
||||
|
|
@ -154,6 +166,7 @@ func (indexer *txIndexer) repair(head uint64) {
|
|||
// A crash may occur between the two delete operations,
|
||||
// potentially leaving dangling indexes in the database.
|
||||
// However, this is considered acceptable.
|
||||
indexer.tail.Store(nil)
|
||||
rawdb.DeleteTxIndexTail(indexer.db)
|
||||
rawdb.DeleteAllTxLookupEntries(indexer.db, nil)
|
||||
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
|
||||
// index namespace might be slow and expensive, but we
|
||||
// have no choice.
|
||||
indexer.tail.Store(nil)
|
||||
rawdb.DeleteTxIndexTail(indexer.db)
|
||||
rawdb.DeleteAllTxLookupEntries(indexer.db, nil)
|
||||
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,
|
||||
// potentially leaving dangling indexes in the database.
|
||||
// However, this is considered acceptable.
|
||||
indexer.tail.Store(&indexer.cutoff)
|
||||
rawdb.WriteTxIndexTail(indexer.db, indexer.cutoff)
|
||||
rawdb.DeleteAllTxLookupEntries(indexer.db, func(txhash common.Hash, blob []byte) bool {
|
||||
n := rawdb.DecodeTxLookupEntry(blob, indexer.db)
|
||||
|
|
@ -216,16 +231,15 @@ func (indexer *txIndexer) loop(chain *BlockChain) {
|
|||
|
||||
// Listening to chain events and manipulate the transaction indexes.
|
||||
var (
|
||||
stop 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
|
||||
|
||||
stop chan struct{} // Non-nil if background routine is active
|
||||
done chan struct{} // Non-nil if background routine is active
|
||||
headCh = make(chan ChainHeadEvent)
|
||||
sub = chain.SubscribeChainHeadEvent(headCh)
|
||||
)
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
// Validate the transaction indexes and repair if necessary
|
||||
head := indexer.head.Load()
|
||||
indexer.repair(head)
|
||||
|
||||
// Launch the initial processing if chain is not empty (head != genesis).
|
||||
|
|
@ -238,17 +252,18 @@ func (indexer *txIndexer) loop(chain *BlockChain) {
|
|||
for {
|
||||
select {
|
||||
case h := <-headCh:
|
||||
indexer.head.Store(h.Header.Number.Uint64())
|
||||
if done == nil {
|
||||
stop = make(chan struct{})
|
||||
done = make(chan struct{})
|
||||
go indexer.run(h.Header.Number.Uint64(), stop, done)
|
||||
}
|
||||
head = h.Header.Number.Uint64()
|
||||
|
||||
case <-done:
|
||||
stop = nil
|
||||
done = nil
|
||||
case ch := <-indexer.progress:
|
||||
ch <- indexer.report(head)
|
||||
indexer.tail.Store(rawdb.ReadTxIndexTail(indexer.db))
|
||||
|
||||
case ch := <-indexer.term:
|
||||
if stop != nil {
|
||||
close(stop)
|
||||
|
|
@ -264,7 +279,7 @@ func (indexer *txIndexer) loop(chain *BlockChain) {
|
|||
}
|
||||
|
||||
// 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,
|
||||
// nothing to index.
|
||||
if head < indexer.cutoff {
|
||||
|
|
@ -284,7 +299,6 @@ func (indexer *txIndexer) report(head uint64) TxIndexProgress {
|
|||
}
|
||||
// Compute how many blocks have been indexed
|
||||
var indexed uint64
|
||||
tail := rawdb.ReadTxIndexTail(indexer.db)
|
||||
if tail != nil {
|
||||
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
|
||||
// background tx indexer is already stopped.
|
||||
func (indexer *txIndexer) txIndexProgress() (TxIndexProgress, error) {
|
||||
ch := make(chan TxIndexProgress, 1)
|
||||
select {
|
||||
case indexer.progress <- ch:
|
||||
return <-ch, nil
|
||||
case <-indexer.closed:
|
||||
return TxIndexProgress{}, errors.New("indexer is closed")
|
||||
}
|
||||
// txIndexProgress retrieves the transaction indexing progress. The reported
|
||||
// progress may slightly lag behind the actual indexing state, as the tail is
|
||||
// only updated at the end of each indexing operation. However, this delay is
|
||||
// considered acceptable.
|
||||
func (indexer *txIndexer) txIndexProgress() TxIndexProgress {
|
||||
return indexer.report(indexer.head.Load(), indexer.tail.Load())
|
||||
}
|
||||
|
||||
// close shutdown the indexer. Safe to be called for multiple times.
|
||||
|
|
|
|||
|
|
@ -121,9 +121,8 @@ func TestTxIndexer(t *testing.T) {
|
|||
|
||||
// Index the initial blocks from ancient store
|
||||
indexer := &txIndexer{
|
||||
limit: 0,
|
||||
db: db,
|
||||
progress: make(chan chan TxIndexProgress),
|
||||
limit: 0,
|
||||
db: db,
|
||||
}
|
||||
for i, limit := range c.limits {
|
||||
indexer.limit = limit
|
||||
|
|
@ -241,9 +240,8 @@ func TestTxIndexerRepair(t *testing.T) {
|
|||
|
||||
// Index the initial blocks from ancient store
|
||||
indexer := &txIndexer{
|
||||
limit: c.limit,
|
||||
db: db,
|
||||
progress: make(chan chan TxIndexProgress),
|
||||
limit: c.limit,
|
||||
db: db,
|
||||
}
|
||||
indexer.run(chainHead, make(chan struct{}), make(chan struct{}))
|
||||
|
||||
|
|
@ -432,15 +430,11 @@ func TestTxIndexerReport(t *testing.T) {
|
|||
|
||||
// Index the initial blocks from ancient store
|
||||
indexer := &txIndexer{
|
||||
limit: c.limit,
|
||||
cutoff: c.cutoff,
|
||||
db: db,
|
||||
progress: make(chan chan TxIndexProgress),
|
||||
limit: c.limit,
|
||||
cutoff: c.cutoff,
|
||||
db: db,
|
||||
}
|
||||
if c.tail != nil {
|
||||
rawdb.WriteTxIndexTail(db, *c.tail)
|
||||
}
|
||||
p := indexer.report(c.head)
|
||||
p := indexer.report(c.head, c.tail)
|
||||
if 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.
|
||||
func (pool *LegacyPool) truncatePending() {
|
||||
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 {
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
offenders := []common.Address{}
|
||||
for pending > pool.config.GlobalSlots && !spammers.Empty() {
|
||||
|
|
@ -1934,7 +1934,7 @@ func (pool *LegacyPool) Clear() {
|
|||
pool.reserver.Release(addr)
|
||||
}
|
||||
pool.all.Clear()
|
||||
pool.priced = newPricedList(pool.all)
|
||||
pool.priced.Reheap()
|
||||
pool.pending = make(map[common.Address]*list)
|
||||
pool.queue = make(map[common.Address]*list)
|
||||
pool.pendingNonces = newNoncer(pool.currentState)
|
||||
|
|
|
|||
|
|
@ -149,6 +149,17 @@ func VerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error {
|
|||
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.
|
||||
// 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) {
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ import (
|
|||
"errors"
|
||||
"sync"
|
||||
|
||||
gokzg4844 "github.com/crate-crypto/go-kzg-4844"
|
||||
ckzg4844 "github.com/ethereum/c-kzg-4844/bindings/go"
|
||||
gokzg4844 "github.com/crate-crypto/go-eth-kzg"
|
||||
ckzg4844 "github.com/ethereum/c-kzg-4844/v2/bindings/go"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
|
|
@ -47,15 +47,21 @@ func ckzgInit() {
|
|||
if err = gokzg4844.CheckTrustedSetupIsWellFormed(params); err != nil {
|
||||
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 {
|
||||
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))
|
||||
}
|
||||
g2s := make([]byte, len(params.SetupG2)*(len(params.SetupG2[0])-2)/2)
|
||||
for i, g2 := range params.SetupG2 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -125,3 +131,21 @@ func ckzgVerifyBlobProof(blob *Blob, commitment Commitment, proof Proof) error {
|
|||
}
|
||||
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 {
|
||||
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"
|
||||
"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.
|
||||
|
|
@ -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))
|
||||
}
|
||||
|
||||
// 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
|
||||
// with the given transaction hash.
|
||||
//
|
||||
// An error will be returned if the transaction is not found, and background
|
||||
// indexing for transactions is still in progress. The error is used to indicate the
|
||||
// scenario explicitly that the transaction might be reachable shortly.
|
||||
//
|
||||
// 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 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
|
||||
}
|
||||
// A null will be returned if the transaction is not found. The transaction is not
|
||||
// existent from the node's perspective. This can be due to the transaction indexer
|
||||
// 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) {
|
||||
lookup, tx := b.eth.blockchain.GetTransactionLookup(txHash)
|
||||
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) {
|
||||
|
|
@ -391,7 +389,7 @@ func (b *EthAPIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.S
|
|||
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()
|
||||
if txProg, err := b.eth.blockchain.TxIndexProgress(); err == nil {
|
||||
prog.TxIndexFinishedBlocks = txProg.Indexed
|
||||
|
|
|
|||
|
|
@ -373,8 +373,11 @@ func (api *ConsensusAPI) forkchoiceUpdated(update engine.ForkchoiceStateV1, payl
|
|||
}
|
||||
valid := func(id *engine.PayloadID) engine.ForkChoiceResponse {
|
||||
return engine.ForkChoiceResponse{
|
||||
PayloadStatus: engine.PayloadStatusV1{Status: engine.VALID, LatestValidHash: &update.HeadBlockHash},
|
||||
PayloadID: id,
|
||||
PayloadStatus: engine.PayloadStatusV1{
|
||||
Status: engine.VALID,
|
||||
LatestValidHash: &update.HeadBlockHash,
|
||||
},
|
||||
PayloadID: id,
|
||||
}
|
||||
}
|
||||
if rawdb.ReadCanonicalHash(api.eth.ChainDb(), block.NumberU64()) != update.HeadBlockHash {
|
||||
|
|
|
|||
|
|
@ -69,6 +69,9 @@ const (
|
|||
// txGatherSlack is the interval used to collate almost-expired announces
|
||||
// with network fetches.
|
||||
txGatherSlack = 100 * time.Millisecond
|
||||
|
||||
// addTxsBatchSize it the max number of transactions to add in a single batch from a peer.
|
||||
addTxsBatchSize = 128
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -329,8 +332,8 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool)
|
|||
metas = make([]txMetadata, 0, len(txs))
|
||||
)
|
||||
// proceed in batches
|
||||
for i := 0; i < len(txs); i += 128 {
|
||||
end := i + 128
|
||||
for i := 0; i < len(txs); i += addTxsBatchSize {
|
||||
end := i + addTxsBatchSize
|
||||
if end > len(txs) {
|
||||
end = len(txs)
|
||||
}
|
||||
|
|
@ -372,7 +375,7 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool)
|
|||
otherRejectMeter.Mark(otherreject)
|
||||
|
||||
// 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)
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
var (
|
||||
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
|
||||
// with gasLimit set to the first execution's usedGas + gasRefund. Explicitly
|
||||
// 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 {
|
||||
failed, _, err = execute(ctx, call, opts, optimisticGasLimit)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -82,7 +82,8 @@ type Backend interface {
|
|||
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
||||
BlockByHash(ctx context.Context, hash common.Hash) (*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
|
||||
ChainConfig() *params.ChainConfig
|
||||
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
|
||||
chainConfig, canon = overrideConfig(chainConfig, config.Overrides)
|
||||
}
|
||||
|
||||
evm := vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{})
|
||||
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
|
||||
core.ProcessBeaconBlockRoot(*beaconRoot, evm)
|
||||
|
|
@ -786,42 +788,45 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block
|
|||
}
|
||||
for i, tx := range block.Transactions() {
|
||||
// Prepare the transaction for un-traced execution
|
||||
var (
|
||||
msg, _ = core.TransactionToMessage(tx, signer, block.BaseFee())
|
||||
vmConf vm.Config
|
||||
dump *os.File
|
||||
writer *bufio.Writer
|
||||
err error
|
||||
)
|
||||
// If the transaction needs tracing, swap out the configs
|
||||
if tx.Hash() == txHash || txHash == (common.Hash{}) {
|
||||
// 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])
|
||||
if !canon {
|
||||
prefix = fmt.Sprintf("%valt-", prefix)
|
||||
}
|
||||
dump, err = os.CreateTemp(os.TempDir(), prefix)
|
||||
msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee())
|
||||
if txHash != (common.Hash{}) && tx.Hash() != txHash {
|
||||
// Process the tx to update state, but don't trace it.
|
||||
_, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dumps = append(dumps, dump.Name())
|
||||
|
||||
// Swap out the noop logger to the standard tracer
|
||||
writer = bufio.NewWriter(dump)
|
||||
vmConf = vm.Config{
|
||||
Tracer: logger.NewJSONLogger(&logConfig, writer),
|
||||
EnablePreimageRecording: true,
|
||||
return dumps, err
|
||||
}
|
||||
// Finalize the state so any modifications are written to the trie
|
||||
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
||||
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])
|
||||
if !canon {
|
||||
prefix = fmt.Sprintf("%valt-", prefix)
|
||||
}
|
||||
var dump *os.File
|
||||
dump, err := os.CreateTemp(os.TempDir(), prefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dumps = append(dumps, dump.Name())
|
||||
// Set up the tracer and EVM for the transaction.
|
||||
var (
|
||||
writer = bufio.NewWriter(dump)
|
||||
tracer = logger.NewJSONLogger(&logConfig, writer)
|
||||
evm = vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{
|
||||
Tracer: tracer,
|
||||
NoBaseFee: true,
|
||||
})
|
||||
)
|
||||
// Execute the transaction and flush any traces to disk
|
||||
statedb.SetTxContext(tx.Hash(), i)
|
||||
if vmConf.Tracer.OnTxStart != nil {
|
||||
vmConf.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)
|
||||
if tracer.OnTxStart != nil {
|
||||
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
|
||||
}
|
||||
_, err = core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit))
|
||||
if writer != nil {
|
||||
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
|
||||
// and returns them as a JSON object.
|
||||
func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config *TraceConfig) (interface{}, error) {
|
||||
found, _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash)
|
||||
if err != nil {
|
||||
return nil, ethapi.NewTxIndexingError()
|
||||
}
|
||||
// Only mined txes are supported
|
||||
found, _, blockHash, blockNumber, index := api.backend.GetTransaction(hash)
|
||||
if !found {
|
||||
// Warn in case tx indexer is not done.
|
||||
if !api.backend.TxIndexDone() {
|
||||
return nil, ethapi.NewTxIndexingError()
|
||||
}
|
||||
// Only mined txes are supported
|
||||
return nil, errTxNotFound
|
||||
}
|
||||
// It shouldn't happen in practice.
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sync/atomic"
|
||||
|
|
@ -116,9 +117,13 @@ func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber)
|
|||
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)
|
||||
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 {
|
||||
|
|
@ -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 {
|
||||
Hash common.Hash `json:"hash"`
|
||||
Hash *common.Hash `json:"hash"`
|
||||
Transactions []rpcTransaction `json:"transactions"`
|
||||
UncleHashes []common.Hash `json:"uncles"`
|
||||
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 {
|
||||
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.
|
||||
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")
|
||||
|
|
@ -199,7 +205,7 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface
|
|||
txs := make([]*types.Transaction, len(body.Transactions))
|
||||
for i, tx := range body.Transactions {
|
||||
if tx.From != nil {
|
||||
setSenderFromServer(tx.tx, *tx.From, body.Hash)
|
||||
setSenderFromServer(tx.tx, *tx.From, *body.Hash)
|
||||
}
|
||||
txs[i] = tx.tx
|
||||
}
|
||||
|
|
|
|||
|
|
@ -307,6 +307,12 @@ func testTransactionInBlock(t *testing.T, client *rpc.Client) {
|
|||
if tx.Hash() != testTx2.Hash() {
|
||||
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) {
|
||||
|
|
@ -619,6 +625,21 @@ func testAtFunctions(t *testing.T, client *rpc.Client) {
|
|||
if gas != 21000 {
|
||||
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) {
|
||||
|
|
@ -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
|
||||
// TransactionSender. Ensure the server is not asked by canceling the context here.
|
||||
canceledCtx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
<-canceledCtx.Done() // Ensure the close of the Done channel
|
||||
sender1, err := ec.TransactionSender(canceledCtx, tx1, block2.Hash(), 0)
|
||||
sender1, err := ec.TransactionSender(newCanceledContext(), tx1, block2.Hash(), 0)
|
||||
if err != nil {
|
||||
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 {
|
||||
chainID, err := ec.ChainID(context.Background())
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ type backend interface {
|
|||
CurrentHeader() *types.Header
|
||||
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
|
||||
Stats() (pending int, queued int)
|
||||
SyncProgress() ethereum.SyncProgress
|
||||
SyncProgress(ctx context.Context) ethereum.SyncProgress
|
||||
}
|
||||
|
||||
// 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
|
||||
if fullBackend, ok := s.backend.(fullNodeBackend); ok {
|
||||
sync := fullBackend.SyncProgress()
|
||||
sync := fullBackend.SyncProgress(context.Background())
|
||||
syncing = !sync.Done()
|
||||
|
||||
price, _ := fullBackend.SuggestGasTipCap(context.Background())
|
||||
|
|
@ -775,7 +775,7 @@ func (s *Service) reportStats(conn *connWrapper) error {
|
|||
gasprice += int(basefee.Uint64())
|
||||
}
|
||||
} else {
|
||||
sync := s.backend.SyncProgress()
|
||||
sync := s.backend.SyncProgress(context.Background())
|
||||
syncing = !sync.Done()
|
||||
}
|
||||
// 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/cloudflare/cloudflare-go v0.114.0
|
||||
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-kzg-4844 v1.1.0
|
||||
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/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0
|
||||
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/fatih/color v1.16.0
|
||||
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/smithy-go v1.15.0 // 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/cockroachdb/errors v1.11.3 // indirect
|
||||
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
||||
github.com/cockroachdb/redact v1.1.5 // 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/deepmap/oapi-codegen v1.6.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.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
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.17.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bits-and-blooms/bitset v1.20.0 h1:2F+rfL86jE2d/bmw7OhqUg2Sj/1rURkBn3MdfoPyRVU=
|
||||
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/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
|
||||
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/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/consensys/bavard v0.1.22 h1:Uw2CGvbXSZWhqK59X0VG/zOjpTFuOMcPLStrp1ihI0A=
|
||||
github.com/consensys/bavard v0.1.22/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs=
|
||||
github.com/consensys/gnark-crypto v0.14.0 h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E=
|
||||
github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCeWHHrb1li/n1XoU0=
|
||||
github.com/consensys/bavard v0.1.27 h1:j6hKUrGAy/H+gpNrpLU3I26n1yc+VMGmd6ID5+gAhOs=
|
||||
github.com/consensys/bavard v0.1.27/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs=
|
||||
github.com/consensys/gnark-crypto v0.16.0 h1:8Dl4eYmUWK9WmlP1Bj6je688gBRJCJbT8Mw4KoTAawo=
|
||||
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/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/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM=
|
||||
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.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
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 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
|
||||
github.com/ethereum/c-kzg-4844/v2 v2.1.0 h1:gQropX9YFBhl3g4HYhwE70zq3IHFRgbbNPw0Shwzf5w=
|
||||
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/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
|
||||
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
|
||||
}
|
||||
// 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 {
|
||||
t.tx = tx
|
||||
blockNrOrHash := rpc.BlockNumberOrHashWithHash(blockHash, false)
|
||||
|
|
@ -1530,8 +1530,8 @@ func (s *SyncState) TxIndexRemainingBlocks() hexutil.Uint64 {
|
|||
// - healingBytecode: number of bytecodes pending
|
||||
// - txIndexFinishedBlocks: number of blocks whose transactions are indexed
|
||||
// - txIndexRemainingBlocks: number of blocks whose transactions are not indexed yet
|
||||
func (r *Resolver) Syncing() (*SyncState, error) {
|
||||
progress := r.backend.SyncProgress()
|
||||
func (r *Resolver) Syncing(ctx context.Context) (*SyncState, error) {
|
||||
progress := r.backend.SyncProgress(ctx)
|
||||
|
||||
// Return not syncing if the synchronisation already completed
|
||||
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
|
||||
// - pulledStates: number of state entries processed until now
|
||||
// - knownStates: number of known state entries that still need to be pulled
|
||||
func (api *EthereumAPI) Syncing() (interface{}, error) {
|
||||
progress := api.b.SyncProgress()
|
||||
func (api *EthereumAPI) Syncing(ctx context.Context) (interface{}, error) {
|
||||
progress := api.b.SyncProgress(ctx)
|
||||
|
||||
// Return not syncing if the synchronisation already completed
|
||||
if progress.Done() {
|
||||
|
|
@ -1333,16 +1333,18 @@ func (api *TransactionAPI) GetTransactionCount(ctx context.Context, address comm
|
|||
// GetTransactionByHash returns the transaction for the given hash
|
||||
func (api *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) {
|
||||
// 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 {
|
||||
// No finalized transaction, try to retrieve it from the pool
|
||||
if tx := api.b.GetPoolTransaction(hash); tx != nil {
|
||||
return NewRPCPendingTransaction(tx, api.b.CurrentHeader(), api.b.ChainConfig()), nil
|
||||
}
|
||||
if err == nil {
|
||||
return nil, nil
|
||||
// If also not in the pool there is a chance the tx indexer is still in progress.
|
||||
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)
|
||||
if err != nil {
|
||||
|
|
@ -1354,27 +1356,31 @@ func (api *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common
|
|||
// GetRawTransactionByHash returns the bytes of the transaction for the given hash.
|
||||
func (api *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
|
||||
// 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 tx = api.b.GetPoolTransaction(hash); tx != nil {
|
||||
return tx.MarshalBinary()
|
||||
}
|
||||
if err == nil {
|
||||
return nil, nil
|
||||
// If also not in the pool there is a chance the tx indexer is still in progress.
|
||||
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()
|
||||
}
|
||||
|
||||
// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, NewTxIndexingError() // transaction is not fully indexed
|
||||
}
|
||||
found, tx, blockHash, blockNumber, index := api.b.GetTransaction(hash)
|
||||
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)
|
||||
if err != nil {
|
||||
|
|
@ -1774,15 +1780,17 @@ func (api *DebugAPI) GetRawReceipts(ctx context.Context, blockNrOrHash rpc.Block
|
|||
// GetRawTransaction returns the bytes of the transaction for the given hash.
|
||||
func (api *DebugAPI) GetRawTransaction(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
|
||||
// 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 tx = api.b.GetPoolTransaction(hash); tx != nil {
|
||||
return tx.MarshalBinary()
|
||||
}
|
||||
if err == nil {
|
||||
return nil, nil
|
||||
// If also not in the pool there is a chance the tx indexer is still in progress.
|
||||
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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -472,7 +472,9 @@ func (b *testBackend) setPendingBlock(block *types.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) {
|
||||
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 {
|
||||
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)
|
||||
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) 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")
|
||||
}
|
||||
|
||||
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) {
|
||||
t.Parallel()
|
||||
// Initialize test accounts
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ import (
|
|||
// both full and light clients) with access to necessary functions.
|
||||
type Backend interface {
|
||||
// General Ethereum API
|
||||
SyncProgress() ethereum.SyncProgress
|
||||
SyncProgress(ctx context.Context) ethereum.SyncProgress
|
||||
|
||||
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)
|
||||
|
|
@ -74,7 +74,8 @@ type Backend interface {
|
|||
|
||||
// Transaction pool API
|
||||
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)
|
||||
GetPoolTransaction(txHash common.Hash) *types.Transaction
|
||||
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
|
||||
|
|
|
|||
|
|
@ -78,11 +78,25 @@ type simBlockResult struct {
|
|||
chainConfig *params.ChainConfig
|
||||
Block *types.Block
|
||||
Calls []simCallResult
|
||||
// senders is a map of transaction hashes to their senders.
|
||||
senders map[common.Hash]common.Address
|
||||
}
|
||||
|
||||
func (r *simBlockResult) MarshalJSON() ([]byte, error) {
|
||||
blockData := RPCMarshalBlock(r.Block, true, r.fullTx, r.chainConfig)
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
@ -181,18 +195,18 @@ func (sim *simulator) execute(ctx context.Context, blocks []simBlock) ([]*simBlo
|
|||
parent = sim.base
|
||||
)
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
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()
|
||||
}
|
||||
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.
|
||||
// Parent hash is needed for evm.GetHashFn to work.
|
||||
header.ParentHash = parent.Hash()
|
||||
|
|
@ -222,7 +236,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
|||
precompiles := sim.activePrecompiles(sim.base)
|
||||
// State overrides are applied prior to execution of a block
|
||||
if err := block.StateOverrides.Apply(sim.state, precompiles); err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
var (
|
||||
gasUsed, blobGasUsed uint64
|
||||
|
|
@ -235,6 +249,10 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
|||
NoBaseFee: !sim.validate,
|
||||
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)
|
||||
if hooks := tracer.Hooks(); hooks != nil {
|
||||
|
|
@ -255,16 +273,17 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
|||
var allLogs []*types.Log
|
||||
for i, call := range block.Calls {
|
||||
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 {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
var (
|
||||
tx = call.ToTransaction(types.DynamicFeeTxType)
|
||||
txHash = tx.Hash()
|
||||
)
|
||||
txes[i] = tx
|
||||
senders[txHash] = call.from()
|
||||
tracer.reset(txHash, uint(i))
|
||||
sim.state.SetTxContext(txHash, i)
|
||||
// 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)
|
||||
if err != nil {
|
||||
txErr := txValidationError(err)
|
||||
return nil, nil, txErr
|
||||
return nil, nil, nil, txErr
|
||||
}
|
||||
// Update the state with pending changes.
|
||||
var root []byte
|
||||
|
|
@ -311,15 +330,15 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
|||
requests = [][]byte{}
|
||||
// EIP-6110
|
||||
if err := core.ParseDepositLogs(&requests, allLogs, sim.chainConfig); err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
// EIP-7002
|
||||
if err := core.ProcessWithdrawalQueue(&requests, evm); err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
// EIP-7251
|
||||
if err := core.ProcessConsolidationQueue(&requests, evm); err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
if requests != nil {
|
||||
|
|
@ -330,10 +349,10 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
|
|||
chainHeadReader := &simChainHeadReader{ctx, sim.b}
|
||||
b, err := sim.b.Engine().FinalizeAndAssemble(chainHeadReader, header, sim.state, blockBody, receipts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
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
|
||||
|
|
|
|||
|
|
@ -323,7 +323,9 @@ func (b *backendMock) CurrentHeader() *types.Header { return b.current }
|
|||
func (b *backendMock) ChainConfig() *params.ChainConfig { return b.config }
|
||||
|
||||
// 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) {
|
||||
return nil, nil, nil, nil, nil, nil, nil
|
||||
}
|
||||
|
|
@ -378,9 +380,10 @@ func (b *backendMock) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) eve
|
|||
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) {
|
||||
return false, nil, [32]byte{}, 0, 0, nil
|
||||
func (b *backendMock) GetTransaction(txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64) {
|
||||
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) GetPoolTransaction(txHash common.Hash) *types.Transaction { return nil }
|
||||
func (b *backendMock) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
|
||||
|
|
|
|||
|
|
@ -570,7 +570,6 @@ func TestHTTPWriteTimeout(t *testing.T) {
|
|||
// Send normal request
|
||||
t.Run("message", func(t *testing.T) {
|
||||
resp := rpcRequest(t, url, "test_sleep")
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -584,7 +583,6 @@ func TestHTTPWriteTimeout(t *testing.T) {
|
|||
t.Run("batch", func(t *testing.T) {
|
||||
want := fmt.Sprintf("[%s,%s,%s]", greetRes, timeoutRes, timeoutRes)
|
||||
resp := batchRpcRequest(t, url, []string{"test_greet", "test_sleep", "test_greet"})
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
|||
|
|
@ -358,7 +358,7 @@ var (
|
|||
Max: 9,
|
||||
UpdateFraction: 5007716,
|
||||
}
|
||||
// DefaultBlobSchedule is the latest configured blob schedule for test chains.
|
||||
// DefaultBlobSchedule is the latest configured blob schedule for Ethereum mainnet.
|
||||
DefaultBlobSchedule = &BlobScheduleConfig{
|
||||
Cancun: DefaultCancunBlobConfig,
|
||||
Prague: DefaultPragueBlobConfig,
|
||||
|
|
|
|||
|
|
@ -501,6 +501,10 @@ func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage
|
|||
if msg.isUnsubscribe() {
|
||||
callb = h.unsubscribeCb
|
||||
} 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)
|
||||
}
|
||||
if callb == nil {
|
||||
|
|
@ -536,6 +540,11 @@ func (h *handler) handleSubscribe(cp *callProc, msg *jsonrpcMessage) *jsonrpcMes
|
|||
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.
|
||||
name, err := parseSubscriptionName(msg.Params)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ const (
|
|||
subscribeMethodSuffix = "_subscribe"
|
||||
unsubscribeMethodSuffix = "_unsubscribe"
|
||||
notificationMethodSuffix = "_subscription"
|
||||
maxMethodNameLength = 2048
|
||||
|
||||
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,10 +485,18 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, keys [][]byte, valu
|
|||
if 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++ {
|
||||
if i < len(keys)-1 && bytes.Compare(keys[i], keys[i+1]) >= 0 {
|
||||
return false, errors.New("range is not monotonically increasing")
|
||||
if i < len(keys)-1 {
|
||||
if bytes.Compare(keys[i], keys[i+1]) >= 0 {
|
||||
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 {
|
||||
return false, errors.New("range contains deletion")
|
||||
|
|
|
|||
|
|
@ -1000,3 +1000,35 @@ func TestRangeProofKeysWithSharedPrefix(t *testing.T) {
|
|||
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 (
|
||||
Major = 1 // Major 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
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue