diff --git a/.github/workflows/publish_docker.yml b/.github/workflows/publish_docker.yml
index 009909c1ba..5a16349cf5 100644
--- a/.github/workflows/publish_docker.yml
+++ b/.github/workflows/publish_docker.yml
@@ -15,7 +15,6 @@ on:
branches:
- master
-
jobs:
publish_docker_image:
name: Publish Docker Image
diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go
index 785e0a4eb9..1a6e224090 100644
--- a/accounts/abi/abi.go
+++ b/accounts/abi/abi.go
@@ -39,7 +39,7 @@ import (
)
// The ABI holds information about a contract's context and available
-// invokable methods. It will allow you to type check function calls and
+// invocable methods. It will allow you to type check function calls and
// packs data accordingly.
type ABI struct {
Constructor Method
diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go
index 298723dfd2..b95a05532d 100644
--- a/accounts/abi/bind/backend.go
+++ b/accounts/abi/bind/backend.go
@@ -94,6 +94,11 @@ type BlockHashContractCaller interface {
// used when the user does not provide some needed values, but rather leaves it up
// to the transactor to decide.
type ContractTransactor interface {
+ interfaces.GasEstimator
+ interfaces.GasPricer
+ interfaces.GasPricer1559
+ interfaces.TransactionSender
+
// HeaderByNumber returns a block header from the current canonical chain. If
// number is nil, the latest known header is returned.
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
@@ -101,40 +106,8 @@ type ContractTransactor interface {
// AcceptedCodeAt returns the code of the given account in the accepted state.
AcceptedCodeAt(ctx context.Context, account common.Address) ([]byte, error)
- // AcceptedNonceAt retrieves the current accepted nonce associated with an account.
- AcceptedNonceAt(ctx context.Context, account common.Address) (uint64, error)
-
- // SuggestGasPrice retrieves the currently suggested gas price to allow a timely
- // execution of a transaction.
- SuggestGasPrice(ctx context.Context) (*big.Int, error)
-
- // SuggestGasTipCap retrieves the currently suggested 1559 priority fee to allow
- // a timely execution of a transaction.
- SuggestGasTipCap(ctx context.Context) (*big.Int, error)
-
- // EstimateGas tries to estimate the gas needed to execute a specific
- // transaction based on the current pending state of the backend blockchain.
- // There is no guarantee that this is the true gas limit requirement as other
- // transactions may be added or removed by miners, but it should provide a basis
- // for setting a reasonable default.
- EstimateGas(ctx context.Context, call interfaces.CallMsg) (gas uint64, err error)
-
- // SendTransaction injects the transaction into the pending pool for execution.
- SendTransaction(ctx context.Context, tx *types.Transaction) error
-}
-
-// ContractFilterer defines the methods needed to access log events using one-off
-// queries or continuous event subscriptions.
-type ContractFilterer interface {
- // FilterLogs executes a log filter operation, blocking during execution and
- // returning all the results in one batch.
- //
- // TODO(karalabe): Deprecate when the subscription one can return past data too.
- FilterLogs(ctx context.Context, query interfaces.FilterQuery) ([]types.Log, error)
-
- // SubscribeFilterLogs creates a background log filtering operation, returning
- // a subscription immediately, which can be used to stream the found events.
- SubscribeFilterLogs(ctx context.Context, query interfaces.FilterQuery, ch chan<- types.Log) (interfaces.Subscription, error)
+ // NonceAt retrieves the nonce associated with an account.
+ NonceAt(ctx context.Context, account common.Address, blockNum *big.Int) (uint64, error)
}
// DeployBackend wraps the operations needed by WaitMined and WaitDeployed.
@@ -143,6 +116,12 @@ type DeployBackend interface {
CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error)
}
+// ContractFilterer defines the methods needed to access log events using one-off
+// queries or continuous event subscriptions.
+type ContractFilterer interface {
+ interfaces.LogFilterer
+}
+
// ContractBackend defines the methods needed to work with contracts on a read-write basis.
type ContractBackend interface {
ContractCaller
diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index ee15adcb42..141f30f9c8 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -28,34 +28,12 @@ package backends
import (
"context"
- "errors"
- "fmt"
- "math/big"
- "sync"
- "time"
- "github.com/ava-labs/subnet-evm/eth"
- "github.com/ava-labs/subnet-evm/vmerrs"
-
- "github.com/ava-labs/subnet-evm/accounts/abi"
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
- "github.com/ava-labs/subnet-evm/consensus/dummy"
- "github.com/ava-labs/subnet-evm/core"
- "github.com/ava-labs/subnet-evm/core/bloombits"
- "github.com/ava-labs/subnet-evm/core/rawdb"
- "github.com/ava-labs/subnet-evm/core/state"
"github.com/ava-labs/subnet-evm/core/types"
- "github.com/ava-labs/subnet-evm/core/vm"
- "github.com/ava-labs/subnet-evm/eth/filters"
+ "github.com/ava-labs/subnet-evm/ethclient/simulated"
"github.com/ava-labs/subnet-evm/interfaces"
- "github.com/ava-labs/subnet-evm/params"
- "github.com/ava-labs/subnet-evm/rpc"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/common/math"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/event"
- "github.com/ethereum/go-ethereum/log"
)
// Verify that SimulatedBackend implements required interfaces
@@ -76,953 +54,29 @@ var (
_ interfaces.AcceptedContractCaller = (*SimulatedBackend)(nil)
)
-var (
- errBlockNumberUnsupported = errors.New("simulatedBackend cannot access blocks other than the latest block")
- errBlockHashUnsupported = errors.New("simulatedBackend cannot access blocks by hash other than the latest block")
- errBlockDoesNotExist = errors.New("block does not exist in blockchain")
- errTransactionDoesNotExist = errors.New("transaction does not exist")
-)
-
-// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
-// the background. Its main purpose is to allow for easy testing of contract bindings.
-// Simulated backend implements the following interfaces:
-// ChainReader, ChainStateReader, ContractBackend, ContractCaller, ContractFilterer, ContractTransactor,
-// DeployBackend, GasEstimator, GasPricer, LogFilterer, AcceptedContractCaller, TransactionReader, and TransactionSender
+// SimulatedBackend is a simulated blockchain.
+// Deprecated: use package github.com/ava-labs/subnet-evm/ethclient/simulated instead.
type SimulatedBackend struct {
- database ethdb.Database // In memory database to store our testing data
- blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
-
- mu sync.Mutex
- acceptedBlock *types.Block // Currently accepted block that will be imported on request
- acceptedState *state.StateDB // Currently accepted state that will be the active on request
-
- events *filters.EventSystem // for filtering log events live
- filterSystem *filters.FilterSystem // for filtering database logs
-
- config *params.ChainConfig
+ *simulated.Backend
+ simulated.Client
}
-// NewSimulatedBackendWithDatabase creates a new binding backend based on the given database
-// and uses a simulated blockchain for testing purposes.
-// A simulated backend always uses chainID 1337.
-func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
- copyConfig := *params.TestChainConfig
- copyConfig.ChainID = big.NewInt(1337)
- genesis := core.Genesis{
- Config: ©Config,
- GasLimit: gasLimit,
- Alloc: alloc,
- }
- cacheConfig := &core.CacheConfig{}
- blockchain, _ := core.NewBlockChain(database, cacheConfig, &genesis, dummy.NewCoinbaseFaker(), vm.Config{}, common.Hash{}, false)
-
- backend := &SimulatedBackend{
- database: database,
- blockchain: blockchain,
- config: genesis.Config,
- }
-
- filterBackend := &filterBackend{database, blockchain, backend}
- backend.filterSystem = filters.NewFilterSystem(filterBackend, filters.Config{})
- backend.events = filters.NewEventSystem(backend.filterSystem)
-
- header := backend.blockchain.CurrentBlock()
- block := backend.blockchain.GetBlock(header.Hash(), header.Number.Uint64())
-
- backend.rollback(block)
- return backend
+// Fork sets the head to a new block, which is based on the provided parentHash.
+func (b *SimulatedBackend) Fork(ctx context.Context, parentHash common.Hash) error {
+ return b.Backend.Fork(parentHash)
}
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
// for testing purposes.
-// A simulated backend always uses chainID 1337.
-func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
- return NewSimulatedBackendWithDatabase(rawdb.NewMemoryDatabase(), alloc, gasLimit)
-}
-
-// Close terminates the underlying blockchain's update loop.
-func (b *SimulatedBackend) Close() error {
- b.blockchain.Stop()
- return nil
-}
-
-// Commit imports all the accepted transactions as a single block and starts a
-// fresh new state.
-func (b *SimulatedBackend) Commit(accept bool) common.Hash {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- if _, err := b.blockchain.InsertChain([]*types.Block{b.acceptedBlock}); err != nil {
- panic(err) // This cannot happen unless the simulator is wrong, fail in that case
- }
- if accept {
- if err := b.blockchain.Accept(b.acceptedBlock); err != nil {
- panic(err)
- }
- b.blockchain.DrainAcceptorQueue()
- }
- blockHash := b.acceptedBlock.Hash()
-
- // Using the last inserted block here makes it possible to build on a side
- // chain after a fork.
- b.rollback(b.acceptedBlock)
-
- return blockHash
-}
-
-// Rollback aborts all accepted transactions, reverting to the last committed state.
-func (b *SimulatedBackend) Rollback() {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- header := b.blockchain.CurrentBlock()
- block := b.blockchain.GetBlock(header.Hash(), header.Number.Uint64())
-
- b.rollback(block)
-}
-
-func (b *SimulatedBackend) rollback(parent *types.Block) {
- blocks, _, _ := core.GenerateChain(b.config, parent, dummy.NewFaker(), b.database, 1, 10, func(int, *core.BlockGen) {})
-
- b.acceptedBlock = blocks[0]
- b.acceptedState, _ = state.New(b.acceptedBlock.Root(), b.blockchain.StateCache(), nil)
-}
-
-// Fork creates a side-chain that can be used to simulate reorgs.
//
-// This function should be called with the ancestor block where the new side
-// chain should be started. Transactions (old and new) can then be applied on
-// top and Commit-ed.
-//
-// Note, the side-chain will only become canonical (and trigger the events) when
-// it becomes longer. Until then CallContract will still operate on the current
-// canonical chain.
-//
-// There is a % chance that the side chain becomes canonical at the same length
-// to simulate live network behavior.
-func (b *SimulatedBackend) Fork(ctx context.Context, parent common.Hash) error {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- if len(b.acceptedBlock.Transactions()) != 0 {
- return errors.New("accepted block dirty")
- }
- block, err := b.blockByHash(ctx, parent)
- if err != nil {
- return err
- }
- b.rollback(block)
- return nil
-}
-
-// stateByBlockNumber retrieves a state by a given blocknumber.
-func (b *SimulatedBackend) stateByBlockNumber(ctx context.Context, blockNumber *big.Int) (*state.StateDB, error) {
- if blockNumber == nil || blockNumber.Cmp(b.blockchain.CurrentBlock().Number) == 0 {
- return b.blockchain.State()
- }
- block, err := b.blockByNumber(ctx, blockNumber)
- if err != nil {
- return nil, err
- }
- return b.blockchain.StateAt(block.Root())
-}
-
-// CodeAt returns the code associated with a certain account in the blockchain.
-func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
- if err != nil {
- return nil, err
- }
- return stateDB.GetCode(contract), nil
-}
-
-// CodeAtHash returns the code associated with a certain account in the blockchain.
-func (b *SimulatedBackend) CodeAtHash(ctx context.Context, contract common.Address, blockHash common.Hash) ([]byte, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- header, err := b.headerByHash(blockHash)
- if err != nil {
- return nil, err
- }
-
- stateDB, err := b.blockchain.StateAt(header.Root)
- if err != nil {
- return nil, err
- }
-
- return stateDB.GetCode(contract), nil
-}
-
-// BalanceAt returns the wei balance of a certain account in the blockchain.
-func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (*big.Int, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
- if err != nil {
- return nil, err
- }
- return stateDB.GetBalance(contract), nil
-}
-
-// NonceAt returns the nonce of a certain account in the blockchain.
-func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (uint64, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
- if err != nil {
- return 0, err
- }
- return stateDB.GetNonce(contract), nil
-}
-
-// StorageAt returns the value of key in the storage of an account in the blockchain.
-func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
- if err != nil {
- return nil, err
- }
- val := stateDB.GetState(contract, key)
- return val[:], nil
-}
-
-// TransactionReceipt returns the receipt of a transaction.
-func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- receipt, _, _, _ := rawdb.ReadReceipt(b.database, txHash, b.config)
- if receipt == nil {
- return nil, interfaces.NotFound
- }
- return receipt, nil
-}
-
-// TransactionByHash checks the pool of accepted transactions in addition to the
-// blockchain. The isAccepted return value indicates whether the transaction has been
-// mined yet. Note that the transaction may not be part of the canonical chain even if
-// it's not accepted.
-func (b *SimulatedBackend) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- tx := b.acceptedBlock.Transaction(txHash)
- if tx != nil {
- return tx, true, nil
- }
- tx, _, _, _ = rawdb.ReadTransaction(b.database, txHash)
- if tx != nil {
- return tx, false, nil
- }
- return nil, false, interfaces.NotFound
-}
-
-// BlockByHash retrieves a block based on the block hash.
-func (b *SimulatedBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- return b.blockByHash(ctx, hash)
-}
-
-// blockByHash retrieves a block based on the block hash without Locking.
-func (b *SimulatedBackend) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
- if hash == b.acceptedBlock.Hash() {
- return b.acceptedBlock, nil
- }
-
- block := b.blockchain.GetBlockByHash(hash)
- if block != nil {
- return block, nil
- }
-
- return nil, errBlockDoesNotExist
-}
-
-// BlockByNumber retrieves a block from the database by number, caching it
-// (associated with its hash) if found.
-func (b *SimulatedBackend) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- return b.blockByNumber(ctx, number)
-}
-
-// blockByNumber retrieves a block from the database by number, caching it
-// (associated with its hash) if found without Lock.
-func (b *SimulatedBackend) blockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
- if number == nil || number.Cmp(b.acceptedBlock.Number()) == 0 {
- return b.blockByHash(ctx, b.blockchain.CurrentBlock().Hash())
- }
-
- block := b.blockchain.GetBlockByNumber(uint64(number.Int64()))
- if block == nil {
- return nil, errBlockDoesNotExist
- }
-
- return block, nil
-}
-
-// HeaderByHash returns a block header from the current canonical chain.
-func (b *SimulatedBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
- return b.headerByHash(hash)
-}
-
-// headerByHash retrieves a header from the database by hash without Lock.
-func (b *SimulatedBackend) headerByHash(hash common.Hash) (*types.Header, error) {
- if hash == b.acceptedBlock.Hash() {
- return b.acceptedBlock.Header(), nil
- }
-
- header := b.blockchain.GetHeaderByHash(hash)
- if header == nil {
- return nil, errBlockDoesNotExist
- }
-
- return header, nil
-}
-
-// HeaderByNumber returns a block header from the current canonical chain. If number is
-// nil, the latest known header is returned.
-func (b *SimulatedBackend) HeaderByNumber(ctx context.Context, block *big.Int) (*types.Header, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- if block == nil || block.Cmp(b.acceptedBlock.Number()) == 0 {
- return b.blockchain.CurrentHeader(), nil
- }
-
- return b.blockchain.GetHeaderByNumber(uint64(block.Int64())), nil
-}
-
-// TransactionCount returns the number of transactions in a given block.
-func (b *SimulatedBackend) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- if blockHash == b.acceptedBlock.Hash() {
- return uint(b.acceptedBlock.Transactions().Len()), nil
- }
-
- block := b.blockchain.GetBlockByHash(blockHash)
- if block == nil {
- return uint(0), errBlockDoesNotExist
- }
-
- return uint(block.Transactions().Len()), nil
-}
-
-// TransactionInBlock returns the transaction for a specific block at a specific index.
-func (b *SimulatedBackend) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- if blockHash == b.acceptedBlock.Hash() {
- transactions := b.acceptedBlock.Transactions()
- if uint(len(transactions)) < index+1 {
- return nil, errTransactionDoesNotExist
- }
-
- return transactions[index], nil
- }
-
- block := b.blockchain.GetBlockByHash(blockHash)
- if block == nil {
- return nil, errBlockDoesNotExist
- }
-
- transactions := block.Transactions()
- if uint(len(transactions)) < index+1 {
- return nil, errTransactionDoesNotExist
- }
-
- return transactions[index], nil
-}
-
-// AcceptedCodeAt returns the code associated with an account in the accepted state.
-func (b *SimulatedBackend) AcceptedCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- return b.acceptedState.GetCode(contract), nil
-}
-
-func newRevertError(result *core.ExecutionResult) *revertError {
- reason, errUnpack := abi.UnpackRevert(result.Revert())
- err := errors.New("execution reverted")
- if errUnpack == nil {
- err = fmt.Errorf("execution reverted: %v", reason)
- }
- return &revertError{
- error: err,
- reason: hexutil.Encode(result.Revert()),
- }
-}
-
-// revertError is an API error that encompasses an EVM revert with JSON error
-// code and a binary data blob.
-type revertError struct {
- error
- reason string // revert reason hex encoded
-}
-
-// ErrorCode returns the JSON error code for a revert.
-// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal
-func (e *revertError) ErrorCode() int {
- return 3
-}
-
-// ErrorData returns the hex encoded revert reason.
-func (e *revertError) ErrorData() interface{} {
- return e.reason
-}
-
-// CallContract executes a contract call.
-func (b *SimulatedBackend) CallContract(ctx context.Context, call interfaces.CallMsg, blockNumber *big.Int) ([]byte, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number) != 0 {
- return nil, errBlockNumberUnsupported
- }
- return b.callContractAtHead(ctx, call)
-}
-
-// CallContractAtHash executes a contract call on a specific block hash.
-func (b *SimulatedBackend) CallContractAtHash(ctx context.Context, call interfaces.CallMsg, blockHash common.Hash) ([]byte, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- if blockHash != b.blockchain.CurrentBlock().Hash() {
- return nil, errBlockHashUnsupported
- }
- return b.callContractAtHead(ctx, call)
-}
-
-// callContractAtHead executes a contract call against the latest block state.
-func (b *SimulatedBackend) callContractAtHead(ctx context.Context, call interfaces.CallMsg) ([]byte, error) {
- stateDB, err := b.blockchain.State()
- if err != nil {
- return nil, err
- }
- res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), stateDB)
- if err != nil {
- return nil, err
- }
- // If the result contains a revert reason, try to unpack and return it.
- if len(res.Revert()) > 0 {
- return nil, newRevertError(res)
- }
- return res.Return(), res.Err
-}
-
-// AcceptedCallContract executes a contract call on the accepted state.
-func (b *SimulatedBackend) AcceptedCallContract(ctx context.Context, call interfaces.CallMsg) ([]byte, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
- defer b.acceptedState.RevertToSnapshot(b.acceptedState.Snapshot())
-
- res, err := b.callContract(ctx, call, b.acceptedBlock.Header(), b.acceptedState)
- if err != nil {
- return nil, err
- }
- // If the result contains a revert reason, try to unpack and return it.
- if len(res.Revert()) > 0 {
- return nil, newRevertError(res)
- }
- return res.Return(), res.Err
-}
-
-// AcceptedNonceAt implements AcceptedStateReader.AcceptedNonceAt, retrieving
-// the nonce currently accepted for the account.
-func (b *SimulatedBackend) AcceptedNonceAt(ctx context.Context, account common.Address) (uint64, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- return b.acceptedState.GetOrNewStateObject(account).Nonce(), nil
-}
-
-// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
-// chain doesn't have miners, we just return a gas price of 1 for any call.
-func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- if b.acceptedBlock.Header().BaseFee != nil {
- return b.acceptedBlock.Header().BaseFee, nil
- }
- return big.NewInt(1), nil
-}
-
-// SuggestGasTipCap implements ContractTransactor.SuggestGasTipCap. Since the simulated
-// chain doesn't have miners, we just return a gas tip of 1 for any call.
-func (b *SimulatedBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
- return big.NewInt(1), nil
-}
-
-// EstimateGas executes the requested code against the currently accepted block/state and
-// returns the used amount of gas.
-func (b *SimulatedBackend) EstimateGas(ctx context.Context, call interfaces.CallMsg) (uint64, error) {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- // Determine the lowest and highest possible gas limits to binary search in between
- var (
- lo uint64 = params.TxGas - 1
- hi uint64
- cap uint64
- )
- if call.Gas >= params.TxGas {
- hi = call.Gas
- } else {
- hi = b.acceptedBlock.GasLimit()
- }
- // Normalize the max fee per gas the call is willing to spend.
- var feeCap *big.Int
- if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
- return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
- } else if call.GasPrice != nil {
- feeCap = call.GasPrice
- } else if call.GasFeeCap != nil {
- feeCap = call.GasFeeCap
- } else {
- feeCap = common.Big0
- }
- // Recap the highest gas allowance with account's balance.
- if feeCap.BitLen() != 0 {
- balance := b.acceptedState.GetBalance(call.From) // from can't be nil
- available := new(big.Int).Set(balance)
- if call.Value != nil {
- if call.Value.Cmp(available) >= 0 {
- return 0, core.ErrInsufficientFundsForTransfer
- }
- available.Sub(available, call.Value)
- }
- allowance := new(big.Int).Div(available, feeCap)
- if allowance.IsUint64() && hi > allowance.Uint64() {
- transfer := call.Value
- if transfer == nil {
- transfer = new(big.Int)
- }
- log.Info("Gas estimation capped by limited funds", "original", hi, "balance", balance,
- "sent", transfer, "feecap", feeCap, "fundable", allowance)
- hi = allowance.Uint64()
- }
- }
- cap = hi
-
- // Create a helper to check if a gas allowance results in an executable transaction
- executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
- call.Gas = gas
-
- snapshot := b.acceptedState.Snapshot()
- res, err := b.callContract(ctx, call, b.acceptedBlock.Header(), b.acceptedState)
- b.acceptedState.RevertToSnapshot(snapshot)
-
- if err != nil {
- if errors.Is(err, core.ErrIntrinsicGas) {
- return true, nil, nil // Special case, raise gas limit
- }
- return true, nil, err // Bail out
- }
- return res.Failed(), res, nil
- }
- // Execute the binary search and hone in on an executable gas limit
- for lo+1 < hi {
- mid := (hi + lo) / 2
- failed, _, err := executable(mid)
-
- // If the error is not nil(consensus error), it means the provided message
- // call or transaction will never be accepted no matter how much gas it is
- // assigned. Return the error directly, don't struggle any more
- if err != nil {
- return 0, err
- }
- if failed {
- lo = mid
- } else {
- hi = mid
- }
- }
- // Reject the transaction as invalid if it still fails at the highest allowance
- if hi == cap {
- failed, result, err := executable(hi)
- if err != nil {
- return 0, err
- }
- if failed {
- if result != nil && !errors.Is(result.Err, vmerrs.ErrOutOfGas) {
- if len(result.Revert()) > 0 {
- return 0, newRevertError(result)
- }
- return 0, result.Err
- }
- // Otherwise, the specified gas cap is too low
- return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
- }
- }
- return hi, nil
-}
-
-// callContract implements common code between normal and accepted contract calls.
-// state is modified during execution, make sure to copy it if necessary.
-func (b *SimulatedBackend) callContract(ctx context.Context, call interfaces.CallMsg, header *types.Header, stateDB *state.StateDB) (*core.ExecutionResult, error) {
- // Gas prices post 1559 need to be initialized
- if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
- return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
- }
- if !b.blockchain.Config().IsSubnetEVM(header.Time) {
- // If there's no basefee, then it must be a non-1559 execution
- if call.GasPrice == nil {
- call.GasPrice = new(big.Int)
- }
- call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
- } else {
- // A basefee is provided, necessitating 1559-type execution
- if call.GasPrice != nil {
- // User specified the legacy gas field, convert to 1559 gas typing
- call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
- } else {
- // User specified 1559 gas fields (or none), use those
- if call.GasFeeCap == nil {
- call.GasFeeCap = new(big.Int)
- }
- if call.GasTipCap == nil {
- call.GasTipCap = new(big.Int)
- }
- // Backfill the legacy gasPrice for EVM execution, unless we're all zeroes
- call.GasPrice = new(big.Int)
- if call.GasFeeCap.BitLen() > 0 || call.GasTipCap.BitLen() > 0 {
- call.GasPrice = math.BigMin(new(big.Int).Add(call.GasTipCap, header.BaseFee), call.GasFeeCap)
- }
- }
- }
- // Ensure message is initialized properly.
- if call.Gas == 0 {
- call.Gas = 10 * header.GasLimit
- }
- if call.Value == nil {
- call.Value = new(big.Int)
- }
-
- // Set infinite balance to the fake caller account.
- from := stateDB.GetOrNewStateObject(call.From)
- from.SetBalance(math.MaxBig256)
-
- // Execute the call.
- msg := &core.Message{
- From: call.From,
- To: call.To,
- Value: call.Value,
- GasLimit: call.Gas,
- GasPrice: call.GasPrice,
- GasFeeCap: call.GasFeeCap,
- GasTipCap: call.GasTipCap,
- Data: call.Data,
- AccessList: call.AccessList,
- SkipAccountChecks: true,
- }
-
- // Create a new environment which holds all relevant information
- // about the transaction and calling mechanisms.
- txContext := core.NewEVMTxContext(msg)
- evmContext := core.NewEVMBlockContext(header, b.blockchain, nil)
- vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true})
- gasPool := new(core.GasPool).AddGas(math.MaxUint64)
-
- return core.ApplyMessage(vmEnv, msg, gasPool)
-}
-
-// SendTransaction updates the accepted block to include the given transaction.
-func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- // Get the last block
- block, err := b.blockByHash(ctx, b.acceptedBlock.ParentHash())
- if err != nil {
- return errors.New("could not fetch parent")
- }
- // Check transaction validity
- signer := types.MakeSigner(b.blockchain.Config(), block.Number(), block.Time())
- sender, err := types.Sender(signer, tx)
- if err != nil {
- return fmt.Errorf("invalid transaction: %v", err)
- }
- nonce := b.acceptedState.GetNonce(sender)
- if tx.Nonce() != nonce {
- return fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)
- }
- // Include tx in chain
- blocks, _, err := core.GenerateChain(b.config, block, dummy.NewETHFaker(), b.database, 1, 10, func(number int, block *core.BlockGen) {
- for _, tx := range b.acceptedBlock.Transactions() {
- block.AddTxWithChain(b.blockchain, tx)
- }
- block.AddTxWithChain(b.blockchain, tx)
- })
- if err != nil {
- return err
- }
- stateDB, err := b.blockchain.State()
- if err != nil {
- return err
- }
- b.acceptedBlock = blocks[0]
- b.acceptedState, _ = state.New(b.acceptedBlock.Root(), stateDB.Database(), nil)
- return nil
-}
-
-// FilterLogs executes a log filter operation, blocking during execution and
-// returning all the results in one batch.
+// A simulated backend always uses chainID 1337.
//
-// TODO(karalabe): Deprecate when the subscription one can return past data too.
-func (b *SimulatedBackend) FilterLogs(ctx context.Context, query interfaces.FilterQuery) ([]types.Log, error) {
- var filter *filters.Filter
- if query.BlockHash != nil {
- // Block filter requested, construct a single-shot filter
- filter = b.filterSystem.NewBlockFilter(*query.BlockHash, query.Addresses, query.Topics)
- } else {
- // Initialize unset filter boundaries to run from genesis to chain head
- from := int64(0)
- if query.FromBlock != nil {
- from = query.FromBlock.Int64()
- }
- to := int64(-1)
- if query.ToBlock != nil {
- to = query.ToBlock.Int64()
- }
- // Construct the range filter
- filter = b.filterSystem.NewRangeFilter(from, to, query.Addresses, query.Topics)
- }
- // Run the filter and return all the logs
- logs, err := filter.Logs(ctx)
- if err != nil {
- return nil, err
+// Deprecated: please use simulated.Backend from package
+// github.com/ava-labs/subnet-evm/ethclient/simulated instead.
+func NewSimulatedBackend(alloc types.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
+ b := simulated.NewBackend(alloc, simulated.WithBlockGasLimit(gasLimit))
+ return &SimulatedBackend{
+ Backend: b,
+ Client: b.Client(),
}
- res := make([]types.Log, len(logs))
- for i, nLog := range logs {
- res[i] = *nLog
- }
- return res, nil
-}
-
-// SubscribeFilterLogs creates a background log filtering operation, returning a
-// subscription immediately, which can be used to stream the found events.
-func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query interfaces.FilterQuery, ch chan<- types.Log) (interfaces.Subscription, error) {
- // Subscribe to contract events
- sink := make(chan []*types.Log)
-
- sub, err := b.events.SubscribeLogs(query, sink)
- if err != nil {
- return nil, err
- }
- // Since we're getting logs in batches, we need to flatten them into a plain stream
- return event.NewSubscription(func(quit <-chan struct{}) error {
- defer sub.Unsubscribe()
- for {
- select {
- case logs := <-sink:
- for _, nlog := range logs {
- select {
- case ch <- *nlog:
- case err := <-sub.Err():
- return err
- case <-quit:
- return nil
- }
- }
- case err := <-sub.Err():
- return err
- case <-quit:
- return nil
- }
- }
- }), nil
-}
-
-// SubscribeNewHead returns an event subscription for a new header.
-func (b *SimulatedBackend) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (interfaces.Subscription, error) {
- // subscribe to a new head
- sink := make(chan *types.Header)
- sub := b.events.SubscribeNewHeads(sink)
-
- return event.NewSubscription(func(quit <-chan struct{}) error {
- defer sub.Unsubscribe()
- for {
- select {
- case head := <-sink:
- select {
- case ch <- head:
- case err := <-sub.Err():
- return err
- case <-quit:
- return nil
- }
- case err := <-sub.Err():
- return err
- case <-quit:
- return nil
- }
- }
- }), nil
-}
-
-// AdjustTime adds a time shift to the simulated clock.
-// It can only be called on empty blocks.
-func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
- b.mu.Lock()
- defer b.mu.Unlock()
-
- if len(b.acceptedBlock.Transactions()) != 0 {
- return errors.New("could not adjust time on non-empty block")
- }
- block := b.blockchain.GetBlockByHash(b.acceptedBlock.ParentHash())
- if block == nil {
- return errors.New("could not find parent")
- }
-
- blocks, _, _ := core.GenerateChain(b.config, block, dummy.NewFaker(), b.database, 1, 10, func(number int, block *core.BlockGen) {
- block.OffsetTime(int64(adjustment.Seconds()))
- })
- stateDB, err := b.blockchain.State()
- if err != nil {
- return err
- }
- b.acceptedBlock = blocks[0]
- b.acceptedState, _ = state.New(b.acceptedBlock.Root(), stateDB.Database(), nil)
- return nil
-}
-
-// Blockchain returns the underlying blockchain.
-func (b *SimulatedBackend) Blockchain() *core.BlockChain {
- return b.blockchain
-}
-
-// filterBackend implements filters.Backend to support filtering for logs without
-// taking bloom-bits acceleration structures into account.
-type filterBackend struct {
- db ethdb.Database
- bc *core.BlockChain
- backend *SimulatedBackend
-}
-
-func (fb *filterBackend) SubscribeChainAcceptedEvent(ch chan<- core.ChainEvent) event.Subscription {
- return fb.bc.SubscribeChainAcceptedEvent(ch)
-}
-
-func (fb *filterBackend) SubscribeAcceptedLogsEvent(ch chan<- []*types.Log) event.Subscription {
- return fb.bc.SubscribeAcceptedLogsEvent(ch)
-}
-
-func (fb *filterBackend) SubscribeAcceptedTransactionEvent(ch chan<- core.NewTxsEvent) event.Subscription {
- return fb.bc.SubscribeAcceptedTransactionEvent(ch)
-}
-
-func (fb *filterBackend) IsAllowUnfinalizedQueries() bool {
- return false
-}
-
-func (fb *filterBackend) LastAcceptedBlock() *types.Block {
- return fb.bc.LastAcceptedBlock()
-}
-
-func (fb *filterBackend) GetMaxBlocksPerRequest() int64 {
- return eth.DefaultSettings.MaxBlocksPerRequest
-}
-
-func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db }
-
-func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") }
-
-func (fb *filterBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
- switch number {
- case rpc.PendingBlockNumber, rpc.FinalizedBlockNumber:
- if block := fb.backend.acceptedBlock; block != nil {
- return block.Header(), nil
- }
- return nil, nil
- case rpc.LatestBlockNumber:
- return fb.bc.CurrentHeader(), nil
- default:
- return fb.bc.GetHeaderByNumber(uint64(number.Int64())), nil
- }
-}
-
-func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
- return fb.bc.GetHeaderByHash(hash), nil
-}
-
-func (fb *filterBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
- if body := fb.bc.GetBody(hash); body != nil {
- return body, nil
- }
- return nil, errors.New("block body not found")
-}
-
-func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
- number := rawdb.ReadHeaderNumber(fb.db, hash)
- if number == nil {
- return nil, nil
- }
- header := rawdb.ReadHeader(fb.db, hash, *number)
- if header == nil {
- return nil, nil
- }
- return rawdb.ReadReceipts(fb.db, hash, *number, header.Time, fb.bc.Config()), nil
-}
-
-func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
- logs := rawdb.ReadLogs(fb.db, hash, number)
- return logs, nil
-}
-
-func (fb *filterBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
- return nullSubscription()
-}
-
-func (fb *filterBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
- return fb.bc.SubscribeChainEvent(ch)
-}
-
-func (fb *filterBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
- return fb.bc.SubscribeRemovedLogsEvent(ch)
-}
-
-func (fb *filterBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
- return fb.bc.SubscribeLogsEvent(ch)
-}
-
-func (fb *filterBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription {
- return nullSubscription()
-}
-
-func (fb *filterBackend) BloomStatus() (uint64, uint64) { return 4096, 0 }
-
-func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.MatcherSession) {
- panic("not supported")
-}
-
-func (fb *filterBackend) ChainConfig() *params.ChainConfig {
- panic("not supported")
-}
-
-func (fb *filterBackend) CurrentHeader() *types.Header {
- panic("not supported")
-}
-
-func nullSubscription() event.Subscription {
- return event.NewSubscription(func(quit <-chan struct{}) error {
- <-quit
- return nil
- })
}
diff --git a/accounts/abi/bind/backends/simulated_test.go b/accounts/abi/bind/backends/simulated_test.go
deleted file mode 100644
index dd99f55d7e..0000000000
--- a/accounts/abi/bind/backends/simulated_test.go
+++ /dev/null
@@ -1,1498 +0,0 @@
-// (c) 2019-2020, Ava Labs, Inc.
-//
-// This file is a derived work, based on the go-ethereum library whose original
-// notices appear below.
-//
-// It is distributed under a license compatible with the licensing terms of the
-// original code from which it is derived.
-//
-// Much love to the original authors for their work.
-// **********
-// Copyright 2019 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 .
-
-package backends
-
-import (
- "bytes"
- "context"
- "errors"
- "math/big"
- "math/rand"
- "reflect"
- "strings"
- "testing"
- "time"
-
- "github.com/ava-labs/subnet-evm/accounts/abi"
- "github.com/ava-labs/subnet-evm/accounts/abi/bind"
- "github.com/ava-labs/subnet-evm/core"
- "github.com/ava-labs/subnet-evm/core/types"
- "github.com/ava-labs/subnet-evm/interfaces"
- "github.com/ava-labs/subnet-evm/params"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/crypto"
-)
-
-func TestSimulatedBackend(t *testing.T) {
- t.Parallel()
- var gasLimit uint64 = 8000029
- key, _ := crypto.GenerateKey() // nolint: gosec
- auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- genAlloc := make(core.GenesisAlloc)
- genAlloc[auth.From] = core.GenesisAccount{Balance: big.NewInt(9223372036854775807)}
-
- sim := NewSimulatedBackend(genAlloc, gasLimit)
- defer sim.Close()
-
- // should return an error if the tx is not found
- txHash := common.HexToHash("2")
- _, isPending, err := sim.TransactionByHash(context.Background(), txHash)
-
- if isPending {
- t.Fatal("transaction should not be pending")
- }
- if err != interfaces.NotFound {
- t.Fatalf("err should be `interfaces.NotFound` but received %v", err)
- }
-
- // generate a transaction and confirm you can retrieve it
- head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
- gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
-
- code := `6060604052600a8060106000396000f360606040526008565b00`
- var gas uint64 = 3000000
- tx := types.NewContractCreation(0, big.NewInt(0), gas, gasPrice, common.FromHex(code))
- tx, _ = types.SignTx(tx, types.HomesteadSigner{}, key)
-
- err = sim.SendTransaction(context.Background(), tx)
- if err != nil {
- t.Fatal("error sending transaction")
- }
-
- txHash = tx.Hash()
- _, isPending, err = sim.TransactionByHash(context.Background(), txHash)
- if err != nil {
- t.Fatalf("error getting transaction with hash: %v", txHash.String())
- }
- if !isPending {
- t.Fatal("transaction should have pending status")
- }
-
- sim.Commit(true)
- _, isPending, err = sim.TransactionByHash(context.Background(), txHash)
- if err != nil {
- t.Fatalf("error getting transaction with hash: %v", txHash.String())
- }
- if isPending {
- t.Fatal("transaction should not have pending status")
- }
-}
-
-var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
-
-// the following is based on this contract:
-//
-// contract T {
-// event received(address sender, uint amount, bytes memo);
-// event receivedAddr(address sender);
-//
-// function receive(bytes calldata memo) external payable returns (string memory res) {
-// emit received(msg.sender, msg.value, memo);
-// emit receivedAddr(msg.sender);
-// return "hello world";
-// }
-// }
-const abiJSON = `[ { "constant": false, "inputs": [ { "name": "memo", "type": "bytes" } ], "name": "receive", "outputs": [ { "name": "res", "type": "string" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" }, { "indexed": false, "name": "amount", "type": "uint256" }, { "indexed": false, "name": "memo", "type": "bytes" } ], "name": "received", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" } ], "name": "receivedAddr", "type": "event" } ]`
-const abiBin = `0x608060405234801561001057600080fd5b506102a0806100206000396000f3fe60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063a69b6ed014610040575b600080fd5b6100b76004803603602081101561005657600080fd5b810190808035906020019064010000000081111561007357600080fd5b82018360208201111561008557600080fd5b803590602001918460018302840111640100000000831117156100a757600080fd5b9091929391929390505050610132565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f75780820151818401526020810190506100dc565b50505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60607f75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed33348585604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509550505050505060405180910390a17f46923992397eac56cf13058aced2a1871933622717e27b24eabc13bf9dd329c833604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a16040805190810160405280600b81526020017f68656c6c6f20776f726c6400000000000000000000000000000000000000000081525090509291505056fea165627a7a72305820ff0c57dad254cfeda48c9cfb47f1353a558bccb4d1bc31da1dae69315772d29e0029`
-const deployedCode = `60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063a69b6ed014610040575b600080fd5b6100b76004803603602081101561005657600080fd5b810190808035906020019064010000000081111561007357600080fd5b82018360208201111561008557600080fd5b803590602001918460018302840111640100000000831117156100a757600080fd5b9091929391929390505050610132565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f75780820151818401526020810190506100dc565b50505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60607f75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed33348585604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509550505050505060405180910390a17f46923992397eac56cf13058aced2a1871933622717e27b24eabc13bf9dd329c833604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a16040805190810160405280600b81526020017f68656c6c6f20776f726c6400000000000000000000000000000000000000000081525090509291505056fea165627a7a72305820ff0c57dad254cfeda48c9cfb47f1353a558bccb4d1bc31da1dae69315772d29e0029`
-
-// expected return value contains "hello world"
-var expectedReturn = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
-
-func simTestBackend(testAddr common.Address) *SimulatedBackend {
- return NewSimulatedBackend(
- core.GenesisAlloc{
- testAddr: {Balance: new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000))},
- }, 10000000,
- )
-}
-
-func TestNewSimulatedBackend(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
- expectedBal := new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000))
- sim := simTestBackend(testAddr)
- defer sim.Close()
-
- stateDB, _ := sim.blockchain.State()
- bal := stateDB.GetBalance(testAddr)
- if bal.Cmp(expectedBal) != 0 {
- t.Errorf("expected balance for test address not received. expected: %v actual: %v", expectedBal, bal)
- }
-}
-
-func TestAdjustTime(t *testing.T) {
- t.Parallel()
- sim := NewSimulatedBackend(
- core.GenesisAlloc{}, 10000000,
- )
- defer sim.Close()
-
- prevTime := sim.acceptedBlock.Time()
- if err := sim.AdjustTime(time.Second); err != nil {
- t.Error(err)
- }
- newTime := sim.acceptedBlock.Time()
-
- if newTime-prevTime != uint64(time.Second.Seconds()) {
- t.Errorf("adjusted time not equal to a second. prev: %v, new: %v", prevTime, newTime)
- }
-}
-
-func TestNewAdjustTimeFail(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
- sim := simTestBackend(testAddr)
- defer sim.blockchain.Stop()
-
- // Create tx and send
- head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
- gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
-
- tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
- signer := types.NewLondonSigner(big.NewInt(1337))
- signedTx, err := types.SignTx(tx, signer, testKey)
- if err != nil {
- t.Errorf("could not sign tx: %v", err)
- }
- sim.SendTransaction(context.Background(), signedTx)
- // AdjustTime should fail on non-empty block
- if err := sim.AdjustTime(time.Second); err == nil {
- t.Error("Expected adjust time to error on non-empty block")
- }
- sim.Commit(false)
-
- prevTime := sim.acceptedBlock.Time()
- if err := sim.AdjustTime(time.Minute); err != nil {
- t.Error(err)
- }
- newTime := sim.acceptedBlock.Time()
- if newTime-prevTime != uint64(time.Minute.Seconds()) {
- t.Errorf("adjusted time not equal to a minute. prev: %v, new: %v", prevTime, newTime)
- }
- // Put a transaction after adjusting time
- tx2 := types.NewTransaction(1, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
- signer = types.NewLondonSigner(big.NewInt(1337))
- signedTx2, err := types.SignTx(tx2, signer, testKey)
- if err != nil {
- t.Errorf("could not sign tx: %v", err)
- }
- sim.SendTransaction(context.Background(), signedTx2)
- sim.Commit(false)
- newTime = sim.acceptedBlock.Time()
- if newTime-prevTime >= uint64(time.Minute.Seconds()) {
- t.Errorf("time adjusted, but shouldn't be: prev: %v, new: %v", prevTime, newTime)
- }
-}
-
-func TestBalanceAt(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
- expectedBal := new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000))
- sim := simTestBackend(testAddr)
- defer sim.Close()
- bgCtx := context.Background()
-
- bal, err := sim.BalanceAt(bgCtx, testAddr, nil)
- if err != nil {
- t.Error(err)
- }
-
- if bal.Cmp(expectedBal) != 0 {
- t.Errorf("expected balance for test address not received. expected: %v actual: %v", expectedBal, bal)
- }
-}
-
-func TestBlockByHash(t *testing.T) {
- t.Parallel()
- sim := NewSimulatedBackend(
- core.GenesisAlloc{}, 10000000,
- )
- defer sim.Close()
- bgCtx := context.Background()
-
- block, err := sim.BlockByNumber(bgCtx, nil)
- if err != nil {
- t.Errorf("could not get recent block: %v", err)
- }
- blockByHash, err := sim.BlockByHash(bgCtx, block.Hash())
- if err != nil {
- t.Errorf("could not get recent block: %v", err)
- }
-
- if block.Hash() != blockByHash.Hash() {
- t.Errorf("did not get expected block")
- }
-}
-
-func TestBlockByNumber(t *testing.T) {
- t.Parallel()
- sim := NewSimulatedBackend(
- core.GenesisAlloc{}, 10000000,
- )
- defer sim.Close()
- bgCtx := context.Background()
-
- block, err := sim.BlockByNumber(bgCtx, nil)
- if err != nil {
- t.Errorf("could not get recent block: %v", err)
- }
- if block.NumberU64() != 0 {
- t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64())
- }
-
- // create one block
- sim.Commit(false)
-
- block, err = sim.BlockByNumber(bgCtx, nil)
- if err != nil {
- t.Errorf("could not get recent block: %v", err)
- }
- if block.NumberU64() != 1 {
- t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64())
- }
-
- blockByNumber, err := sim.BlockByNumber(bgCtx, big.NewInt(1))
- if err != nil {
- t.Errorf("could not get block by number: %v", err)
- }
- if blockByNumber.Hash() != block.Hash() {
- t.Errorf("did not get the same block with height of 1 as before")
- }
-}
-
-func TestNonceAt(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
-
- sim := simTestBackend(testAddr)
- defer sim.Close()
- bgCtx := context.Background()
-
- nonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(0))
- if err != nil {
- t.Errorf("could not get nonce for test addr: %v", err)
- }
-
- if nonce != uint64(0) {
- t.Errorf("received incorrect nonce. expected 0, got %v", nonce)
- }
-
- // create a signed transaction to send
- head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
- gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
-
- tx := types.NewTransaction(nonce, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
- signer := types.NewLondonSigner(big.NewInt(1337))
- signedTx, err := types.SignTx(tx, signer, testKey)
- if err != nil {
- t.Errorf("could not sign tx: %v", err)
- }
-
- // send tx to simulated backend
- err = sim.SendTransaction(bgCtx, signedTx)
- if err != nil {
- t.Errorf("could not add tx to pending block: %v", err)
- }
- sim.Commit(false)
-
- newNonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(1))
- if err != nil {
- t.Errorf("could not get nonce for test addr: %v", err)
- }
-
- if newNonce != nonce+uint64(1) {
- t.Errorf("received incorrect nonce. expected 1, got %v", nonce)
- }
- // create some more blocks
- sim.Commit(false)
- // Check that we can get data for an older block/state
- newNonce, err = sim.NonceAt(bgCtx, testAddr, big.NewInt(1))
- if err != nil {
- t.Fatalf("could not get nonce for test addr: %v", err)
- }
- if newNonce != nonce+uint64(1) {
- t.Fatalf("received incorrect nonce. expected 1, got %v", nonce)
- }
-}
-
-func TestSendTransaction(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
-
- sim := simTestBackend(testAddr)
- defer sim.Close()
- bgCtx := context.Background()
-
- // create a signed transaction to send
- head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
- gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
-
- tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
- signer := types.NewLondonSigner(big.NewInt(1337))
- signedTx, err := types.SignTx(tx, signer, testKey)
- if err != nil {
- t.Errorf("could not sign tx: %v", err)
- }
-
- // send tx to simulated backend
- err = sim.SendTransaction(bgCtx, signedTx)
- if err != nil {
- t.Errorf("could not add tx to pending block: %v", err)
- }
- sim.Commit(false)
-
- block, err := sim.BlockByNumber(bgCtx, big.NewInt(1))
- if err != nil {
- t.Errorf("could not get block at height 1: %v", err)
- }
-
- if signedTx.Hash() != block.Transactions()[0].Hash() {
- t.Errorf("did not commit sent transaction. expected hash %v got hash %v", block.Transactions()[0].Hash(), signedTx.Hash())
- }
-}
-
-func TestTransactionByHash(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
-
- sim := NewSimulatedBackend(
- core.GenesisAlloc{
- testAddr: {Balance: new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000))},
- }, 10000000,
- )
- defer sim.Close()
- bgCtx := context.Background()
-
- // create a signed transaction to send
- head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
- gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
-
- tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
- signer := types.NewLondonSigner(big.NewInt(1337))
- signedTx, err := types.SignTx(tx, signer, testKey)
- if err != nil {
- t.Errorf("could not sign tx: %v", err)
- }
-
- // send tx to simulated backend
- err = sim.SendTransaction(bgCtx, signedTx)
- if err != nil {
- t.Errorf("could not add tx to pending block: %v", err)
- }
-
- // ensure tx is committed pending
- receivedTx, pending, err := sim.TransactionByHash(bgCtx, signedTx.Hash())
- if err != nil {
- t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err)
- }
- if !pending {
- t.Errorf("expected transaction to be in pending state")
- }
- if receivedTx.Hash() != signedTx.Hash() {
- t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash())
- }
-
- sim.Commit(true)
-
- // ensure tx is not and committed pending
- receivedTx, pending, err = sim.TransactionByHash(bgCtx, signedTx.Hash())
- if err != nil {
- t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err)
- }
- if pending {
- t.Errorf("expected transaction to not be in pending state")
- }
- if receivedTx.Hash() != signedTx.Hash() {
- t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash())
- }
-}
-
-func TestEstimateGas(t *testing.T) {
- t.Parallel()
- /*
- pragma solidity ^0.6.4;
- contract GasEstimation {
- function PureRevert() public { revert(); }
- function Revert() public { revert("revert reason");}
- function OOG() public { for (uint i = 0; ; i++) {}}
- function Assert() public { assert(false);}
- function Valid() public {}
- }
- */
- const contractAbi = "[{\"inputs\":[],\"name\":\"Assert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OOG\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PureRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Revert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Valid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
- const contractBin = "0x60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040523480156100115760006000fd5b506004361061005c5760003560e01c806350f6fe3414610062578063aa8b1d301461006c578063b9b046f914610076578063d8b9839114610080578063e09fface1461008a5761005c565b60006000fd5b61006a610094565b005b6100746100ad565b005b61007e6100b5565b005b6100886100c2565b005b610092610135565b005b6000600090505b5b808060010191505061009b565b505b565b60006000fd5b565b600015156100bf57fe5b5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081526020015060200191505060405180910390fd5b565b5b56fea2646970667358221220345bbcbb1a5ecf22b53a78eaebf95f8ee0eceff6d10d4b9643495084d2ec934a64736f6c63430006040033"
-
- key, _ := crypto.GenerateKey()
- addr := crypto.PubkeyToAddress(key.PublicKey)
- opts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
-
- sim := NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(params.Ether)}}, 10000000)
- defer sim.Close()
-
- parsed, _ := abi.JSON(strings.NewReader(contractAbi))
- contractAddr, _, _, _ := bind.DeployContract(opts, parsed, common.FromHex(contractBin), sim)
- sim.Commit(false)
-
- var cases = []struct {
- name string
- message interfaces.CallMsg
- expect uint64
- expectError error
- expectData interface{}
- }{
- {"plain transfer(valid)", interfaces.CallMsg{
- From: addr,
- To: &addr,
- Gas: 0,
- GasPrice: big.NewInt(0),
- Value: big.NewInt(1),
- Data: nil,
- }, params.TxGas, nil, nil},
-
- {"plain transfer(invalid)", interfaces.CallMsg{
- From: addr,
- To: &contractAddr,
- Gas: 0,
- GasPrice: big.NewInt(0),
- Value: big.NewInt(1),
- Data: nil,
- }, 0, errors.New("execution reverted"), nil},
-
- {"Revert", interfaces.CallMsg{
- From: addr,
- To: &contractAddr,
- Gas: 0,
- GasPrice: big.NewInt(0),
- Value: nil,
- Data: common.Hex2Bytes("d8b98391"),
- }, 0, errors.New("execution reverted: revert reason"), "0x08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d72657665727420726561736f6e00000000000000000000000000000000000000"},
-
- {"PureRevert", interfaces.CallMsg{
- From: addr,
- To: &contractAddr,
- Gas: 0,
- GasPrice: big.NewInt(0),
- Value: nil,
- Data: common.Hex2Bytes("aa8b1d30"),
- }, 0, errors.New("execution reverted"), nil},
-
- {"OOG", interfaces.CallMsg{
- From: addr,
- To: &contractAddr,
- Gas: 100000,
- GasPrice: big.NewInt(0),
- Value: nil,
- Data: common.Hex2Bytes("50f6fe34"),
- }, 0, errors.New("gas required exceeds allowance (100000)"), nil},
-
- {"Assert", interfaces.CallMsg{
- From: addr,
- To: &contractAddr,
- Gas: 100000,
- GasPrice: big.NewInt(0),
- Value: nil,
- Data: common.Hex2Bytes("b9b046f9"),
- }, 0, errors.New("invalid opcode: INVALID"), nil},
-
- {"Valid", interfaces.CallMsg{
- From: addr,
- To: &contractAddr,
- Gas: 100000,
- GasPrice: big.NewInt(0),
- Value: nil,
- Data: common.Hex2Bytes("e09fface"),
- }, 21275, nil, nil},
- }
- for _, c := range cases {
- got, err := sim.EstimateGas(context.Background(), c.message)
- if c.expectError != nil {
- if err == nil {
- t.Fatalf("Expect error, got nil")
- }
- if c.expectError.Error() != err.Error() {
- t.Fatalf("Expect error, want %v, got %v", c.expectError, err)
- }
- if c.expectData != nil {
- if err, ok := err.(*revertError); !ok {
- t.Fatalf("Expect revert error, got %T", err)
- } else if !reflect.DeepEqual(err.ErrorData(), c.expectData) {
- t.Fatalf("Error data mismatch, want %v, got %v", c.expectData, err.ErrorData())
- }
- }
- continue
- }
- if got != c.expect {
- t.Fatalf("Gas estimation mismatch, want %d, got %d", c.expect, got)
- }
- }
-}
-
-func TestEstimateGasWithPrice(t *testing.T) {
- t.Parallel()
- key, _ := crypto.GenerateKey()
- addr := crypto.PubkeyToAddress(key.PublicKey)
-
- sim := NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(params.Ether*2 + 2e17)}}, 10000000)
- defer sim.Close()
-
- recipient := common.HexToAddress("deadbeef")
- var cases = []struct {
- name string
- message interfaces.CallMsg
- expect uint64
- expectError error
- }{
- {"EstimateWithoutPrice", interfaces.CallMsg{
- From: addr,
- To: &recipient,
- Gas: 0,
- GasPrice: big.NewInt(0),
- Value: big.NewInt(100000000000),
- Data: nil,
- }, 21000, nil},
-
- {"EstimateWithPrice", interfaces.CallMsg{
- From: addr,
- To: &recipient,
- Gas: 0,
- GasPrice: big.NewInt(225000000000),
- Value: big.NewInt(100000000000),
- Data: nil,
- }, 21000, nil},
-
- {"EstimateWithVeryHighPrice", interfaces.CallMsg{
- From: addr,
- To: &recipient,
- Gas: 0,
- GasPrice: big.NewInt(1e14), // gascost = 2.1ether
- Value: big.NewInt(1e17), // the remaining balance for fee is 2.1ether
- Data: nil,
- }, 21000, nil},
-
- {"EstimateWithSuperhighPrice", interfaces.CallMsg{
- From: addr,
- To: &recipient,
- Gas: 0,
- GasPrice: big.NewInt(2e14), // gascost = 4.2ether
- Value: big.NewInt(100000000000),
- Data: nil,
- }, 21000, errors.New("gas required exceeds allowance (10999)")}, // 10999=(2.2ether-1000wei)/(2e14)
-
- {"EstimateEIP1559WithHighFees", interfaces.CallMsg{
- From: addr,
- To: &addr,
- Gas: 0,
- GasFeeCap: big.NewInt(1e14), // maxgascost = 2.1ether
- GasTipCap: big.NewInt(1),
- Value: big.NewInt(1e17), // the remaining balance for fee is 2.1ether
- Data: nil,
- }, params.TxGas, nil},
-
- {"EstimateEIP1559WithSuperHighFees", interfaces.CallMsg{
- From: addr,
- To: &addr,
- Gas: 0,
- GasFeeCap: big.NewInt(1e14), // maxgascost = 2.1ether
- GasTipCap: big.NewInt(1),
- Value: big.NewInt(1e17 + 1), // the remaining balance for fee is 2.1ether
- Data: nil,
- }, params.TxGas, errors.New("gas required exceeds allowance (20999)")}, // 20999=(2.2ether-0.1ether-1wei)/(1e14)
- }
- for i, c := range cases {
- got, err := sim.EstimateGas(context.Background(), c.message)
- if c.expectError != nil {
- if err == nil {
- t.Fatalf("test %d: expect error, got nil", i)
- }
- if c.expectError.Error() != err.Error() {
- t.Fatalf("test %d: expect error, want %v, got %v", i, c.expectError, err)
- }
- continue
- }
- if c.expectError == nil && err != nil {
- t.Fatalf("test %d: didn't expect error, got %v", i, err)
- }
- if got != c.expect {
- t.Fatalf("test %d: gas estimation mismatch, want %d, got %d", i, c.expect, got)
- }
- }
-}
-
-func TestHeaderByHash(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
-
- sim := simTestBackend(testAddr)
- defer sim.Close()
- bgCtx := context.Background()
-
- header, err := sim.HeaderByNumber(bgCtx, nil)
- if err != nil {
- t.Errorf("could not get recent block: %v", err)
- }
- headerByHash, err := sim.HeaderByHash(bgCtx, header.Hash())
- if err != nil {
- t.Errorf("could not get recent block: %v", err)
- }
-
- if header.Hash() != headerByHash.Hash() {
- t.Errorf("did not get expected block")
- }
-}
-
-func TestHeaderByNumber(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
-
- sim := simTestBackend(testAddr)
- defer sim.Close()
- bgCtx := context.Background()
-
- latestBlockHeader, err := sim.HeaderByNumber(bgCtx, nil)
- if err != nil {
- t.Errorf("could not get header for tip of chain: %v", err)
- }
- if latestBlockHeader == nil {
- t.Errorf("received a nil block header")
- } else if latestBlockHeader.Number.Uint64() != uint64(0) {
- t.Errorf("expected block header number 0, instead got %v", latestBlockHeader.Number.Uint64())
- }
-
- sim.Commit(false)
-
- latestBlockHeader, err = sim.HeaderByNumber(bgCtx, nil)
- if err != nil {
- t.Errorf("could not get header for blockheight of 1: %v", err)
- }
-
- blockHeader, err := sim.HeaderByNumber(bgCtx, big.NewInt(1))
- if err != nil {
- t.Errorf("could not get header for blockheight of 1: %v", err)
- }
-
- if blockHeader.Hash() != latestBlockHeader.Hash() {
- t.Errorf("block header and latest block header are not the same")
- }
- if blockHeader.Number.Int64() != int64(1) {
- t.Errorf("did not get blockheader for block 1. instead got block %v", blockHeader.Number.Int64())
- }
-
- block, err := sim.BlockByNumber(bgCtx, big.NewInt(1))
- if err != nil {
- t.Errorf("could not get block for blockheight of 1: %v", err)
- }
-
- if block.Hash() != blockHeader.Hash() {
- t.Errorf("block hash and block header hash do not match. expected %v, got %v", block.Hash(), blockHeader.Hash())
- }
-}
-
-func TestTransactionCount(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
-
- sim := simTestBackend(testAddr)
- defer sim.Close()
- bgCtx := context.Background()
- currentBlock, err := sim.BlockByNumber(bgCtx, nil)
- if err != nil || currentBlock == nil {
- t.Error("could not get current block")
- }
-
- count, err := sim.TransactionCount(bgCtx, currentBlock.Hash())
- if err != nil {
- t.Error("could not get current block's transaction count")
- }
-
- if count != 0 {
- t.Errorf("expected transaction count of %v does not match actual count of %v", 0, count)
- }
- // create a signed transaction to send
- head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
- gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
-
- tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
- signer := types.NewLondonSigner(big.NewInt(1337))
- signedTx, err := types.SignTx(tx, signer, testKey)
- if err != nil {
- t.Errorf("could not sign tx: %v", err)
- }
-
- // send tx to simulated backend
- err = sim.SendTransaction(bgCtx, signedTx)
- if err != nil {
- t.Errorf("could not add tx to pending block: %v", err)
- }
-
- sim.Commit(false)
-
- lastBlock, err := sim.BlockByNumber(bgCtx, nil)
- if err != nil {
- t.Errorf("could not get header for tip of chain: %v", err)
- }
-
- count, err = sim.TransactionCount(bgCtx, lastBlock.Hash())
- if err != nil {
- t.Error("could not get current block's transaction count")
- }
-
- if count != 1 {
- t.Errorf("expected transaction count of %v does not match actual count of %v", 1, count)
- }
-}
-
-func TestTransactionInBlock(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
-
- sim := simTestBackend(testAddr)
- defer sim.Close()
- bgCtx := context.Background()
-
- transaction, err := sim.TransactionInBlock(bgCtx, sim.acceptedBlock.Hash(), uint(0))
- if err == nil && err != errTransactionDoesNotExist {
- t.Errorf("expected a transaction does not exist error to be received but received %v", err)
- }
- if transaction != nil {
- t.Errorf("expected transaction to be nil but received %v", transaction)
- }
-
- // expect accepted nonce to be 0 since account has not been used
- acceptedNonce, err := sim.AcceptedNonceAt(bgCtx, testAddr)
- if err != nil {
- t.Errorf("did not get the pending nonce: %v", err)
- }
-
- if acceptedNonce != uint64(0) {
- t.Errorf("expected pending nonce of 0 got %v", acceptedNonce)
- }
- // create a signed transaction to send
- head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
- gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
-
- tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
- signer := types.NewLondonSigner(big.NewInt(1337))
- signedTx, err := types.SignTx(tx, signer, testKey)
- if err != nil {
- t.Errorf("could not sign tx: %v", err)
- }
-
- // send tx to simulated backend
- err = sim.SendTransaction(bgCtx, signedTx)
- if err != nil {
- t.Errorf("could not add tx to pending block: %v", err)
- }
-
- sim.Commit(false)
-
- lastBlock, err := sim.BlockByNumber(bgCtx, nil)
- if err != nil {
- t.Errorf("could not get header for tip of chain: %v", err)
- }
-
- transaction, err = sim.TransactionInBlock(bgCtx, lastBlock.Hash(), uint(1))
- if err == nil && err != errTransactionDoesNotExist {
- t.Errorf("expected a transaction does not exist error to be received but received %v", err)
- }
- if transaction != nil {
- t.Errorf("expected transaction to be nil but received %v", transaction)
- }
-
- transaction, err = sim.TransactionInBlock(bgCtx, lastBlock.Hash(), uint(0))
- if err != nil {
- t.Errorf("could not get transaction in the lastest block with hash %v: %v", lastBlock.Hash().String(), err)
- }
-
- if signedTx.Hash().String() != transaction.Hash().String() {
- t.Errorf("received transaction that did not match the sent transaction. expected hash %v, got hash %v", signedTx.Hash().String(), transaction.Hash().String())
- }
-}
-
-func TestAcceptedNonceAt(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
-
- sim := simTestBackend(testAddr)
- defer sim.Close()
- bgCtx := context.Background()
-
- // expect accepted nonce to be 0 since account has not been used
- acceptedNonce, err := sim.AcceptedNonceAt(bgCtx, testAddr)
- if err != nil {
- t.Errorf("did not get the accepted nonce: %v", err)
- }
-
- if acceptedNonce != uint64(0) {
- t.Errorf("expected accepted nonce of 0 got %v", acceptedNonce)
- }
-
- // create a signed transaction to send
- head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
- gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
-
- tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
- signer := types.NewLondonSigner(big.NewInt(1337))
- signedTx, err := types.SignTx(tx, signer, testKey)
- if err != nil {
- t.Errorf("could not sign tx: %v", err)
- }
-
- // send tx to simulated backend
- err = sim.SendTransaction(bgCtx, signedTx)
- if err != nil {
- t.Errorf("could not add tx to pending block: %v", err)
- }
-
- // expect accepted nonce to be 1 since account has submitted one transaction
- acceptedNonce, err = sim.AcceptedNonceAt(bgCtx, testAddr)
- if err != nil {
- t.Errorf("did not get the accepted nonce: %v", err)
- }
-
- if acceptedNonce != uint64(1) {
- t.Errorf("expected accepted nonce of 1 got %v", acceptedNonce)
- }
-
- // make a new transaction with a nonce of 1
- tx = types.NewTransaction(uint64(1), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
- signer = types.NewLondonSigner(big.NewInt(1337))
- signedTx, err = types.SignTx(tx, signer, testKey)
- if err != nil {
- t.Errorf("could not sign tx: %v", err)
- }
- err = sim.SendTransaction(bgCtx, signedTx)
- if err != nil {
- t.Errorf("could not send tx: %v", err)
- }
-
- // expect accepted nonce to be 2 since account now has two transactions
- acceptedNonce, err = sim.AcceptedNonceAt(bgCtx, testAddr)
- if err != nil {
- t.Errorf("did not get the accepted nonce: %v", err)
- }
-
- if acceptedNonce != uint64(2) {
- t.Errorf("expected accepted nonce of 2 got %v", acceptedNonce)
- }
-}
-
-func TestTransactionReceipt(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
-
- sim := simTestBackend(testAddr)
- defer sim.Close()
- bgCtx := context.Background()
-
- // create a signed transaction to send
- head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
- gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
-
- tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
- signer := types.NewLondonSigner(big.NewInt(1337))
- signedTx, err := types.SignTx(tx, signer, testKey)
- if err != nil {
- t.Errorf("could not sign tx: %v", err)
- }
-
- // send tx to simulated backend
- err = sim.SendTransaction(bgCtx, signedTx)
- if err != nil {
- t.Errorf("could not add tx to pending block: %v", err)
- }
- sim.Commit(true)
-
- receipt, err := sim.TransactionReceipt(bgCtx, signedTx.Hash())
- if err != nil {
- t.Errorf("could not get transaction receipt: %v", err)
- }
-
- if receipt.ContractAddress != testAddr && receipt.TxHash != signedTx.Hash() {
- t.Errorf("received receipt is not correct: %v", receipt)
- }
-}
-
-func TestSuggestGasPrice(t *testing.T) {
- t.Parallel()
- sim := NewSimulatedBackend(
- core.GenesisAlloc{},
- 10000000,
- )
- defer sim.Close()
- bgCtx := context.Background()
- gasPrice, err := sim.SuggestGasPrice(bgCtx)
- if err != nil {
- t.Errorf("could not get gas price: %v", err)
- }
- if gasPrice.Uint64() != sim.acceptedBlock.Header().BaseFee.Uint64() {
- t.Errorf("gas price was not expected value of %v. actual: %v", sim.acceptedBlock.Header().BaseFee.Uint64(), gasPrice.Uint64())
- }
-}
-
-func TestAcceptedCodeAt(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
- sim := simTestBackend(testAddr)
- defer sim.Close()
- bgCtx := context.Background()
- code, err := sim.CodeAt(bgCtx, testAddr, nil)
- if err != nil {
- t.Errorf("could not get code at test addr: %v", err)
- }
- if len(code) != 0 {
- t.Errorf("got code for account that does not have contract code")
- }
-
- parsed, err := abi.JSON(strings.NewReader(abiJSON))
- if err != nil {
- t.Errorf("could not get code at test addr: %v", err)
- }
- auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
- contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
- if err != nil {
- t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
- }
-
- code, err = sim.AcceptedCodeAt(bgCtx, contractAddr)
- if err != nil {
- t.Errorf("could not get code at test addr: %v", err)
- }
- if len(code) == 0 {
- t.Errorf("did not get code for account that has contract code")
- }
- // ensure code received equals code deployed
- if !bytes.Equal(code, common.FromHex(deployedCode)) {
- t.Errorf("code received did not match expected deployed code:\n expected %v\n actual %v", common.FromHex(deployedCode), code)
- }
-}
-
-func TestCodeAt(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
- sim := simTestBackend(testAddr)
- defer sim.Close()
- bgCtx := context.Background()
- code, err := sim.CodeAt(bgCtx, testAddr, nil)
- if err != nil {
- t.Errorf("could not get code at test addr: %v", err)
- }
- if len(code) != 0 {
- t.Errorf("got code for account that does not have contract code")
- }
-
- parsed, err := abi.JSON(strings.NewReader(abiJSON))
- if err != nil {
- t.Errorf("could not get code at test addr: %v", err)
- }
- auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
- contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
- if err != nil {
- t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
- }
-
- sim.Commit(false)
- code, err = sim.CodeAt(bgCtx, contractAddr, nil)
- if err != nil {
- t.Errorf("could not get code at test addr: %v", err)
- }
- if len(code) == 0 {
- t.Errorf("did not get code for account that has contract code")
- }
- // ensure code received equals code deployed
- if !bytes.Equal(code, common.FromHex(deployedCode)) {
- t.Errorf("code received did not match expected deployed code:\n expected %v\n actual %v", common.FromHex(deployedCode), code)
- }
-}
-
-func TestCodeAtHash(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
- sim := simTestBackend(testAddr)
- defer sim.Close()
- bgCtx := context.Background()
- code, err := sim.CodeAtHash(bgCtx, testAddr, sim.Blockchain().CurrentHeader().Hash())
- if err != nil {
- t.Errorf("could not get code at test addr: %v", err)
- }
- if len(code) != 0 {
- t.Errorf("got code for account that does not have contract code")
- }
-
- parsed, err := abi.JSON(strings.NewReader(abiJSON))
- if err != nil {
- t.Errorf("could not get code at test addr: %v", err)
- }
- auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
- contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
- if err != nil {
- t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
- }
-
- blockHash := sim.Commit(true)
- code, err = sim.CodeAtHash(bgCtx, contractAddr, blockHash)
- if err != nil {
- t.Errorf("could not get code at test addr: %v", err)
- }
- if len(code) == 0 {
- t.Errorf("did not get code for account that has contract code")
- }
- // ensure code received equals code deployed
- if !bytes.Equal(code, common.FromHex(deployedCode)) {
- t.Errorf("code received did not match expected deployed code:\n expected %v\n actual %v", common.FromHex(deployedCode), code)
- }
-}
-
-// When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt:
-//
-// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
-func TestPendingAndCallContract(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
- sim := simTestBackend(testAddr)
- defer sim.Close()
- bgCtx := context.Background()
-
- parsed, err := abi.JSON(strings.NewReader(abiJSON))
- if err != nil {
- t.Errorf("could not get code at test addr: %v", err)
- }
- contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
- addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(abiBin), sim)
- if err != nil {
- t.Errorf("could not deploy contract: %v", err)
- }
-
- input, err := parsed.Pack("receive", []byte("X"))
- if err != nil {
- t.Errorf("could not pack receive function on contract: %v", err)
- }
-
- // make sure you can call the contract in accepted state
- res, err := sim.AcceptedCallContract(bgCtx, interfaces.CallMsg{
- From: testAddr,
- To: &addr,
- Data: input,
- })
- if err != nil {
- t.Errorf("could not call receive method on contract: %v", err)
- }
- if len(res) == 0 {
- t.Errorf("result of contract call was empty: %v", res)
- }
-
- // while comparing against the byte array is more exact, also compare against the human readable string for readability
- if !bytes.Equal(res, expectedReturn) || !strings.Contains(string(res), "hello world") {
- t.Errorf("response from calling contract was expected to be 'hello world' instead received %v", string(res))
- }
-
- blockHash := sim.Commit(false)
-
- // make sure you can call the contract
- res, err = sim.CallContract(bgCtx, interfaces.CallMsg{
- From: testAddr,
- To: &addr,
- Data: input,
- }, nil)
- if err != nil {
- t.Errorf("could not call receive method on contract: %v", err)
- }
- if len(res) == 0 {
- t.Errorf("result of contract call was empty: %v", res)
- }
-
- if !bytes.Equal(res, expectedReturn) || !strings.Contains(string(res), "hello world") {
- t.Errorf("response from calling contract was expected to be 'hello world' instead received %v", string(res))
- }
-
- // make sure you can call the contract by hash
- res, err = sim.CallContractAtHash(bgCtx, interfaces.CallMsg{
- From: testAddr,
- To: &addr,
- Data: input,
- }, blockHash)
- if err != nil {
- t.Errorf("could not call receive method on contract: %v", err)
- }
- if len(res) == 0 {
- t.Errorf("result of contract call was empty: %v", res)
- }
-
- if !bytes.Equal(res, expectedReturn) || !strings.Contains(string(res), "hello world") {
- t.Errorf("response from calling contract was expected to be 'hello world' instead received %v", string(res))
- }
-}
-
-// This test is based on the following contract:
-/*
-contract Reverter {
- function revertString() public pure{
- require(false, "some error");
- }
- function revertNoString() public pure {
- require(false, "");
- }
- function revertASM() public pure {
- assembly {
- revert(0x0, 0x0)
- }
- }
- function noRevert() public pure {
- assembly {
- // Assembles something that looks like require(false, "some error") but is not reverted
- mstore(0x0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
- mstore(0x4, 0x0000000000000000000000000000000000000000000000000000000000000020)
- mstore(0x24, 0x000000000000000000000000000000000000000000000000000000000000000a)
- mstore(0x44, 0x736f6d65206572726f7200000000000000000000000000000000000000000000)
- return(0x0, 0x64)
- }
- }
-}*/
-func TestCallContractRevert(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
- sim := simTestBackend(testAddr)
- defer sim.Close()
- bgCtx := context.Background()
-
- reverterABI := `[{"inputs": [],"name": "noRevert","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertASM","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertNoString","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertString","outputs": [],"stateMutability": "pure","type": "function"}]`
- reverterBin := "608060405234801561001057600080fd5b506101d3806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80634b409e01146100515780639b340e361461005b5780639bd6103714610065578063b7246fc11461006f575b600080fd5b610059610079565b005b6100636100ca565b005b61006d6100cf565b005b610077610145565b005b60006100c8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401808060200182810382526000815260200160200191505060405180910390fd5b565b600080fd5b6000610143576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600a8152602001807f736f6d65206572726f720000000000000000000000000000000000000000000081525060200191505060405180910390fd5b565b7f08c379a0000000000000000000000000000000000000000000000000000000006000526020600452600a6024527f736f6d65206572726f720000000000000000000000000000000000000000000060445260646000f3fea2646970667358221220cdd8af0609ec4996b7360c7c780bad5c735740c64b1fffc3445aa12d37f07cb164736f6c63430006070033"
-
- parsed, err := abi.JSON(strings.NewReader(reverterABI))
- if err != nil {
- t.Errorf("could not get code at test addr: %v", err)
- }
- contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
- addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(reverterBin), sim)
- if err != nil {
- t.Errorf("could not deploy contract: %v", err)
- }
-
- inputs := make(map[string]interface{}, 3)
- inputs["revertASM"] = nil
- inputs["revertNoString"] = ""
- inputs["revertString"] = "some error"
-
- call := make([]func([]byte) ([]byte, error), 2)
- call[0] = func(input []byte) ([]byte, error) {
- return sim.AcceptedCallContract(bgCtx, interfaces.CallMsg{
- From: testAddr,
- To: &addr,
- Data: input,
- })
- }
- call[1] = func(input []byte) ([]byte, error) {
- return sim.CallContract(bgCtx, interfaces.CallMsg{
- From: testAddr,
- To: &addr,
- Data: input,
- }, nil)
- }
-
- // Run pending calls then commit
- for _, cl := range call {
- for key, val := range inputs {
- input, err := parsed.Pack(key)
- if err != nil {
- t.Errorf("could not pack %v function on contract: %v", key, err)
- }
-
- res, err := cl(input)
- if err == nil {
- t.Errorf("call to %v was not reverted", key)
- }
- if res != nil {
- t.Errorf("result from %v was not nil: %v", key, res)
- }
- if val != nil {
- rerr, ok := err.(*revertError)
- if !ok {
- t.Errorf("expect revert error")
- }
- if rerr.Error() != "execution reverted: "+val.(string) {
- t.Errorf("error was malformed: got %v want %v", rerr.Error(), val)
- }
- } else {
- // revert(0x0,0x0)
- if err.Error() != "execution reverted" {
- t.Errorf("error was malformed: got %v want %v", err, "execution reverted")
- }
- }
- }
- input, err := parsed.Pack("noRevert")
- if err != nil {
- t.Errorf("could not pack noRevert function on contract: %v", err)
- }
- res, err := cl(input)
- if err != nil {
- t.Error("call to noRevert was reverted")
- }
- if res == nil {
- t.Errorf("result from noRevert was nil")
- }
- sim.Commit(false)
- }
-}
-
-// TestFork check that the chain length after a reorg is correct.
-// Steps:
-// 1. Save the current block which will serve as parent for the fork.
-// 2. Mine n blocks with n ∈ [0, 20].
-// 3. Assert that the chain length is n.
-// 4. Fork by using the parent block as ancestor.
-// 5. Mine n+1 blocks which should trigger a reorg.
-// 6. Assert that the chain length is n+1.
-// Since Commit() was called 2n+1 times in total,
-// having a chain length of just n+1 means that a reorg occurred.
-func TestFork(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
- sim := simTestBackend(testAddr)
- defer sim.Close()
- // 1.
- parent := sim.blockchain.CurrentBlock()
- // 2.
- n := int(rand.Int31n(21))
- for i := 0; i < n; i++ {
- sim.Commit(false)
- }
- // 3.
- if sim.blockchain.CurrentBlock().Number.Uint64() != uint64(n) {
- t.Error("wrong chain length")
- }
- // 4.
- sim.Fork(context.Background(), parent.Hash())
- // 5.
- for i := 0; i < n+1; i++ {
- sim.Commit(false)
- }
- // 6.
- if sim.blockchain.CurrentBlock().Number.Uint64() != uint64(n+1) {
- t.Error("wrong chain length")
- }
-}
-
-/*
-Example contract to test event emission:
-
- pragma solidity >=0.7.0 <0.9.0;
- contract Callable {
- event Called();
- function Call() public { emit Called(); }
- }
-*/
-// The fork tests are commented out because transactions are not indexed in subnet-evm until they are marked
-// as accepted, which breaks the logic of these tests.
-// const callableAbi = "[{\"anonymous\":false,\"inputs\":[],\"name\":\"Called\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"Call\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
-
-// const callableBin = "6080604052348015600f57600080fd5b5060998061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806334e2292114602d575b600080fd5b60336035565b005b7f81fab7a4a0aa961db47eefc81f143a5220e8c8495260dd65b1356f1d19d3c7b860405160405180910390a156fea2646970667358221220029436d24f3ac598ceca41d4d712e13ced6d70727f4cdc580667de66d2f51d8b64736f6c63430008010033"
-
-// // TestForkLogsReborn check that the simulated reorgs
-// // correctly remove and reborn logs.
-// // Steps:
-// // 1. Deploy the Callable contract.
-// // 2. Set up an event subscription.
-// // 3. Save the current block which will serve as parent for the fork.
-// // 4. Send a transaction.
-// // 5. Check that the event was included.
-// // 6. Fork by using the parent block as ancestor.
-// // 7. Mine two blocks to trigger a reorg.
-// // 8. Check that the event was removed.
-// // 9. Re-send the transaction and mine a block.
-// // 10. Check that the event was reborn.
-// func TestForkLogsReborn(t *testing.T) {
-// t.Parallel()
-// testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
-// sim := simTestBackend(testAddr)
-// defer sim.Close()
-// // 1.
-// parsed, _ := abi.JSON(strings.NewReader(callableAbi))
-// auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
-// _, _, contract, err := bind.DeployContract(auth, parsed, common.FromHex(callableBin), sim)
-// if err != nil {
-// t.Errorf("deploying contract: %v", err)
-// }
-// sim.Commit()
-// // 2.
-// logs, sub, err := contract.WatchLogs(nil, "Called")
-// if err != nil {
-// t.Errorf("watching logs: %v", err)
-// }
-// defer sub.Unsubscribe()
-// // 3.
-// parent := sim.blockchain.CurrentBlock()
-// // 4.
-// tx, err := contract.Transact(auth, "Call")
-// if err != nil {
-// t.Errorf("transacting: %v", err)
-// }
-// sim.Commit()
-// // 5.
-// log := <-logs
-// if log.TxHash != tx.Hash() {
-// t.Error("wrong event tx hash")
-// }
-// if log.Removed {
-// t.Error("Event should be included")
-// }
-// // 6.
-// if err := sim.Fork(context.Background(), parent.Hash()); err != nil {
-// t.Errorf("forking: %v", err)
-// }
-// // 7.
-// sim.Commit()
-// sim.Commit()
-// // 8.
-// log = <-logs
-// if log.TxHash != tx.Hash() {
-// t.Error("wrong event tx hash")
-// }
-// if !log.Removed {
-// t.Error("Event should be removed")
-// }
-// // 9.
-// if err := sim.SendTransaction(context.Background(), tx); err != nil {
-// t.Errorf("sending transaction: %v", err)
-// }
-// sim.Commit()
-// // 10.
-// log = <-logs
-// if log.TxHash != tx.Hash() {
-// t.Error("wrong event tx hash")
-// }
-// if log.Removed {
-// t.Error("Event should be included")
-// }
-// }
-//
-// // TestForkResendTx checks that re-sending a TX after a fork
-// // is possible and does not cause a "nonce mismatch" panic.
-// // Steps:
-// // 1. Save the current block which will serve as parent for the fork.
-// // 2. Send a transaction.
-// // 3. Check that the TX is included in block 1.
-// // 4. Fork by using the parent block as ancestor.
-// // 5. Mine a block, Re-send the transaction and mine another one.
-// // 6. Check that the TX is now included in block 2.
-// func TestForkResendTx(t *testing.T) {
-// t.Parallel()
-// testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
-// sim := simTestBackend(testAddr)
-// defer sim.Close()
-// // 1.
-// parent := sim.blockchain.CurrentBlock()
-// // 2.
-// head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
-// gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
-//
-// _tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
-// signer := types.NewLondonSigner(big.NewInt(1337))
-// tx, _ := types.SignTx(_tx, signer, testKey)
-// sim.SendTransaction(context.Background(), tx)
-// sim.Commit(false)
-// // 3.
-// receipt, _ := sim.TransactionReceipt(context.Background(), tx.Hash())
-// if h := receipt.BlockNumber.Uint64(); h != 1 {
-// t.Errorf("TX included in wrong block: %d", h)
-// }
-// // 4.
-// if err := sim.Fork(context.Background(), parent.Hash()); err != nil {
-// t.Errorf("forking: %v", err)
-// }
-// // 5.
-// sim.Commit(false)
-// if err := sim.SendTransaction(context.Background(), tx); err != nil {
-// t.Errorf("sending transaction: %v", err)
-// }
-// sim.Commit(false)
-// // 6.
-// receipt, _ = sim.TransactionReceipt(context.Background(), tx.Hash())
-// if h := receipt.BlockNumber.Uint64(); h != 2 {
-// t.Errorf("TX included in wrong block: %d", h)
-// }
-// }
-
-func TestCommitReturnValue(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
- sim := simTestBackend(testAddr)
- defer sim.Close()
-
- startBlockHeight := sim.blockchain.CurrentBlock().Number.Uint64()
-
- // Test if Commit returns the correct block hash
- h1 := sim.Commit(true)
- if h1 != sim.blockchain.CurrentBlock().Hash() {
- t.Error("Commit did not return the hash of the last block.")
- }
-
- // Create a block in the original chain (containing a transaction to force different block hashes)
- head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
- gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
- _tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
- tx, _ := types.SignTx(_tx, types.HomesteadSigner{}, testKey)
- sim.SendTransaction(context.Background(), tx)
- h2 := sim.Commit(false)
-
- // Create another block in the original chain
- sim.Commit(false)
-
- // Fork at the first bock
- if err := sim.Fork(context.Background(), h1); err != nil {
- t.Errorf("forking: %v", err)
- }
-
- // Test if Commit returns the correct block hash after the reorg
- h2fork := sim.Commit(false)
- if h2 == h2fork {
- t.Error("The block in the fork and the original block are the same block!")
- }
- if sim.blockchain.GetHeader(h2fork, startBlockHeight+2) == nil {
- t.Error("Could not retrieve the just created block (side-chain)")
- }
-}
-
-// TestAdjustTimeAfterFork ensures that after a fork, AdjustTime uses the pending fork
-// block's parent rather than the canonical head's parent.
-func TestAdjustTimeAfterFork(t *testing.T) {
- t.Parallel()
- testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
- sim := simTestBackend(testAddr)
- defer sim.Close()
-
- sim.Commit(false) // h1
- h1 := sim.blockchain.CurrentHeader().Hash()
- sim.Commit(false) // h2
- sim.Fork(context.Background(), h1)
- sim.AdjustTime(1 * time.Second)
- sim.Commit(false)
-
- head := sim.blockchain.CurrentHeader()
- if head.Number == common.Big2 && head.ParentHash != h1 {
- t.Errorf("failed to build block on fork")
- }
-}
diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go
index 9aff177c1c..50c4a74ac1 100644
--- a/accounts/abi/bind/base.go
+++ b/accounts/abi/bind/base.go
@@ -37,6 +37,7 @@ import (
"github.com/ava-labs/subnet-evm/accounts/abi"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/interfaces"
+ "github.com/ava-labs/subnet-evm/rpc"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event"
@@ -387,7 +388,8 @@ func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Ad
func (c *BoundContract) getNonce(opts *TransactOpts) (uint64, error) {
if opts.Nonce == nil {
- return c.transactor.AcceptedNonceAt(ensureContext(opts.Context), opts.From)
+ pendingBlock := big.NewInt(int64(rpc.PendingBlockNumber))
+ return c.transactor.NonceAt(ensureContext(opts.Context), opts.From, pendingBlock)
} else {
return opts.Nonce.Uint64(), nil
}
diff --git a/accounts/abi/bind/base_test.go b/accounts/abi/bind/base_test.go
index 0f420a32eb..8ed30c23a4 100644
--- a/accounts/abi/bind/base_test.go
+++ b/accounts/abi/bind/base_test.go
@@ -63,7 +63,7 @@ func (mt *mockTransactor) AcceptedCodeAt(ctx context.Context, account common.Add
return []byte{1}, nil
}
-func (mt *mockTransactor) AcceptedNonceAt(ctx context.Context, account common.Address) (uint64, error) {
+func (mt *mockTransactor) NonceAt(ctx context.Context, account common.Address, blockNum *big.Int) (uint64, error) {
return 0, nil
}
diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go
index 4ccb21ba91..e2832add64 100644
--- a/accounts/abi/bind/bind_test.go
+++ b/accounts/abi/bind/bind_test.go
@@ -299,7 +299,7 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
`
@@ -307,7 +307,7 @@ var bindTests = []struct {
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
defer sim.Close()
// Deploy an interaction tester contract and call a transaction on it
@@ -315,6 +315,7 @@ var bindTests = []struct {
if err != nil {
t.Fatalf("Failed to deploy interactor contract: %v", err)
}
+ sim.Commit(false)
if _, err := interactor.Transact(auth, "Transact string"); err != nil {
t.Fatalf("Failed to transact with interactor contract: %v", err)
}
@@ -354,7 +355,7 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
`
@@ -362,7 +363,7 @@ var bindTests = []struct {
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
defer sim.Close()
// Deploy a tuple tester contract and execute a structured call on it
@@ -400,7 +401,7 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
`
@@ -408,7 +409,7 @@ var bindTests = []struct {
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
defer sim.Close()
// Deploy a tuple tester contract and execute a structured call on it
@@ -458,7 +459,7 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
`
@@ -466,7 +467,7 @@ var bindTests = []struct {
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
defer sim.Close()
// Deploy a slice tester contract and execute a n array call on it
@@ -506,7 +507,7 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
`
@@ -514,7 +515,7 @@ var bindTests = []struct {
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
defer sim.Close()
// Deploy a default method invoker contract and execute its default method
@@ -522,6 +523,7 @@ var bindTests = []struct {
if err != nil {
t.Fatalf("Failed to deploy defaulter contract: %v", err)
}
+ sim.Commit(false)
if _, err := (&DefaulterRaw{defaulter}).Transfer(auth); err != nil {
t.Fatalf("Failed to invoke default method: %v", err)
}
@@ -572,7 +574,7 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
`
@@ -580,7 +582,7 @@ var bindTests = []struct {
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
defer sim.Close()
// Deploy a structs method invoker contract and execute its default method
@@ -618,12 +620,12 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
`,
`
// Create a simulator and wrap a non-deployed contract
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{}, uint64(10000000000))
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{}, uint64(10000000000))
defer sim.Close()
nonexistent, err := NewNonExistent(common.Address{}, sim)
@@ -657,12 +659,12 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
`,
`
// Create a simulator and wrap a non-deployed contract
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{}, uint64(10000000000))
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{}, uint64(10000000000))
defer sim.Close()
nonexistent, err := NewNonExistentStruct(common.Address{}, sim)
@@ -704,7 +706,7 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
`
@@ -712,7 +714,7 @@ var bindTests = []struct {
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
defer sim.Close()
// Deploy a funky gas pattern contract
@@ -754,7 +756,7 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
`
@@ -762,7 +764,7 @@ var bindTests = []struct {
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
defer sim.Close()
// Deploy a sender tester contract and execute a structured call on it
@@ -829,7 +831,7 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
`
@@ -837,7 +839,7 @@ var bindTests = []struct {
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
defer sim.Close()
// Deploy a underscorer tester contract and execute a structured call on it
@@ -923,7 +925,7 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
`
@@ -931,7 +933,7 @@ var bindTests = []struct {
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
defer sim.Close()
// Deploy an eventer contract
@@ -1113,7 +1115,7 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
`
@@ -1121,7 +1123,7 @@ var bindTests = []struct {
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
defer sim.Close()
//deploy the test contract
@@ -1248,7 +1250,7 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
@@ -1256,7 +1258,7 @@ var bindTests = []struct {
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
defer sim.Close()
_, _, contract, err := DeployTuple(auth, sim)
@@ -1390,7 +1392,7 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
`
@@ -1398,7 +1400,7 @@ var bindTests = []struct {
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
defer sim.Close()
//deploy the test contract
@@ -1456,14 +1458,14 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
`
// Initialize test accounts
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000))}}, 10000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000))}}, 10000000)
defer sim.Close()
// deploy the test contract
@@ -1566,7 +1568,7 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
`,
`
// Initialize test accounts
@@ -1574,7 +1576,7 @@ var bindTests = []struct {
addr := crypto.PubkeyToAddress(key.PublicKey)
// Deploy registrar contract
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000))}}, 10000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{addr: {Balance: new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000))}}, 10000000)
defer sim.Close()
transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
@@ -1629,14 +1631,14 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
`,
`
key, _ := crypto.GenerateKey()
addr := crypto.PubkeyToAddress(key.PublicKey)
// Deploy registrar contract
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{addr: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
defer sim.Close()
transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
@@ -1690,7 +1692,7 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
`
@@ -1698,7 +1700,7 @@ var bindTests = []struct {
key, _ := crypto.GenerateKey()
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
defer sim.Close()
// Deploy a tester contract and execute a structured call on it
@@ -1751,15 +1753,14 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
`
key, _ := crypto.GenerateKey()
addr := crypto.PubkeyToAddress(key.PublicKey)
-
- sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000000000000)}}, 1000000)
+ sim := backends.NewSimulatedBackend(types.GenesisAlloc{addr: {Balance: big.NewInt(1000000000000000000)}}, 1000000)
defer sim.Close()
opts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
@@ -1840,14 +1841,14 @@ var bindTests = []struct {
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
`
var (
key, _ = crypto.GenerateKey()
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
)
defer sim.Close()
@@ -1905,18 +1906,19 @@ var bindTests = []struct {
[]string{"0x6080604052348015600f57600080fd5b5060998061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063726c638214602d575b600080fd5b60336035565b005b60405163024876cd60e61b815260016004820152600260248201526003604482015260640160405180910390fdfea264697066735822122093f786a1bc60216540cd999fbb4a6109e0fef20abcff6e9107fb2817ca968f3c64736f6c63430008070033"},
[]string{`[{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError1","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError2","type":"error"},{"inputs":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"b","type":"uint256"},{"internalType":"uint256","name":"c","type":"uint256"}],"name":"MyError3","type":"error"},{"inputs":[],"name":"Error","outputs":[],"stateMutability":"pure","type":"function"}]`},
`
+ "context"
"math/big"
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
`
var (
key, _ = crypto.GenerateKey()
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
)
defer sim.Close()
@@ -1925,7 +1927,7 @@ var bindTests = []struct {
t.Fatal(err)
}
sim.Commit(true)
- _, err = bind.WaitDeployed(nil, sim, tx)
+ _, err = bind.WaitDeployed(context.Background(), sim, tx)
if err != nil {
t.Error(err)
}
@@ -1956,18 +1958,19 @@ var bindTests = []struct {
bytecode: []string{`0x608060405234801561001057600080fd5b506040516101c43803806101c48339818101604052810190610032919061014a565b50610177565b6000604051905090565b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6100958261004c565b810181811067ffffffffffffffff821117156100b4576100b361005d565b5b80604052505050565b60006100c7610038565b90506100d3828261008c565b919050565b6000819050919050565b6100eb816100d8565b81146100f657600080fd5b50565b600081519050610108816100e2565b92915050565b60006020828403121561012457610123610047565b5b61012e60206100bd565b9050600061013e848285016100f9565b60008301525092915050565b6000602082840312156101605761015f610042565b5b600061016e8482850161010e565b91505092915050565b603f806101856000396000f3fe6080604052600080fdfea2646970667358221220cdffa667affecefac5561f65f4a4ba914204a8d4eb859d8cd426fb306e5c12a364736f6c634300080a0033`},
abi: []string{`[{"inputs":[{"components":[{"internalType":"uint256","name":"field","type":"uint256"}],"internalType":"struct ConstructorWithStructParam.StructType","name":"st","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"}]`},
imports: `
+ "context"
"math/big"
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
tester: `
var (
key, _ = crypto.GenerateKey()
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
+ sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, 10000000)
)
defer sim.Close()
@@ -1977,7 +1980,7 @@ var bindTests = []struct {
}
sim.Commit(true)
- if _, err = bind.WaitDeployed(nil, sim, tx); err != nil {
+ if _, err = bind.WaitDeployed(context.Background(), sim, tx); err != nil {
t.Logf("Deployment tx: %+v", tx)
t.Errorf("bind.WaitDeployed(nil, %T, ) got err %v; want nil err", sim, err)
}
@@ -2003,11 +2006,12 @@ var bindTests = []struct {
bytecode: []string{"0x608060405234801561001057600080fd5b5061042b806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063c2bb515f1461003b578063cce7b04814610059575b600080fd5b610043610075565b60405161005091906101af565b60405180910390f35b610073600480360381019061006e91906103ac565b6100b5565b005b61007d6100b8565b604051806040016040528060405180602001604052806000815250815260200160405180602001604052806000815250815250905090565b50565b604051806040016040528060608152602001606081525090565b600081519050919050565b600082825260208201905092915050565b60005b8381101561010c5780820151818401526020810190506100f1565b8381111561011b576000848401525b50505050565b6000601f19601f8301169050919050565b600061013d826100d2565b61014781856100dd565b93506101578185602086016100ee565b61016081610121565b840191505092915050565b600060408301600083015184820360008601526101888282610132565b915050602083015184820360208601526101a28282610132565b9150508091505092915050565b600060208201905081810360008301526101c9818461016b565b905092915050565b6000604051905090565b600080fd5b600080fd5b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61022282610121565b810181811067ffffffffffffffff82111715610241576102406101ea565b5b80604052505050565b60006102546101d1565b90506102608282610219565b919050565b600080fd5b600080fd5b600080fd5b600067ffffffffffffffff82111561028f5761028e6101ea565b5b61029882610121565b9050602081019050919050565b82818337600083830152505050565b60006102c76102c284610274565b61024a565b9050828152602081018484840111156102e3576102e261026f565b5b6102ee8482856102a5565b509392505050565b600082601f83011261030b5761030a61026a565b5b813561031b8482602086016102b4565b91505092915050565b60006040828403121561033a576103396101e5565b5b610344604061024a565b9050600082013567ffffffffffffffff81111561036457610363610265565b5b610370848285016102f6565b600083015250602082013567ffffffffffffffff81111561039457610393610265565b5b6103a0848285016102f6565b60208301525092915050565b6000602082840312156103c2576103c16101db565b5b600082013567ffffffffffffffff8111156103e0576103df6101e0565b5b6103ec84828501610324565b9150509291505056fea264697066735822122033bca1606af9b6aeba1673f98c52003cec19338539fb44b86690ce82c51483b564736f6c634300080e0033"},
abi: []string{`[ { "anonymous": false, "inputs": [ { "indexed": false, "internalType": "int256", "name": "msg", "type": "int256" }, { "indexed": false, "internalType": "int256", "name": "_msg", "type": "int256" } ], "name": "log", "type": "event" }, { "inputs": [ { "components": [ { "internalType": "bytes", "name": "data", "type": "bytes" }, { "internalType": "bytes", "name": "_data", "type": "bytes" } ], "internalType": "struct oracle.request", "name": "req", "type": "tuple" } ], "name": "addRequest", "outputs": [], "stateMutability": "pure", "type": "function" }, { "inputs": [], "name": "getRequest", "outputs": [ { "components": [ { "internalType": "bytes", "name": "data", "type": "bytes" }, { "internalType": "bytes", "name": "_data", "type": "bytes" } ], "internalType": "struct oracle.request", "name": "", "type": "tuple" } ], "stateMutability": "pure", "type": "function" } ]`},
imports: `
+ "context"
"math/big"
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
tester: `
@@ -2015,7 +2019,7 @@ var bindTests = []struct {
gasCeil = uint64(30000000) // Note: from geth's ethconfig.Defaults.Miner.GasCeil
key, _ = crypto.GenerateKey()
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, gasCeil)
+ sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, gasCeil)
)
defer sim.Close()
@@ -2025,7 +2029,7 @@ var bindTests = []struct {
}
sim.Commit(true)
- if _, err = bind.WaitDeployed(nil, sim, tx); err != nil {
+ if _, err = bind.WaitDeployed(context.Background(), sim, tx); err != nil {
t.Logf("Deployment tx: %+v", tx)
t.Errorf("bind.WaitDeployed(nil, %T, ) got err %v; want nil err", sim, err)
}
@@ -2043,11 +2047,12 @@ var bindTests = []struct {
bytecode: []string{"0x608060405234801561001057600080fd5b5060dc8061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063527a119f14602d575b600080fd5b60436004803603810190603f9190605b565b6045565b005b50565b6000813590506055816092565b92915050565b600060208284031215606e57606d608d565b5b6000607a848285016048565b91505092915050565b6000819050919050565b600080fd5b6099816083565b811460a357600080fd5b5056fea2646970667358221220d4f4525e2615516394055d369fb17df41c359e5e962734f27fd683ea81fd9db164736f6c63430008070033"},
abi: []string{`[{"inputs":[{"internalType":"uint256","name":"range","type":"uint256"}],"name":"functionWithKeywordParameter","outputs":[],"stateMutability":"pure","type":"function"}]`},
imports: `
+ "context"
"math/big"
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
"github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/crypto"
`,
tester: `
@@ -2055,7 +2060,7 @@ var bindTests = []struct {
gasCeil = uint64(30000000) // Note: from geth's ethconfig.Defaults.Miner.GasCeil
key, _ = crypto.GenerateKey()
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
- sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, gasCeil)
+ sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, gasCeil)
)
_, tx, _, err := DeployRangeKeyword(user, sim)
if err != nil {
@@ -2063,7 +2068,7 @@ var bindTests = []struct {
}
sim.Commit(true)
- if _, err = bind.WaitDeployed(nil, sim, tx); err != nil {
+ if _, err = bind.WaitDeployed(context.Background(), sim, tx); err != nil {
t.Errorf("error deploying the contract: %v", err)
}
`,
diff --git a/accounts/abi/bind/util_test.go b/accounts/abi/bind/util_test.go
index db46bcbbb6..93b03be810 100644
--- a/accounts/abi/bind/util_test.go
+++ b/accounts/abi/bind/util_test.go
@@ -34,9 +34,9 @@ import (
"time"
"github.com/ava-labs/subnet-evm/accounts/abi/bind"
- "github.com/ava-labs/subnet-evm/accounts/abi/bind/backends"
- "github.com/ava-labs/subnet-evm/core"
"github.com/ava-labs/subnet-evm/core/types"
+ "github.com/ava-labs/subnet-evm/ethclient/simulated"
+ "github.com/ava-labs/subnet-evm/params"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
@@ -65,21 +65,19 @@ var waitDeployedTests = map[string]struct {
func TestWaitDeployed(t *testing.T) {
t.Parallel()
for name, test := range waitDeployedTests {
- backend := backends.NewSimulatedBackend(
- core.GenesisAlloc{
+ backend := simulated.NewBackend(
+ types.GenesisAlloc{
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: new(big.Int).Mul(big.NewInt(10000000000000000), big.NewInt(1000))},
},
- 10000000,
)
defer backend.Close()
// Create the transaction
- head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
- gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
+ head, _ := backend.Client().HeaderByNumber(context.Background(), nil) // Should be child's, good enough
+ gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(params.GWei))
tx := types.NewContractCreation(0, big.NewInt(0), test.gas, gasPrice, common.FromHex(test.code))
- signer := types.NewLondonSigner(big.NewInt(1337))
- tx, _ = types.SignTx(tx, signer, testKey)
+ tx, _ = types.SignTx(tx, types.LatestSignerForChainID(big.NewInt(1337)), testKey)
// Wait for it to get mined in the background.
var (
@@ -89,13 +87,13 @@ func TestWaitDeployed(t *testing.T) {
ctx = context.Background()
)
go func() {
- address, err = bind.WaitDeployed(ctx, backend, tx)
+ address, err = bind.WaitDeployed(ctx, backend.Client(), tx)
close(mined)
}()
// Send and mine the transaction.
- if err := backend.SendTransaction(ctx, tx); err != nil {
- t.Errorf("Failed to send transaction: %s", err)
+ if err := backend.Client().SendTransaction(ctx, tx); err != nil {
+ t.Fatalf("Failed to send transaction: %s", err)
}
backend.Commit(true)
@@ -114,42 +112,40 @@ func TestWaitDeployed(t *testing.T) {
}
func TestWaitDeployedCornerCases(t *testing.T) {
- t.Parallel()
- backend := backends.NewSimulatedBackend(
- core.GenesisAlloc{
+ backend := simulated.NewBackend(
+ types.GenesisAlloc{
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(1000000000000000000)},
},
- 10000000,
)
defer backend.Close()
- head, _ := backend.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
+ head, _ := backend.Client().HeaderByNumber(context.Background(), nil) // Should be child's, good enough
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
// Create a transaction to an account.
code := "6060604052600a8060106000396000f360606040526008565b00"
tx := types.NewTransaction(0, common.HexToAddress("0x01"), big.NewInt(0), 3000000, gasPrice, common.FromHex(code))
- tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
+ tx, _ = types.SignTx(tx, types.LatestSigner(params.TestChainConfig), testKey)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
- backend.SendTransaction(ctx, tx)
+ backend.Client().SendTransaction(ctx, tx)
backend.Commit(true)
notContractCreation := errors.New("tx is not contract creation")
- if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != notContractCreation.Error() {
+ if _, err := bind.WaitDeployed(ctx, backend.Client(), tx); err.Error() != notContractCreation.Error() {
t.Errorf("error mismatch: want %q, got %q, ", notContractCreation, err)
}
// Create a transaction that is not mined.
tx = types.NewContractCreation(1, big.NewInt(0), 3000000, gasPrice, common.FromHex(code))
- tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
+ tx, _ = types.SignTx(tx, types.LatestSigner(params.TestChainConfig), testKey)
go func() {
contextCanceled := errors.New("context canceled")
- if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != contextCanceled.Error() {
+ if _, err := bind.WaitDeployed(ctx, backend.Client(), tx); err.Error() != contextCanceled.Error() {
t.Errorf("error mismatch: want %q, got %q, ", contextCanceled, err)
}
}()
- backend.SendTransaction(ctx, tx)
+ backend.Client().SendTransaction(ctx, tx)
cancel()
}
diff --git a/accounts/abi/topics_test.go b/accounts/abi/topics_test.go
index 8e3c83cbda..70522b6ce6 100644
--- a/accounts/abi/topics_test.go
+++ b/accounts/abi/topics_test.go
@@ -36,8 +36,6 @@ import (
"github.com/ethereum/go-ethereum/crypto"
)
-var MaxHash = common.HexToHash("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
-
func TestMakeTopics(t *testing.T) {
t.Parallel()
type args struct {
@@ -86,7 +84,7 @@ func TestMakeTopics(t *testing.T) {
{big.NewInt(math.MinInt64)},
}},
[][]common.Hash{
- {MaxHash},
+ {common.MaxHash},
{common.HexToHash("ffffffffffffffffffffffffffffffffffffffffffffffff8000000000000000")},
},
false,
diff --git a/accounts/keystore/passphrase.go b/accounts/keystore/passphrase.go
index 98cf02ba37..7b1ce5945a 100644
--- a/accounts/keystore/passphrase.go
+++ b/accounts/keystore/passphrase.go
@@ -146,7 +146,7 @@ func (ks keyStorePassphrase) JoinPath(filename string) string {
return filepath.Join(ks.keysDirPath, filename)
}
-// Encryptdata encrypts the data given as 'data' with the password 'auth'.
+// EncryptDataV3 encrypts the data given as 'data' with the password 'auth'.
func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {
salt := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
diff --git a/accounts/scwallet/hub.go b/accounts/scwallet/hub.go
index 2a933651fb..3a81e084cf 100644
--- a/accounts/scwallet/hub.go
+++ b/accounts/scwallet/hub.go
@@ -251,7 +251,7 @@ func (hub *Hub) refreshWallets() {
card.Disconnect(pcsc.LeaveCard)
continue
}
- // Card connected, start tracking in amongs the wallets
+ // Card connected, start tracking among the wallets
hub.wallets[reader] = wallet
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
}
diff --git a/cmd/evm/README.md b/cmd/evm/README.md
index 6c70e0dd3b..6306dbf892 100644
--- a/cmd/evm/README.md
+++ b/cmd/evm/README.md
@@ -214,7 +214,7 @@ exitcode:3 OK
The chain configuration to be used for a transition is specified via the
`--state.fork` CLI flag. A list of possible values and configurations can be
-found in [`tests/init.go`](tests/init.go).
+found in [`tests/init.go`](../../tests/init.go).
#### Examples
##### Basic usage
diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go
index ea4bf549dc..f44e01d7ac 100644
--- a/cmd/evm/internal/t8ntool/execution.go
+++ b/cmd/evm/internal/t8ntool/execution.go
@@ -38,18 +38,20 @@ import (
"github.com/ava-labs/subnet-evm/core/vm"
"github.com/ava-labs/subnet-evm/params"
"github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/holiman/uint256"
"golang.org/x/crypto/sha3"
)
type Prestate struct {
- Env stEnv `json:"env"`
- Pre core.GenesisAlloc `json:"pre"`
+ Env stEnv `json:"env"`
+ Pre types.GenesisAlloc `json:"pre"`
}
// ExecutionResult contains the execution status after running a state test, any
@@ -148,6 +150,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
rejectedTxs []*rejectedTx
includedTxs types.Transactions
gasUsed = uint64(0)
+ blobGasUsed = uint64(0)
receipts = make(types.Receipts, 0)
txIndex = 0
)
@@ -198,7 +201,6 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb)
}
- var blobGasUsed uint64
for i := 0; txIt.Next(); i++ {
tx, err := txIt.Tx()
@@ -219,15 +221,15 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
continue
}
+ txBlobGas := uint64(0)
if tx.Type() == types.BlobTxType {
- txBlobGas := uint64(params.BlobTxBlobGasPerBlob * len(tx.BlobHashes()))
+ txBlobGas = uint64(params.BlobTxBlobGasPerBlob * len(tx.BlobHashes()))
if used, max := blobGasUsed+txBlobGas, uint64(params.MaxBlobGasPerBlock); used > max {
err := fmt.Errorf("blob gas (%d) would exceed maximum allowance %d", used, max)
log.Warn("rejected tx", "index", i, "err", err)
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
continue
}
- blobGasUsed += txBlobGas
}
tracer, err := getTracerFn(txIndex, tx.Hash())
if err != nil {
@@ -256,6 +258,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
if hashError != nil {
return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError)
}
+ blobGasUsed += txBlobGas
gasUsed += msgResult.UsedGas
// Receipt:
@@ -316,9 +319,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
reward.Sub(reward, new(big.Int).SetUint64(ommer.Delta))
reward.Mul(reward, blockReward)
reward.Div(reward, big.NewInt(8))
- statedb.AddBalance(ommer.Address, reward)
+ statedb.AddBalance(ommer.Address, uint256.MustFromBig(reward))
}
- statedb.AddBalance(pre.Env.Coinbase, minerReward)
+ statedb.AddBalance(pre.Env.Coinbase, uint256.MustFromBig(minerReward))
}
// Commit block
root, err := statedb.Commit(vmContext.BlockNumber.Uint64(), chainConfig.IsEIP158(vmContext.BlockNumber), false)
@@ -351,13 +354,13 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
return statedb, execRs, body, nil
}
-func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
- sdb := state.NewDatabaseWithConfig(db, &trie.Config{Preimages: true})
+func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB {
+ sdb := state.NewDatabaseWithConfig(db, &triedb.Config{Preimages: true})
statedb, _ := state.New(types.EmptyRootHash, sdb, nil)
for addr, a := range accounts {
statedb.SetCode(addr, a.Code)
statedb.SetNonce(addr, a.Nonce)
- statedb.SetBalance(addr, a.Balance)
+ statedb.SetBalance(addr, uint256.MustFromBig(a.Balance))
for k, v := range a.Storage {
statedb.SetState(addr, k, v)
}
diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go
index 5bb33c1e91..d2d957875d 100644
--- a/cmd/evm/internal/t8ntool/transition.go
+++ b/cmd/evm/internal/t8ntool/transition.go
@@ -35,7 +35,6 @@ import (
"path"
"github.com/ava-labs/subnet-evm/consensus/dummy"
- "github.com/ava-labs/subnet-evm/core"
"github.com/ava-labs/subnet-evm/core/state"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/core/vm"
@@ -84,10 +83,10 @@ var (
)
type input struct {
- Alloc core.GenesisAlloc `json:"alloc,omitempty"`
- Env *stEnv `json:"env,omitempty"`
- Txs []*txWithKey `json:"txs,omitempty"`
- TxRlp string `json:"txsRlp,omitempty"`
+ Alloc types.GenesisAlloc `json:"alloc,omitempty"`
+ Env *stEnv `json:"env,omitempty"`
+ Txs []*txWithKey `json:"txs,omitempty"`
+ TxRlp string `json:"txsRlp,omitempty"`
}
func Transition(ctx *cli.Context) error {
@@ -198,7 +197,7 @@ func Transition(ctx *cli.Context) error {
if err != nil {
return err
}
- // Dump the excution result
+ // Dump the execution result
collector := make(Alloc)
s.DumpToCollector(collector, nil)
return dispatchOutput(ctx, baseDir, result, collector, body)
@@ -251,7 +250,7 @@ func applyCancunChecks(env *stEnv, chainConfig *params.ChainConfig) error {
return nil
}
-type Alloc map[common.Address]core.GenesisAccount
+type Alloc map[common.Address]types.Account
func (g Alloc) OnRoot(common.Hash) {}
@@ -259,7 +258,7 @@ func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
if addr == nil {
return
}
- balance, _ := new(big.Int).SetString(dumpAccount.Balance, 10)
+ balance, _ := new(big.Int).SetString(dumpAccount.Balance, 0)
var storage map[common.Hash]common.Hash
if dumpAccount.Storage != nil {
storage = make(map[common.Hash]common.Hash)
@@ -267,7 +266,7 @@ func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
storage[k] = common.HexToHash(v)
}
}
- genesisAccount := core.GenesisAccount{
+ genesisAccount := types.Account{
Code: dumpAccount.Code,
Storage: storage,
Balance: balance,
diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go
index d182715ca4..48902609ba 100644
--- a/cmd/evm/runner.go
+++ b/cmd/evm/runner.go
@@ -47,8 +47,8 @@ import (
"github.com/ava-labs/subnet-evm/eth/tracers/logger"
"github.com/ava-labs/subnet-evm/internal/flags"
"github.com/ava-labs/subnet-evm/params"
- "github.com/ava-labs/subnet-evm/trie"
- "github.com/ava-labs/subnet-evm/trie/triedb/hashdb"
+ "github.com/ava-labs/subnet-evm/triedb"
+ "github.com/ava-labs/subnet-evm/triedb/hashdb"
"github.com/ethereum/go-ethereum/common"
"github.com/urfave/cli/v2"
)
@@ -158,7 +158,7 @@ func runCmd(ctx *cli.Context) error {
}
db := rawdb.NewMemoryDatabase()
- triedb := trie.NewDatabase(db, &trie.Config{
+ triedb := triedb.NewDatabase(db, &triedb.Config{
Preimages: preimages,
HashDB: hashdb.Defaults,
})
diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go
index db8aca19e8..2c0e77385d 100644
--- a/cmd/evm/staterunner.go
+++ b/cmd/evm/staterunner.go
@@ -34,7 +34,6 @@ import (
"github.com/ava-labs/subnet-evm/core/rawdb"
"github.com/ava-labs/subnet-evm/core/state"
- "github.com/ava-labs/subnet-evm/core/state/snapshot"
"github.com/ava-labs/subnet-evm/core/vm"
"github.com/ava-labs/subnet-evm/eth/tracers/logger"
"github.com/ava-labs/subnet-evm/tests"
@@ -100,26 +99,27 @@ func runStateTest(fname string, cfg vm.Config, jsonOut, dump bool) error {
if err != nil {
return err
}
- var tests map[string]tests.StateTest
- if err := json.Unmarshal(src, &tests); err != nil {
+ var testsByName map[string]tests.StateTest
+ if err := json.Unmarshal(src, &testsByName); err != nil {
return err
}
+
// Iterate over all the tests, run them and aggregate the results
- results := make([]StatetestResult, 0, len(tests))
- for key, test := range tests {
+ results := make([]StatetestResult, 0, len(testsByName))
+ for key, test := range testsByName {
for _, st := range test.Subtests() {
// Run the test and aggregate the result
result := &StatetestResult{Name: key, Fork: st.Fork, Pass: true}
- test.Run(st, cfg, false, rawdb.HashScheme, func(err error, snaps *snapshot.Tree, statedb *state.StateDB) {
+ test.Run(st, cfg, false, rawdb.HashScheme, func(err error, tstate *tests.StateTestState) {
var root common.Hash
- if statedb != nil {
- root = statedb.IntermediateRoot(false)
+ if tstate.StateDB != nil {
+ root = tstate.StateDB.IntermediateRoot(false)
result.Root = &root
if jsonOut {
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root)
}
if dump { // Dump any state to aid debugging
- cpy, _ := state.New(root, statedb.Database(), nil)
+ cpy, _ := state.New(root, tstate.StateDB.Database(), nil)
dump := cpy.RawDump(nil)
result.State = &dump
}
diff --git a/cmd/evm/transition-test.sh b/cmd/evm/transition-test.sh
index 52fe01e25f..48bc8b6d98 100755
--- a/cmd/evm/transition-test.sh
+++ b/cmd/evm/transition-test.sh
@@ -103,7 +103,7 @@ type Env struct {
CurrentTimestamp uint64 `json:"currentTimestamp"`
Withdrawals []*Withdrawal `json:"withdrawals"`
// optional
- CurrentDifficulty *big.Int `json:"currentDifficuly"`
+ CurrentDifficulty *big.Int `json:"currentDifficulty"`
CurrentRandom *big.Int `json:"currentRandom"`
CurrentBaseFee *big.Int `json:"currentBaseFee"`
ParentDifficulty *big.Int `json:"parentDifficulty"`
diff --git a/consensus/dummy/consensus.go b/consensus/dummy/consensus.go
index e73f94ec4d..49c6253beb 100644
--- a/consensus/dummy/consensus.go
+++ b/consensus/dummy/consensus.go
@@ -70,6 +70,13 @@ func NewFakerWithMode(mode Mode) *DummyEngine {
}
}
+func NewFakerWithModeAndClock(mode Mode, clock *mockable.Clock) *DummyEngine {
+ return &DummyEngine{
+ clock: clock,
+ consensusMode: mode,
+ }
+}
+
func NewCoinbaseFaker() *DummyEngine {
return &DummyEngine{
clock: &mockable.Clock{},
@@ -85,8 +92,8 @@ func NewFullFaker() *DummyEngine {
}
// verifyCoinbase checks that the coinbase is valid for the given [header] and [parent].
-func (self *DummyEngine) verifyCoinbase(config *params.ChainConfig, header *types.Header, parent *types.Header, chain consensus.ChainHeaderReader) error {
- if self.consensusMode.ModeSkipCoinbase {
+func (eng *DummyEngine) verifyCoinbase(config *params.ChainConfig, header *types.Header, parent *types.Header, chain consensus.ChainHeaderReader) error {
+ if eng.consensusMode.ModeSkipCoinbase {
return nil
}
// get the coinbase configured at parent
@@ -107,7 +114,7 @@ func (self *DummyEngine) verifyCoinbase(config *params.ChainConfig, header *type
return nil
}
-func (self *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, header *types.Header, parent *types.Header, chain consensus.ChainHeaderReader) error {
+func (eng *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, header *types.Header, parent *types.Header, chain consensus.ChainHeaderReader) error {
// Verify that the gas limit is <= 2^63-1
if header.GasLimit > params.MaxGasLimit {
return fmt.Errorf("invalid gasLimit: have %v, max %v", header.GasLimit, params.MaxGasLimit)
@@ -192,7 +199,7 @@ func (self *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, heade
}
// modified from consensus.go
-func (self *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header, uncle bool) error {
+func (eng *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header, uncle bool) error {
config := chain.Config()
// Ensure that we do not verify an uncle
if uncle {
@@ -213,16 +220,16 @@ func (self *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header
}
}
// Ensure gas-related header fields are correct
- if err := self.verifyHeaderGasFields(config, header, parent, chain); err != nil {
+ if err := eng.verifyHeaderGasFields(config, header, parent, chain); err != nil {
return err
}
// Ensure that coinbase is valid
- if err := self.verifyCoinbase(config, header, parent, chain); err != nil {
+ if err := eng.verifyCoinbase(config, header, parent, chain); err != nil {
return err
}
// Verify the header's timestamp
- if header.Time > uint64(self.clock.Time().Add(allowedFutureBlockTime).Unix()) {
+ if header.Time > uint64(eng.clock.Time().Add(allowedFutureBlockTime).Unix()) {
return consensus.ErrFutureBlock
}
// Verify the header's timestamp is not earlier than parent's
@@ -259,13 +266,13 @@ func (self *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header
return nil
}
-func (self *DummyEngine) Author(header *types.Header) (common.Address, error) {
+func (*DummyEngine) Author(header *types.Header) (common.Address, error) {
return header.Coinbase, nil
}
-func (self *DummyEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error {
+func (eng *DummyEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error {
// If we're running a full engine faking, accept any input as valid
- if self.consensusMode.ModeSkipHeader {
+ if eng.consensusMode.ModeSkipHeader {
return nil
}
// Short circuit if the header is known, or it's parent not
@@ -278,28 +285,28 @@ func (self *DummyEngine) VerifyHeader(chain consensus.ChainHeaderReader, header
return consensus.ErrUnknownAncestor
}
// Sanity checks passed, do a proper verification
- return self.verifyHeader(chain, header, parent, false)
+ return eng.verifyHeader(chain, header, parent, false)
}
-func (self *DummyEngine) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
+func (*DummyEngine) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
if len(block.Uncles()) > 0 {
return errUnclesUnsupported
}
return nil
}
-func (self *DummyEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
+func (*DummyEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
header.Difficulty = big.NewInt(1)
return nil
}
-func (self *DummyEngine) verifyBlockFee(
+func (eng *DummyEngine) verifyBlockFee(
baseFee *big.Int,
requiredBlockGasCost *big.Int,
txs []*types.Transaction,
receipts []*types.Receipt,
) error {
- if self.consensusMode.ModeSkipBlockFee {
+ if eng.consensusMode.ModeSkipBlockFee {
return nil
}
if baseFee == nil || baseFee.Sign() <= 0 {
@@ -352,7 +359,7 @@ func (self *DummyEngine) verifyBlockFee(
return nil
}
-func (self *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *types.Block, parent *types.Header, state *state.StateDB, receipts []*types.Receipt) error {
+func (eng *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *types.Block, parent *types.Header, state *state.StateDB, receipts []*types.Receipt) error {
if chain.Config().IsSubnetEVM(block.Time()) {
// we use the parent to determine the fee config
// since the current block has not been finalized yet.
@@ -376,7 +383,7 @@ func (self *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *type
return fmt.Errorf("invalid blockGasCost: have %d, want %d", blockBlockGasCost, blockGasCost)
}
// Verify the block fee was paid.
- if err := self.verifyBlockFee(
+ if err := eng.verifyBlockFee(
block.BaseFee(),
block.BlockGasCost(),
block.Transactions(),
@@ -389,7 +396,7 @@ func (self *DummyEngine) Finalize(chain consensus.ChainHeaderReader, block *type
return nil
}
-func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header, state *state.StateDB, txs []*types.Transaction,
+func (eng *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, parent *types.Header, state *state.StateDB, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt,
) (*types.Block, error) {
if chain.Config().IsSubnetEVM(header.Time) {
@@ -409,7 +416,7 @@ func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader,
parent.Time, header.Time,
)
// Verify that this block covers the block fee.
- if err := self.verifyBlockFee(
+ if err := eng.verifyBlockFee(
header.BaseFee,
header.BlockGasCost,
txs,
@@ -427,10 +434,10 @@ func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader,
), nil
}
-func (self *DummyEngine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
+func (*DummyEngine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
return big.NewInt(1)
}
-func (self *DummyEngine) Close() error {
+func (*DummyEngine) Close() error {
return nil
}
diff --git a/core/bench_test.go b/core/bench_test.go
index d7b353fb8c..0e8f7444bf 100644
--- a/core/bench_test.go
+++ b/core/bench_test.go
@@ -175,7 +175,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// generator function.
gspec := &Genesis{
Config: params.TestChainConfig,
- Alloc: GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}},
+ Alloc: types.GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}},
}
_, chain, _, _ := GenerateChainWithGenesis(gspec, dummy.NewCoinbaseFaker(), b.N, 10, gen)
@@ -229,7 +229,7 @@ func BenchmarkChainWrite_full_500k(b *testing.B) {
// makeChainForBench writes a given number of headers or empty blocks/receipts
// into a database.
-func makeChainForBench(db ethdb.Database, full bool, count uint64) {
+func makeChainForBench(db ethdb.Database, genesis *Genesis, full bool, count uint64) {
var hash common.Hash
for n := uint64(0); n < count; n++ {
header := &types.Header{
@@ -241,13 +241,16 @@ func makeChainForBench(db ethdb.Database, full bool, count uint64) {
TxHash: types.EmptyTxsHash,
ReceiptHash: types.EmptyReceiptsHash,
}
+ if n == 0 {
+ header = genesis.ToBlock().Header()
+ }
hash = header.Hash()
rawdb.WriteHeader(db, header)
rawdb.WriteCanonicalHash(db, hash, n)
if n == 0 {
- rawdb.WriteChainConfig(db, hash, params.TestChainConfig)
+ rawdb.WriteChainConfig(db, hash, genesis.Config)
}
rawdb.WriteHeadHeaderHash(db, hash)
@@ -260,13 +263,14 @@ func makeChainForBench(db ethdb.Database, full bool, count uint64) {
}
func benchWriteChain(b *testing.B, full bool, count uint64) {
+ genesis := &Genesis{Config: params.TestChainConfig}
for i := 0; i < b.N; i++ {
dir := b.TempDir()
db, err := rawdb.NewLevelDBDatabase(dir, 128, 1024, "", false)
if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err)
}
- makeChainForBench(db, full, count)
+ makeChainForBench(db, genesis, full, count)
db.Close()
}
}
@@ -278,7 +282,8 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err)
}
- makeChainForBench(db, full, count)
+ genesis := &Genesis{Config: params.TestChainConfig}
+ makeChainForBench(db, genesis, full, count)
db.Close()
b.ReportAllocs()
@@ -289,7 +294,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err)
}
- chain, err := NewBlockChain(db, DefaultCacheConfig, nil, dummy.NewFaker(), vm.Config{}, common.Hash{}, false)
+ chain, err := NewBlockChain(db, DefaultCacheConfig, genesis, dummy.NewFaker(), vm.Config{}, common.Hash{}, false)
if err != nil {
b.Fatalf("error creating chain: %v", err)
}
diff --git a/core/block_validator_test.go b/core/block_validator_test.go
index 1ea33274b5..b0bc93ff5f 100644
--- a/core/block_validator_test.go
+++ b/core/block_validator_test.go
@@ -30,6 +30,8 @@ import (
"testing"
)
+// TODO: Add TestHeaderVerification
+
func TestCalcGasLimit(t *testing.T) {
for i, tc := range []struct {
pGasLimit uint64
diff --git a/core/blockchain.go b/core/blockchain.go
index 25a471d1d9..a1272336e3 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -51,8 +51,9 @@ import (
"github.com/ava-labs/subnet-evm/metrics"
"github.com/ava-labs/subnet-evm/params"
"github.com/ava-labs/subnet-evm/trie"
- "github.com/ava-labs/subnet-evm/trie/triedb/hashdb"
- "github.com/ava-labs/subnet-evm/trie/triedb/pathdb"
+ "github.com/ava-labs/subnet-evm/triedb"
+ "github.com/ava-labs/subnet-evm/triedb/hashdb"
+ "github.com/ava-labs/subnet-evm/triedb/pathdb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/ethdb"
@@ -193,8 +194,8 @@ type CacheConfig struct {
}
// triedbConfig derives the configures for trie database.
-func (c *CacheConfig) triedbConfig() *trie.Config {
- config := &trie.Config{Preimages: c.Preimages}
+func (c *CacheConfig) triedbConfig() *triedb.Config {
+ config := &triedb.Config{Preimages: c.Preimages}
if c.StateScheme == rawdb.HashScheme {
config.HashDB = &hashdb.Config{
CleanCacheSize: c.TrieCleanLimit * 1024 * 1024,
@@ -234,6 +235,13 @@ func DefaultCacheConfigWithScheme(scheme string) *CacheConfig {
return &config
}
+// txLookup is wrapper over transaction lookup along with the corresponding
+// transaction object.
+type txLookup struct {
+ lookup *rawdb.LegacyTxLookupEntry
+ transaction *types.Transaction
+}
+
// BlockChain represents the canonical chain given a database with a genesis
// block. The Blockchain manages chain imports, reverts, chain reorganisations.
//
@@ -252,10 +260,11 @@ type BlockChain struct {
chainConfig *params.ChainConfig // Chain & network configuration
cacheConfig *CacheConfig // Cache configuration for pruning
- db ethdb.Database // Low level persistent database to store final content in
- snaps *snapshot.Tree // Snapshot tree for fast trie leaf access
- triedb *trie.Database // The database handler for maintaining trie nodes.
- stateCache state.Database // State database to reuse between imports (contains state cache)
+ db ethdb.Database // Low level persistent database to store final content in
+ snaps *snapshot.Tree // Snapshot tree for fast trie leaf access
+ triedb *triedb.Database // The database handler for maintaining trie nodes.
+ stateCache state.Database // State database to reuse between imports (contains state cache)
+ txIndexer *txIndexer // Transaction indexer, might be nil if not enabled
stateManager TrieWriter
hc *HeaderChain
@@ -277,13 +286,13 @@ type BlockChain struct {
currentBlock atomic.Pointer[types.Header] // Current head of the block chain
- bodyCache *lru.Cache[common.Hash, *types.Body] // Cache for the most recent block bodies
- receiptsCache *lru.Cache[common.Hash, []*types.Receipt] // Cache for the most recent receipts per block
- blockCache *lru.Cache[common.Hash, *types.Block] // Cache for the most recent entire blocks
- txLookupCache *lru.Cache[common.Hash, *rawdb.LegacyTxLookupEntry] // Cache for the most recent transaction lookup data.
- badBlocks *lru.Cache[common.Hash, *badBlock] // Cache for bad blocks
- feeConfigCache *lru.Cache[common.Hash, *cacheableFeeConfig] // Cache for the most recent feeConfig lookup data.
- coinbaseConfigCache *lru.Cache[common.Hash, *cacheableCoinbaseConfig] // Cache for the most recent coinbaseConfig lookup data.
+ bodyCache *lru.Cache[common.Hash, *types.Body] // Cache for the most recent block bodies
+ receiptsCache *lru.Cache[common.Hash, []*types.Receipt] // Cache for the most recent receipts per block
+ blockCache *lru.Cache[common.Hash, *types.Block] // Cache for the most recent entire blocks
+ txLookupCache *lru.Cache[common.Hash, txLookup] // Cache for the most recent transaction lookup data.
+ badBlocks *lru.Cache[common.Hash, *badBlock] // Cache for bad blocks
+ feeConfigCache *lru.Cache[common.Hash, *cacheableFeeConfig] // Cache for the most recent feeConfig lookup data.
+ coinbaseConfigCache *lru.Cache[common.Hash, *cacheableCoinbaseConfig] // Cache for the most recent coinbaseConfig lookup data.
stopping atomic.Bool // false if chain is running, true when stopped
@@ -351,7 +360,7 @@ func NewBlockChain(
return nil, errCacheConfigNotSpecified
}
// Open trie database with provided config
- triedb := trie.NewDatabase(db, cacheConfig.triedbConfig())
+ triedb := triedb.NewDatabase(db, cacheConfig.triedbConfig())
// Setup the genesis block, commit the provided genesis specification
// to database if the genesis block is not present yet, or load the
@@ -379,7 +388,7 @@ func NewBlockChain(
bodyCache: lru.NewCache[common.Hash, *types.Body](bodyCacheLimit),
receiptsCache: lru.NewCache[common.Hash, []*types.Receipt](receiptsCacheLimit),
blockCache: lru.NewCache[common.Hash, *types.Block](blockCacheLimit),
- txLookupCache: lru.NewCache[common.Hash, *rawdb.LegacyTxLookupEntry](txLookupCacheLimit),
+ txLookupCache: lru.NewCache[common.Hash, txLookup](txLookupCacheLimit),
badBlocks: lru.NewCache[common.Hash, *badBlock](badBlockLimit),
feeConfigCache: lru.NewCache[common.Hash, *cacheableFeeConfig](feeConfigCacheLimit),
coinbaseConfigCache: lru.NewCache[common.Hash, *cacheableCoinbaseConfig](coinbaseConfigCacheLimit),
@@ -450,113 +459,19 @@ func NewBlockChain(
// if txlookup limit is 0 (uindexing disabled), we don't need to repair the tx index tail.
if bc.cacheConfig.TransactionHistory != 0 {
latestStateSynced := rawdb.GetLatestSyncPerformed(bc.db)
- bc.setTxIndexTail(latestStateSynced)
+ bc.repairTxIndexTail(latestStateSynced)
}
// Start processing accepted blocks effects in the background
go bc.startAcceptor()
- // Start tx indexer/unindexer if required.
+ // Start tx indexer if it's enabled.
if bc.cacheConfig.TransactionHistory != 0 {
- bc.wg.Add(1)
- var (
- headCh = make(chan ChainEvent, 1) // Buffered to avoid locking up the event feed
- sub = bc.SubscribeChainAcceptedEvent(headCh)
- )
- go func() {
- defer bc.wg.Done()
- if sub == nil {
- log.Warn("could not create chain accepted subscription to unindex txs")
- return
- }
- defer sub.Unsubscribe()
-
- bc.maintainTxIndex(headCh)
- }()
+ bc.txIndexer = newTxIndexer(bc.cacheConfig.TransactionHistory, bc)
}
return bc, nil
}
-// unindexBlocks unindexes transactions depending on user configuration
-func (bc *BlockChain) unindexBlocks(tail uint64, head uint64, done chan struct{}) {
- start := time.Now()
- txLookupLimit := bc.cacheConfig.TransactionHistory
- bc.txIndexTailLock.Lock()
- defer func() {
- txUnindexTimer.Inc(time.Since(start).Milliseconds())
- bc.txIndexTailLock.Unlock()
- close(done)
- bc.wg.Done()
- }()
-
- // If head is 0, it means the chain is just initialized and no blocks are inserted,
- // so don't need to indexing anything.
- if head == 0 {
- return
- }
-
- if head-txLookupLimit+1 >= tail {
- // Unindex a part of stale indices and forward index tail to HEAD-limit
- rawdb.UnindexTransactions(bc.db, tail, head-txLookupLimit+1, bc.quit)
- }
-}
-
-// maintainTxIndex is responsible for the deletion of the
-// transaction index. This does not support reconstruction of removed indexes.
-// Invariant: If TxLookupLimit is 0, it means all tx indices will be preserved.
-// Meaning that this function should never be called.
-func (bc *BlockChain) maintainTxIndex(headCh <-chan ChainEvent) {
- txLookupLimit := bc.cacheConfig.TransactionHistory
-
- // If the user just upgraded to a new version which supports transaction
- // index pruning, write the new tail and remove anything older.
- if rawdb.ReadTxIndexTail(bc.db) == nil {
- rawdb.WriteTxIndexTail(bc.db, 0)
- }
-
- // Any reindexing done, start listening to chain events and moving the index window
- var (
- done chan struct{} // Non-nil if background unindexing or reindexing routine is active.
- )
- log.Info("Initialized transaction unindexer", "limit", txLookupLimit)
-
- // Launch the initial processing if chain is not empty. This step is
- // useful in these scenarios that chain has no progress and indexer
- // is never triggered.
- if head := bc.CurrentBlock(); head != nil && head.Number.Uint64() > txLookupLimit {
- done = make(chan struct{})
- tail := rawdb.ReadTxIndexTail(bc.db)
- bc.wg.Add(1)
- go bc.unindexBlocks(*tail, head.Number.Uint64(), done)
- }
-
- for {
- select {
- case head := <-headCh:
- headNum := head.Block.NumberU64()
- if headNum < txLookupLimit {
- break
- }
-
- if done == nil {
- done = make(chan struct{})
- // Note: tail will not be nil since it is initialized in this function.
- tail := rawdb.ReadTxIndexTail(bc.db)
- bc.wg.Add(1)
- go bc.unindexBlocks(*tail, headNum, done)
- }
- case <-done:
- done = nil
- case <-bc.quit:
- if done != nil {
- log.Info("Waiting background transaction unindexer to exit")
- <-done
- }
- return
- }
- }
-}
-
// writeBlockAcceptedIndices writes any indices that must be persisted for accepted block.
// This includes the following:
// - transaction lookup indices
@@ -957,7 +872,7 @@ func (bc *BlockChain) ValidateCanonicalChain() error {
// Ensure that all of the transactions have been stored correctly in the canonical
// chain
for txIndex, tx := range txs {
- txLookup := bc.GetTransactionLookup(tx.Hash())
+ txLookup, _, _ := bc.GetTransactionLookup(tx.Hash())
if txLookup == nil {
return fmt.Errorf("failed to find transaction %s", tx.Hash().String())
}
@@ -1014,6 +929,10 @@ func (bc *BlockChain) stopWithoutSaving() {
if !bc.stopping.CompareAndSwap(false, true) {
return
}
+ // Signal shutdown tx indexer.
+ if bc.txIndexer != nil {
+ bc.txIndexer.close()
+ }
log.Info("Closing quit channel")
close(bc.quit)
@@ -1434,7 +1353,7 @@ func (bc *BlockChain) insertBlock(block *types.Block, writes bool) error {
// The chain importer is starting and stopping trie prefetchers. If a bad
// block or other error is hit however, an early return may not properly
// terminate the background threads. This defer ensures that we clean up
- // and dangling prefetcher, without defering each and holding on live refs.
+ // and dangling prefetcher, without deferring each and holding on live refs.
if activeState != nil {
activeState.StopPrefetcher()
}
@@ -1646,6 +1565,12 @@ func (bc *BlockChain) reorg(oldHead *types.Header, newHead *types.Block) error {
} else {
log.Debug("Preference change (rewind to ancestor) occurred", "oldnum", oldHead.Number, "oldhash", oldHead.Hash(), "newnum", newHead.Number(), "newhash", newHead.Hash())
}
+ // Reset the tx lookup cache in case to clear stale txlookups.
+ // This is done before writing any new chain data to avoid the
+ // weird scenario that canonical chain is changed while the
+ // stale lookups are still cached.
+ bc.txLookupCache.Purge()
+
// Insert the new chain(except the head block(reverse order)),
// taking care of the proper incremental order.
for i := len(newChain) - 1; i >= 1; i-- {
@@ -2199,7 +2124,7 @@ func (bc *BlockChain) ResetToStateSyncedBlock(block *types.Block) error {
// if txlookup limit is 0 (uindexing disabled), we don't need to repair the tx index tail.
if bc.cacheConfig.TransactionHistory != 0 {
- bc.setTxIndexTail(block.NumberU64())
+ bc.repairTxIndexTail(block.NumberU64())
}
// Update all in-memory chain markers
@@ -2235,18 +2160,12 @@ func (bc *BlockChain) CacheConfig() *CacheConfig {
return bc.cacheConfig
}
-func (bc *BlockChain) setTxIndexTail(newTail uint64) error {
+func (bc *BlockChain) repairTxIndexTail(newTail uint64) error {
bc.txIndexTailLock.Lock()
defer bc.txIndexTailLock.Unlock()
- tailP := rawdb.ReadTxIndexTail(bc.db)
- var tailV uint64
- if tailP != nil {
- tailV = *tailP
- }
-
- if newTail > tailV {
- log.Info("Repairing tx index tail", "old", tailV, "new", newTail)
+ if curr := rawdb.ReadTxIndexTail(bc.db); curr == nil || *curr < newTail {
+ log.Info("Repairing tx index tail", "old", curr, "new", newTail)
rawdb.WriteTxIndexTail(bc.db, newTail)
}
return nil
diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go
index 554c20e730..eba02b712c 100644
--- a/core/blockchain_reader.go
+++ b/core/blockchain_reader.go
@@ -40,7 +40,7 @@ import (
"github.com/ava-labs/subnet-evm/params"
"github.com/ava-labs/subnet-evm/precompile/contracts/feemanager"
"github.com/ava-labs/subnet-evm/precompile/contracts/rewardmanager"
- "github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/event"
)
@@ -203,20 +203,37 @@ func (bc *BlockChain) GetCanonicalHash(number uint64) common.Hash {
return bc.hc.GetCanonicalHash(number)
}
-// GetTransactionLookup retrieves the lookup associate with the given transaction
-// hash from the cache or database.
-func (bc *BlockChain) GetTransactionLookup(hash common.Hash) *rawdb.LegacyTxLookupEntry {
+// 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) {
// Short circuit if the txlookup already in the cache, retrieve otherwise
- if lookup, exist := bc.txLookupCache.Get(hash); exist {
- return lookup
+ if item, exist := bc.txLookupCache.Get(hash); exist {
+ return item.lookup, item.transaction, nil
}
tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
if tx == nil {
- return nil
+ // The transaction is already indexed, the transaction is either
+ // not existent or not in the range of index, returning null.
+ return nil, nil, nil
}
- lookup := &rawdb.LegacyTxLookupEntry{BlockHash: blockHash, BlockIndex: blockNumber, Index: txIndex}
- bc.txLookupCache.Add(hash, lookup)
- return lookup
+ lookup := &rawdb.LegacyTxLookupEntry{
+ BlockHash: blockHash,
+ BlockIndex: blockNumber,
+ Index: txIndex,
+ }
+ bc.txLookupCache.Add(hash, txLookup{
+ lookup: lookup,
+ transaction: tx,
+ })
+ return lookup, tx, nil
}
// HasState checks if state trie is fully present in the database or not.
@@ -288,10 +305,15 @@ func (bc *BlockChain) GetVMConfig() *vm.Config {
}
// TrieDB retrieves the low level trie database used for data storage.
-func (bc *BlockChain) TrieDB() *trie.Database {
+func (bc *BlockChain) TrieDB() *triedb.Database {
return bc.triedb
}
+// HeaderChain returns the underlying header chain.
+func (bc *BlockChain) HeaderChain() *HeaderChain {
+ return bc.hc
+}
+
// SubscribeRemovedLogsEvent registers a subscription of RemovedLogsEvent.
func (bc *BlockChain) SubscribeRemovedLogsEvent(ch chan<- RemovedLogsEvent) event.Subscription {
return bc.scope.Track(bc.rmLogsFeed.Subscribe(ch))
diff --git a/core/blockchain_repair_test.go b/core/blockchain_repair_test.go
index f124b0efaa..3772aa8da9 100644
--- a/core/blockchain_repair_test.go
+++ b/core/blockchain_repair_test.go
@@ -39,7 +39,7 @@ import (
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/core/vm"
"github.com/ava-labs/subnet-evm/params"
- "github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
@@ -564,7 +564,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
var sideblocks types.Blocks
if tt.sidechainBlocks > 0 {
genDb := rawdb.NewMemoryDatabase()
- gspec.MustCommit(genDb, trie.NewDatabase(genDb, nil))
+ gspec.MustCommit(genDb, triedb.NewDatabase(genDb, nil))
sideblocks, _, err = GenerateChain(gspec.Config, gspec.ToBlock(), engine, genDb, tt.sidechainBlocks, 10, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{0x01})
tx, err := types.SignTx(types.NewTransaction(b.TxNonce(addr1), common.Address{0x01}, big.NewInt(10000), params.TxGas, common.Big1, nil), signer, key1)
@@ -577,7 +577,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
}
}
genDb := rawdb.NewMemoryDatabase()
- gspec.MustCommit(genDb, trie.NewDatabase(genDb, nil))
+ gspec.MustCommit(genDb, triedb.NewDatabase(genDb, nil))
canonblocks, _, err := GenerateChain(gspec.Config, gspec.ToBlock(), engine, genDb, tt.canonicalBlocks, 10, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{0x02})
b.SetDifficulty(big.NewInt(1000000))
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index 8fad072f9e..d1d124f668 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -21,7 +21,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
- "github.com/stretchr/testify/require"
+ "github.com/holiman/uint256"
)
var (
@@ -314,7 +314,7 @@ func testRepopulateMissingTriesParallel(t *testing.T, parallelism int) {
genesisBalance := big.NewInt(1000000)
gspec := &Genesis{
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int), FeeConfig: params.DefaultFeeConfig},
- Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}},
+ Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}},
}
blockchain, err := createBlockChain(chainDB, pruningConfig, gspec, common.Hash{})
@@ -427,7 +427,7 @@ func TestUngracefulAsyncShutdown(t *testing.T) {
genesisBalance := big.NewInt(1000000)
gspec := &Genesis{
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int), FeeConfig: params.DefaultFeeConfig},
- Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}},
+ Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}},
}
blockchain, err := create(chainDB, gspec, common.Hash{})
@@ -481,13 +481,13 @@ func TestUngracefulAsyncShutdown(t *testing.T) {
// After inserting all blocks, we should confirm that txs added after the
// async worker shutdown cannot be found.
for _, tx := range foundTxs {
- txLookup := blockchain.GetTransactionLookup(tx)
+ txLookup, _, _ := blockchain.GetTransactionLookup(tx)
if txLookup == nil {
t.Fatalf("missing transaction: %v", tx)
}
}
for _, tx := range missingTxs {
- txLookup := blockchain.GetTransactionLookup(tx)
+ txLookup, _, _ := blockchain.GetTransactionLookup(tx)
if txLookup != nil {
t.Fatalf("transaction should be missing: %v", tx)
}
@@ -499,9 +499,10 @@ func TestUngracefulAsyncShutdown(t *testing.T) {
if nonce != 10 {
return fmt.Errorf("expected nonce addr1: 10, found nonce: %d", nonce)
}
- transferredFunds := big.NewInt(100000)
+ transferredFunds := uint256.MustFromBig(big.NewInt(100000))
balance1 := sdb.GetBalance(addr1)
- expectedBalance1 := new(big.Int).Sub(genesisBalance, transferredFunds)
+ genesisBalance := uint256.MustFromBig(genesisBalance)
+ expectedBalance1 := new(uint256.Int).Sub(genesisBalance, transferredFunds)
if balance1.Cmp(expectedBalance1) != 0 {
return fmt.Errorf("expected addr1 balance: %d, found balance: %d", expectedBalance1, balance1)
}
@@ -530,7 +531,7 @@ func TestUngracefulAsyncShutdown(t *testing.T) {
// We should confirm all transactions can now be queried
for _, tx := range allTxs {
- txLookup := bc.GetTransactionLookup(tx)
+ txLookup, _, _ := bc.GetTransactionLookup(tx)
if txLookup == nil {
t.Fatalf("missing transaction: %v", tx)
}
@@ -538,207 +539,6 @@ func TestUngracefulAsyncShutdown(t *testing.T) {
}
}
-func TestTransactionIndices(t *testing.T) {
- // Configure and generate a sample block chain
- require := require.New(t)
- var (
- key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
- addr1 = crypto.PubkeyToAddress(key1.PublicKey)
- addr2 = crypto.PubkeyToAddress(key2.PublicKey)
- funds = big.NewInt(10000000000000)
- gspec = &Genesis{
- Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
- Alloc: GenesisAlloc{addr1: {Balance: funds}},
- }
- signer = types.LatestSigner(gspec.Config)
- )
- genDb, blocks, _, err := GenerateChainWithGenesis(gspec, dummy.NewFaker(), 128, 10, func(i int, block *BlockGen) {
- tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
- require.NoError(err)
- block.AddTx(tx)
- })
- require.NoError(err)
-
- blocks2, _, err := GenerateChain(gspec.Config, blocks[len(blocks)-1], dummy.NewFaker(), genDb, 10, 10, func(i int, block *BlockGen) {
- tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
- require.NoError(err)
- block.AddTx(tx)
- })
- require.NoError(err)
-
- conf := &CacheConfig{
- TrieCleanLimit: 256,
- TrieDirtyLimit: 256,
- TrieDirtyCommitTarget: 20,
- TriePrefetcherParallelism: 4,
- Pruning: true,
- CommitInterval: 4096,
- SnapshotLimit: 256,
- SnapshotNoBuild: true, // Ensure the test errors if snapshot initialization fails
- AcceptorQueueLimit: 64,
- }
-
- // Init block chain and check all needed indices has been indexed.
- chainDB := rawdb.NewMemoryDatabase()
- chain, err := createBlockChain(chainDB, conf, gspec, common.Hash{})
- require.NoError(err)
-
- _, err = chain.InsertChain(blocks)
- require.NoError(err)
-
- for _, block := range blocks {
- err := chain.Accept(block)
- require.NoError(err)
- }
- chain.DrainAcceptorQueue()
-
- lastAcceptedBlock := blocks[len(blocks)-1]
- require.Equal(lastAcceptedBlock.Hash(), chain.CurrentHeader().Hash())
-
- CheckTxIndices(t, nil, lastAcceptedBlock.NumberU64(), chain.db, false) // check all indices has been indexed
- chain.Stop()
-
- // Reconstruct a block chain which only reserves limited tx indices
- // 128 blocks were previously indexed. Now we add a new block at each test step.
- limits := []uint64{
- 0, /* tip: 129 reserve all (don't run) */
- 131, /* tip: 130 reserve all */
- 140, /* tip: 131 reserve all */
- 64, /* tip: 132, limit:64 */
- 32, /* tip: 133, limit:32 */
- }
- for i, l := range limits {
- t.Run(fmt.Sprintf("test-%d, limit: %d", i+1, l), func(t *testing.T) {
- conf.TransactionHistory = l
-
- chain, err := createBlockChain(chainDB, conf, gspec, lastAcceptedBlock.Hash())
- require.NoError(err)
-
- tail := getTail(l, lastAcceptedBlock.NumberU64())
- // check if startup indices are correct
- CheckTxIndices(t, tail, lastAcceptedBlock.NumberU64(), chain.db, false)
-
- newBlks := blocks2[i : i+1]
- _, err = chain.InsertChain(newBlks) // Feed chain a higher block to trigger indices updater.
- require.NoError(err)
-
- lastAcceptedBlock = newBlks[0]
- err = chain.Accept(lastAcceptedBlock) // Accept the block to trigger indices updater.
- require.NoError(err)
- chain.DrainAcceptorQueue()
-
- tail = getTail(l, lastAcceptedBlock.NumberU64())
- // check if indices are updated correctly
- CheckTxIndices(t, tail, lastAcceptedBlock.NumberU64(), chain.db, false)
- chain.Stop()
- })
- }
-}
-
-func getTail(limit uint64, lastAccepted uint64) *uint64 {
- if limit == 0 {
- return nil
- }
- var tail uint64
- if lastAccepted > limit {
- // tail should be the oldest block number which is indexed
- // i.e the first block number that's in the lookup range
- tail = lastAccepted - limit + 1
- }
- return &tail
-}
-
-func TestTransactionSkipIndexing(t *testing.T) {
- // Configure and generate a sample block chain
- require := require.New(t)
- var (
- key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
- key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
- addr1 = crypto.PubkeyToAddress(key1.PublicKey)
- addr2 = crypto.PubkeyToAddress(key2.PublicKey)
- funds = big.NewInt(10000000000000)
- gspec = &Genesis{
- Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
- Alloc: GenesisAlloc{addr1: {Balance: funds}},
- }
- signer = types.LatestSigner(gspec.Config)
- )
- genDb, blocks, _, err := GenerateChainWithGenesis(gspec, dummy.NewCoinbaseFaker(), 5, 10, func(i int, block *BlockGen) {
- tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
- require.NoError(err)
- block.AddTx(tx)
- })
- require.NoError(err)
-
- blocks2, _, err := GenerateChain(gspec.Config, blocks[len(blocks)-1], dummy.NewCoinbaseFaker(), genDb, 5, 10, func(i int, block *BlockGen) {
- tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
- require.NoError(err)
- block.AddTx(tx)
- })
- require.NoError(err)
-
- conf := &CacheConfig{
- TrieCleanLimit: 256,
- TrieDirtyLimit: 256,
- TrieDirtyCommitTarget: 20,
- TriePrefetcherParallelism: 4,
- Pruning: true,
- CommitInterval: 4096,
- SnapshotLimit: 256,
- SnapshotNoBuild: true, // Ensure the test errors if snapshot initialization fails
- AcceptorQueueLimit: 64,
- SkipTxIndexing: true,
- }
-
- // test1: Init block chain and check all indices has been skipped.
- chainDB := rawdb.NewMemoryDatabase()
- chain, err := createAndInsertChain(chainDB, conf, gspec, blocks, common.Hash{},
- func(b *types.Block) {
- bNumber := b.NumberU64()
- checkTxIndicesHelper(t, nil, bNumber+1, bNumber+1, bNumber, chainDB, false) // check all indices has been skipped
- })
- require.NoError(err)
- chain.Stop()
-
- // test2: specify lookuplimit with tx index skipping enabled. Blocks should not be indexed but tail should be updated.
- conf.TransactionHistory = 2
- chainDB = rawdb.NewMemoryDatabase()
- chain, err = createAndInsertChain(chainDB, conf, gspec, blocks, common.Hash{},
- func(b *types.Block) {
- bNumber := b.NumberU64()
- tail := bNumber - conf.TransactionHistory + 1
- checkTxIndicesHelper(t, &tail, bNumber+1, bNumber+1, bNumber, chainDB, false) // check all indices has been skipped
- })
- require.NoError(err)
- chain.Stop()
-
- // test3: tx index skipping and unindexer disabled. Blocks should be indexed and tail should be updated.
- conf.TransactionHistory = 0
- conf.SkipTxIndexing = false
- chainDB = rawdb.NewMemoryDatabase()
- chain, err = createAndInsertChain(chainDB, conf, gspec, blocks, common.Hash{},
- func(b *types.Block) {
- bNumber := b.NumberU64()
- checkTxIndicesHelper(t, nil, 0, bNumber, bNumber, chainDB, false) // check all indices has been indexed
- })
- require.NoError(err)
- chain.Stop()
-
- // now change tx index skipping to true and check that the indices are skipped for the last block
- // and old indices are removed up to the tail, but [tail, current) indices are still there.
- conf.TransactionHistory = 2
- conf.SkipTxIndexing = true
- chain, err = createAndInsertChain(chainDB, conf, gspec, blocks2[0:1], chain.CurrentHeader().Hash(),
- func(b *types.Block) {
- bNumber := b.NumberU64()
- tail := bNumber - conf.TransactionHistory + 1
- checkTxIndicesHelper(t, &tail, tail, bNumber-1, bNumber, chainDB, false)
- })
- require.NoError(err)
- chain.Stop()
-}
-
// TestCanonicalHashMarker tests all the canonical hash markers are updated/deleted
// correctly in case reorg is called.
func TestCanonicalHashMarker(t *testing.T) {
@@ -785,7 +585,7 @@ func testCanonicalHashMarker(t *testing.T, scheme string) {
var (
gspec = &Genesis{
Config: params.TestChainConfig,
- Alloc: GenesisAlloc{},
+ Alloc: types.GenesisAlloc{},
BaseFee: big.NewInt(params.TestInitialBaseFee),
}
engine = dummy.NewCoinbaseFaker()
@@ -948,7 +748,7 @@ func testCreateThenDelete(t *testing.T, config *params.ChainConfig) {
}...)
gspec := &Genesis{
Config: config,
- Alloc: GenesisAlloc{
+ Alloc: types.GenesisAlloc{
address: {Balance: funds},
},
}
@@ -1034,7 +834,7 @@ func TestDeleteThenCreate(t *testing.T) {
gspec := &Genesis{
Config: params.TestChainConfig,
- Alloc: GenesisAlloc{
+ Alloc: types.GenesisAlloc{
address: {Balance: funds},
},
}
@@ -1150,7 +950,7 @@ func TestTransientStorageReset(t *testing.T) {
}...)
gspec := &Genesis{
Config: params.TestChainConfig,
- Alloc: GenesisAlloc{
+ Alloc: types.GenesisAlloc{
address: {Balance: funds},
},
}
@@ -1218,7 +1018,7 @@ func TestEIP3651(t *testing.T) {
gspec = &Genesis{
Config: params.TestChainConfig,
Timestamp: uint64(upgrade.InitiallyActiveTime.Unix()),
- Alloc: GenesisAlloc{
+ Alloc: types.GenesisAlloc{
addr1: {Balance: funds},
addr2: {Balance: funds},
// The address 0xAAAA sloads 0x00 and 0x01
@@ -1296,7 +1096,7 @@ func TestEIP3651(t *testing.T) {
// Note this differs from go-ethereum where the miner receives the gasUsed * block baseFee,
// as our handling of the coinbase payment is different.
// Note we use block.GasUsed() here as there is only one tx.
- actual := state.GetBalance(block.Coinbase())
+ actual := state.GetBalance(block.Coinbase()).ToBig()
tx := block.Transactions()[0]
gasPrice := new(big.Int).Add(block.BaseFee(), tx.EffectiveGasTipValue(block.BaseFee()))
expected := new(big.Int).SetUint64(block.GasUsed() * gasPrice.Uint64())
@@ -1307,31 +1107,8 @@ func TestEIP3651(t *testing.T) {
// 4: Ensure the tx sender paid for the gasUsed * (block baseFee + effectiveGasTip).
// Note this differs from go-ethereum where the miner receives the gasUsed * block baseFee,
// as our handling of the coinbase payment is different.
- actual = new(big.Int).Sub(funds, state.GetBalance(addr1))
+ actual = new(big.Int).Sub(funds, state.GetBalance(addr1).ToBig())
if actual.Cmp(expected) != 0 {
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
}
}
-
-func createAndInsertChain(db ethdb.Database, cacheConfig *CacheConfig, gspec *Genesis, blocks types.Blocks, lastAcceptedHash common.Hash, accepted func(*types.Block)) (*BlockChain, error) {
- chain, err := createBlockChain(db, cacheConfig, gspec, lastAcceptedHash)
- if err != nil {
- return nil, err
- }
- _, err = chain.InsertChain(blocks)
- if err != nil {
- return nil, err
- }
- for _, block := range blocks {
- err := chain.Accept(block)
- if err != nil {
- return nil, err
- }
- chain.DrainAcceptorQueue()
- if accepted != nil {
- accepted(block)
- }
- }
-
- return chain, nil
-}
diff --git a/core/chain_makers.go b/core/chain_makers.go
index e34330d44f..ad5c106cf3 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -40,9 +40,10 @@ import (
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/core/vm"
"github.com/ava-labs/subnet-evm/params"
- "github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
+ "github.com/holiman/uint256"
)
// BlockGen creates blocks for testing.
@@ -169,7 +170,7 @@ func (b *BlockGen) AddTxWithVMConfig(tx *types.Transaction, config vm.Config) {
}
// GetBalance returns the balance of the given address at the generated block.
-func (b *BlockGen) GetBalance(addr common.Address) *big.Int {
+func (b *BlockGen) GetBalance(addr common.Address) *uint256.Int {
return b.statedb.GetBalance(addr)
}
@@ -279,7 +280,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
}
cm := newChainMaker(parent, config, engine)
- genblock := func(i int, parent *types.Block, triedb *trie.Database, statedb *state.StateDB) (*types.Block, types.Receipts, error) {
+ genblock := func(i int, parent *types.Block, triedb *triedb.Database, statedb *state.StateDB) (*types.Block, types.Receipts, error) {
b := &BlockGen{i: i, cm: cm, parent: parent, statedb: statedb, engine: engine}
b.header = cm.makeHeader(parent, gap, statedb, b.engine)
@@ -311,8 +312,9 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
}
return block, b.receipts, nil
}
+
// Forcibly use hash-based state scheme for retaining all nodes in disk.
- triedb := trie.NewDatabase(db, trie.HashDefaults)
+ triedb := triedb.NewDatabase(db, triedb.HashDefaults)
defer triedb.Close()
for i := 0; i < n; i++ {
@@ -360,7 +362,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
// then generate chain on top.
func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gap uint64, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts, error) {
db := rawdb.NewMemoryDatabase()
- triedb := trie.NewDatabase(db, trie.HashDefaults)
+ triedb := triedb.NewDatabase(db, triedb.HashDefaults)
defer triedb.Close()
_, err := genesis.Commit(db, triedb)
if err != nil {
diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go
index c3f6f576e4..19e27299bc 100644
--- a/core/chain_makers_test.go
+++ b/core/chain_makers_test.go
@@ -35,7 +35,7 @@ import (
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/core/vm"
"github.com/ava-labs/subnet-evm/params"
- "github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
@@ -55,9 +55,9 @@ func ExampleGenerateChain() {
// Ensure that key1 has some funds in the genesis block.
gspec := &Genesis{
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
- Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}},
+ Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}},
}
- genesis := gspec.MustCommit(genDb, trie.NewDatabase(genDb, trie.HashDefaults))
+ genesis := gspec.MustCommit(genDb, triedb.NewDatabase(genDb, triedb.HashDefaults))
// This call generates a chain of 3 blocks. The function runs for
// each block and adds different features to gen based on the
diff --git a/core/error.go b/core/error.go
index eac66628d6..2a23e691d0 100644
--- a/core/error.go
+++ b/core/error.go
@@ -109,4 +109,10 @@ var (
// ErrBlobFeeCapTooLow is returned if the transaction fee cap is less than the
// blob gas fee of the block.
ErrBlobFeeCapTooLow = errors.New("max fee per blob gas less than block blob gas fee")
+
+ // ErrMissingBlobHashes is returned if a blob transaction has no blob hashes.
+ ErrMissingBlobHashes = errors.New("blob transaction missing blob hashes")
+
+ // ErrBlobTxCreate is returned if a blob transaction has no explicit to field.
+ ErrBlobTxCreate = errors.New("blob transaction of type create")
)
diff --git a/core/evm.go b/core/evm.go
index 326a530af4..0b732bb8ae 100644
--- a/core/evm.go
+++ b/core/evm.go
@@ -36,7 +36,7 @@ import (
"github.com/ava-labs/subnet-evm/predicate"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
- //"github.com/ethereum/go-ethereum/log"
+ "github.com/holiman/uint256"
)
// ChainContext supports retrieving headers and consensus parameters from the
@@ -165,12 +165,12 @@ func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash
// CanTransfer checks whether there are enough funds in the address' account to make a transfer.
// This does not take the necessary gas in to account to make the transfer valid.
-func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool {
+func CanTransfer(db vm.StateDB, addr common.Address, amount *uint256.Int) bool {
return db.GetBalance(addr).Cmp(amount) >= 0
}
// Transfer subtracts amount from sender and adds amount to recipient using the given Db
-func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
+func Transfer(db vm.StateDB, sender, recipient common.Address, amount *uint256.Int) {
db.SubBalance(sender, amount)
db.AddBalance(recipient, amount)
}
diff --git a/core/gen_genesis.go b/core/gen_genesis.go
index d2938b70d0..13f61a27f6 100644
--- a/core/gen_genesis.go
+++ b/core/gen_genesis.go
@@ -7,6 +7,7 @@ import (
"errors"
"math/big"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/params"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
@@ -18,24 +19,24 @@ var _ = (*genesisSpecMarshaling)(nil)
// MarshalJSON marshals as JSON.
func (g Genesis) MarshalJSON() ([]byte, error) {
type Genesis struct {
- Config *params.ChainConfig `json:"config"`
- Nonce math.HexOrDecimal64 `json:"nonce"`
- Timestamp math.HexOrDecimal64 `json:"timestamp"`
- ExtraData hexutil.Bytes `json:"extraData"`
- GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
- Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
- Mixhash common.Hash `json:"mixHash"`
- Coinbase common.Address `json:"coinbase"`
- Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"`
- AirdropHash common.Hash `json:"airdropHash"`
- AirdropAmount *math.HexOrDecimal256 `json:"airdropAmount"`
- AirdropData []byte `json:"-"`
- Number math.HexOrDecimal64 `json:"number"`
- GasUsed math.HexOrDecimal64 `json:"gasUsed"`
- ParentHash common.Hash `json:"parentHash"`
- BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
- ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
- BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
+ Config *params.ChainConfig `json:"config"`
+ Nonce math.HexOrDecimal64 `json:"nonce"`
+ Timestamp math.HexOrDecimal64 `json:"timestamp"`
+ ExtraData hexutil.Bytes `json:"extraData"`
+ GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
+ Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
+ Mixhash common.Hash `json:"mixHash"`
+ Coinbase common.Address `json:"coinbase"`
+ Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"`
+ AirdropHash common.Hash `json:"airdropHash"`
+ AirdropAmount *math.HexOrDecimal256 `json:"airdropAmount"`
+ AirdropData []byte `json:"-"`
+ Number math.HexOrDecimal64 `json:"number"`
+ GasUsed math.HexOrDecimal64 `json:"gasUsed"`
+ ParentHash common.Hash `json:"parentHash"`
+ BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
+ ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
+ BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
}
var enc Genesis
enc.Config = g.Config
@@ -47,7 +48,7 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
enc.Mixhash = g.Mixhash
enc.Coinbase = g.Coinbase
if g.Alloc != nil {
- enc.Alloc = make(map[common.UnprefixedAddress]GenesisAccount, len(g.Alloc))
+ enc.Alloc = make(map[common.UnprefixedAddress]types.Account, len(g.Alloc))
for k, v := range g.Alloc {
enc.Alloc[common.UnprefixedAddress(k)] = v
}
@@ -67,24 +68,24 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
// UnmarshalJSON unmarshals from JSON.
func (g *Genesis) UnmarshalJSON(input []byte) error {
type Genesis struct {
- Config *params.ChainConfig `json:"config"`
- Nonce *math.HexOrDecimal64 `json:"nonce"`
- Timestamp *math.HexOrDecimal64 `json:"timestamp"`
- ExtraData *hexutil.Bytes `json:"extraData"`
- GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
- Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
- Mixhash *common.Hash `json:"mixHash"`
- Coinbase *common.Address `json:"coinbase"`
- Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"`
- AirdropHash *common.Hash `json:"airdropHash"`
- AirdropAmount *math.HexOrDecimal256 `json:"airdropAmount"`
- AirdropData []byte `json:"-"`
- Number *math.HexOrDecimal64 `json:"number"`
- GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
- ParentHash *common.Hash `json:"parentHash"`
- BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
- ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
- BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
+ Config *params.ChainConfig `json:"config"`
+ Nonce *math.HexOrDecimal64 `json:"nonce"`
+ Timestamp *math.HexOrDecimal64 `json:"timestamp"`
+ ExtraData *hexutil.Bytes `json:"extraData"`
+ GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
+ Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
+ Mixhash *common.Hash `json:"mixHash"`
+ Coinbase *common.Address `json:"coinbase"`
+ Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"`
+ AirdropHash *common.Hash `json:"airdropHash"`
+ AirdropAmount *math.HexOrDecimal256 `json:"airdropAmount"`
+ AirdropData []byte `json:"-"`
+ Number *math.HexOrDecimal64 `json:"number"`
+ GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
+ ParentHash *common.Hash `json:"parentHash"`
+ BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
+ ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
+ BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
}
var dec Genesis
if err := json.Unmarshal(input, &dec); err != nil {
@@ -119,7 +120,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
if dec.Alloc == nil {
return errors.New("missing required field 'alloc' for Genesis")
}
- g.Alloc = make(GenesisAlloc, len(dec.Alloc))
+ g.Alloc = make(types.GenesisAlloc, len(dec.Alloc))
for k, v := range dec.Alloc {
g.Alloc[common.Address(k)] = v
}
diff --git a/core/genesis.go b/core/genesis.go
index 1b786345a2..7d044289fe 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -27,8 +27,6 @@
package core
import (
- "bytes"
- "encoding/hex"
"encoding/json"
"errors"
"fmt"
@@ -40,20 +38,27 @@ import (
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/params"
"github.com/ava-labs/subnet-evm/trie"
- "github.com/ava-labs/subnet-evm/trie/triedb/pathdb"
+ "github.com/ava-labs/subnet-evm/triedb"
+ "github.com/ava-labs/subnet-evm/triedb/pathdb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
+ "github.com/holiman/uint256"
)
//go:generate go run github.com/fjl/gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go
-//go:generate go run github.com/fjl/gencodec -type GenesisAccount -field-override genesisAccountMarshaling -out gen_genesis_account.go
var errGenesisNoConfig = errors.New("genesis has no chain configuration")
+// Deprecated: use types.GenesisAccount instead.
+type GenesisAccount = types.Account
+
+// Deprecated: use types.GenesisAlloc instead.
+type GenesisAlloc = types.GenesisAlloc
+
type Airdrop struct {
// Address strings are hex-formatted common.Address
Address common.Address `json:"address"`
@@ -70,7 +75,7 @@ type Genesis struct {
Difficulty *big.Int `json:"difficulty" gencodec:"required"`
Mixhash common.Hash `json:"mixHash"`
Coinbase common.Address `json:"coinbase"`
- Alloc GenesisAlloc `json:"alloc" gencodec:"required"`
+ Alloc types.GenesisAlloc `json:"alloc" gencodec:"required"`
AirdropHash common.Hash `json:"airdropHash"`
AirdropAmount *big.Int `json:"airdropAmount"`
AirdropData []byte `json:"-"` // provided in a separate file, not serialized in this struct.
@@ -85,30 +90,6 @@ type Genesis struct {
BlobGasUsed *uint64 `json:"blobGasUsed"` // EIP-4844
}
-// GenesisAlloc specifies the initial state that is part of the genesis block.
-type GenesisAlloc map[common.Address]GenesisAccount
-
-func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error {
- m := make(map[common.UnprefixedAddress]GenesisAccount)
- if err := json.Unmarshal(data, &m); err != nil {
- return err
- }
- *ga = make(GenesisAlloc)
- for addr, a := range m {
- (*ga)[common.Address(addr)] = a
- }
- return nil
-}
-
-// GenesisAccount is an account in the state of the genesis block.
-type GenesisAccount struct {
- Code []byte `json:"code,omitempty"`
- Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
- Balance *big.Int `json:"balance" gencodec:"required"`
- Nonce uint64 `json:"nonce,omitempty"`
- PrivateKey []byte `json:"secretKey,omitempty"` // for tests
-}
-
// field type overrides for gencodec
type genesisSpecMarshaling struct {
Nonce math.HexOrDecimal64
@@ -118,41 +99,13 @@ type genesisSpecMarshaling struct {
GasUsed math.HexOrDecimal64
Number math.HexOrDecimal64
Difficulty *math.HexOrDecimal256
- Alloc map[common.UnprefixedAddress]GenesisAccount
+ Alloc map[common.UnprefixedAddress]types.Account
BaseFee *math.HexOrDecimal256
AirdropAmount *math.HexOrDecimal256
ExcessBlobGas *math.HexOrDecimal64
BlobGasUsed *math.HexOrDecimal64
}
-type genesisAccountMarshaling struct {
- Code hexutil.Bytes
- Balance *math.HexOrDecimal256
- Nonce math.HexOrDecimal64
- Storage map[storageJSON]storageJSON
- PrivateKey hexutil.Bytes
-}
-
-// storageJSON represents a 256 bit byte array, but allows less than 256 bits when
-// unmarshaling from hex.
-type storageJSON common.Hash
-
-func (h *storageJSON) UnmarshalText(text []byte) error {
- text = bytes.TrimPrefix(text, []byte("0x"))
- if len(text) > 64 {
- return fmt.Errorf("too many hex characters in storage key/value %q", text)
- }
- offset := len(h) - len(text)/2 // pad on the left
- if _, err := hex.Decode(h[offset:], text); err != nil {
- return fmt.Errorf("invalid hex storage key/value %q", text)
- }
- return nil
-}
-
-func (h storageJSON) MarshalText() ([]byte, error) {
- return hexutil.Bytes(h[:]).MarshalText()
-}
-
// GenesisMismatchError is raised when trying to overwrite an existing
// genesis block with an incompatible one.
type GenesisMismatchError struct {
@@ -179,7 +132,7 @@ func (e *GenesisMismatchError) Error() string {
// specify a fork block below the local head block). In case of a conflict, the
// error is a *params.ConfigCompatError and the new, unwritten config is returned.
func SetupGenesisBlock(
- db ethdb.Database, triedb *trie.Database, genesis *Genesis, lastAcceptedHash common.Hash, skipChainConfigCheckCompatible bool,
+ db ethdb.Database, triedb *triedb.Database, genesis *Genesis, lastAcceptedHash common.Hash, skipChainConfigCheckCompatible bool,
) (*params.ChainConfig, common.Hash, error) {
if genesis == nil {
return nil, common.Hash{}, ErrNoGenesis
@@ -269,21 +222,21 @@ func (g *Genesis) IsVerkle() bool {
// ToBlock returns the genesis block according to genesis specification.
func (g *Genesis) ToBlock() *types.Block {
db := rawdb.NewMemoryDatabase()
- return g.toBlock(db, trie.NewDatabase(db, g.trieConfig()))
+ return g.toBlock(db, triedb.NewDatabase(db, g.trieConfig()))
}
-func (g *Genesis) trieConfig() *trie.Config {
+func (g *Genesis) trieConfig() *triedb.Config {
if !g.IsVerkle() {
return nil
}
- return &trie.Config{
+ return &triedb.Config{
PathDB: pathdb.Defaults,
IsVerkle: true,
}
}
// TODO: migrate this function to "flush" for more similarity with upstream.
-func (g *Genesis) toBlock(db ethdb.Database, triedb *trie.Database) *types.Block {
+func (g *Genesis) toBlock(db ethdb.Database, triedb *triedb.Database) *types.Block {
statedb, err := state.New(types.EmptyRootHash, state.NewDatabaseWithNodeDB(db, triedb), nil)
if err != nil {
panic(err)
@@ -298,8 +251,9 @@ func (g *Genesis) toBlock(db ethdb.Database, triedb *trie.Database) *types.Block
if err := json.Unmarshal(g.AirdropData, &airdrop); err != nil {
panic(err)
}
+ airdropAmount := uint256.MustFromBig(g.AirdropAmount)
for _, alloc := range airdrop {
- statedb.SetBalance(alloc.Address, g.AirdropAmount)
+ statedb.SetBalance(alloc.Address, airdropAmount)
}
log.Debug(
"applied airdrop allocation",
@@ -331,7 +285,7 @@ func (g *Genesis) toBlock(db ethdb.Database, triedb *trie.Database) *types.Block
// Do custom allocation after airdrop in case an address shows up in standard
// allocation
for addr, account := range g.Alloc {
- statedb.SetBalance(addr, account.Balance)
+ statedb.SetBalance(addr, uint256.MustFromBig(account.Balance))
statedb.SetCode(addr, account.Code)
statedb.SetNonce(addr, account.Nonce)
for key, value := range account.Storage {
@@ -385,7 +339,7 @@ func (g *Genesis) toBlock(db ethdb.Database, triedb *trie.Database) *types.Block
// Commit writes the block and state of a genesis specification to the database.
// The block is committed as the canonical head block.
-func (g *Genesis) Commit(db ethdb.Database, triedb *trie.Database) (*types.Block, error) {
+func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Block, error) {
block := g.toBlock(db, triedb)
if block.Number().Sign() != 0 {
return nil, errors.New("can't commit genesis block with number > 0")
@@ -412,7 +366,7 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *trie.Database) (*types.Block
// MustCommit writes the genesis block and state to db, panicking on error.
// The block is committed as the canonical head block.
-func (g *Genesis) MustCommit(db ethdb.Database, triedb *trie.Database) *types.Block {
+func (g *Genesis) MustCommit(db ethdb.Database, triedb *triedb.Database) *types.Block {
block, err := g.Commit(db, triedb)
if err != nil {
panic(err)
@@ -444,7 +398,7 @@ func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big
Alloc: GenesisAlloc{addr: {Balance: balance}},
BaseFee: big.NewInt(params.TestMaxBaseFee),
}
- return g.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults))
+ return g.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults))
}
// ReadBlockByHash reads the block with the given hash from the database.
diff --git a/core/genesis_test.go b/core/genesis_test.go
index 4b4023e27b..d515f9dddf 100644
--- a/core/genesis_test.go
+++ b/core/genesis_test.go
@@ -42,7 +42,8 @@ import (
"github.com/ava-labs/subnet-evm/precompile/allowlist"
"github.com/ava-labs/subnet-evm/precompile/contracts/deployerallowlist"
"github.com/ava-labs/subnet-evm/trie"
- "github.com/ava-labs/subnet-evm/trie/triedb/pathdb"
+ "github.com/ava-labs/subnet-evm/triedb"
+ "github.com/ava-labs/subnet-evm/triedb/pathdb"
"github.com/ava-labs/subnet-evm/utils"
"github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/common"
@@ -51,7 +52,7 @@ import (
"github.com/stretchr/testify/require"
)
-func setupGenesisBlock(db ethdb.Database, triedb *trie.Database, genesis *Genesis, lastAcceptedHash common.Hash) (*params.ChainConfig, common.Hash, error) {
+func setupGenesisBlock(db ethdb.Database, triedb *triedb.Database, genesis *Genesis, lastAcceptedHash common.Hash) (*params.ChainConfig, common.Hash, error) {
return SetupGenesisBlock(db, triedb, genesis, lastAcceptedHash, false)
}
@@ -75,7 +76,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
customghash = common.HexToHash("0x4a12fe7bf8d40d152d7e9de22337b115186a4662aa3a97217b36146202bbfc66")
customg = Genesis{
Config: &preSubnetConfig,
- Alloc: GenesisAlloc{
+ Alloc: types.GenesisAlloc{
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
},
GasLimit: preSubnetConfig.FeeConfig.GasLimit.Uint64(),
@@ -97,7 +98,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
{
name: "genesis without ChainConfig",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
- return setupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), new(Genesis), common.Hash{})
+ return setupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), new(Genesis), common.Hash{})
},
wantErr: errGenesisNoConfig,
wantConfig: nil,
@@ -105,7 +106,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
{
name: "no block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
- return setupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil, common.Hash{})
+ return setupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), nil, common.Hash{})
},
wantErr: ErrNoGenesis,
wantConfig: nil,
@@ -113,7 +114,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
{
name: "custom block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
- tdb := trie.NewDatabase(db, newDbConfig(scheme))
+ tdb := triedb.NewDatabase(db, newDbConfig(scheme))
customg.Commit(db, tdb)
return setupGenesisBlock(db, tdb, nil, common.Hash{})
},
@@ -123,7 +124,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
{
name: "compatible config in DB",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
- tdb := trie.NewDatabase(db, newDbConfig(scheme))
+ tdb := triedb.NewDatabase(db, newDbConfig(scheme))
oldcustomg.Commit(db, tdb)
return setupGenesisBlock(db, tdb, &customg, customghash)
},
@@ -135,7 +136,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
// Commit the 'old' genesis block with SubnetEVM transition at 90.
// Advance to block #4, past the SubnetEVM transition block of customg.
- tdb := trie.NewDatabase(db, newDbConfig(scheme))
+ tdb := triedb.NewDatabase(db, newDbConfig(scheme))
genesis, err := oldcustomg.Commit(db, tdb)
if err != nil {
t.Fatal(err)
@@ -224,7 +225,7 @@ func TestStatefulPrecompilesConfigure(t *testing.T) {
genesis := &Genesis{
Config: config,
- Alloc: GenesisAlloc{
+ Alloc: types.GenesisAlloc{
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
},
GasLimit: config.FeeConfig.GasLimit.Uint64(),
@@ -235,7 +236,7 @@ func TestStatefulPrecompilesConfigure(t *testing.T) {
genesisBlock := genesis.ToBlock()
genesisRoot := genesisBlock.Root()
- _, _, err := setupGenesisBlock(db, trie.NewDatabase(db, trie.HashDefaults), genesis, genesisBlock.Hash())
+ _, _, err := setupGenesisBlock(db, triedb.NewDatabase(db, triedb.HashDefaults), genesis, genesisBlock.Hash())
if err != nil {
t.Fatal(err)
}
@@ -257,7 +258,7 @@ func TestPrecompileActivationAfterHeaderBlock(t *testing.T) {
db := rawdb.NewMemoryDatabase()
customg := Genesis{
Config: params.TestChainConfig,
- Alloc: GenesisAlloc{
+ Alloc: types.GenesisAlloc{
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
},
GasLimit: params.TestChainConfig.FeeConfig.GasLimit.Uint64(),
@@ -297,7 +298,7 @@ func TestPrecompileActivationAfterHeaderBlock(t *testing.T) {
require.Less(bc.lastAccepted.Time(), *contractDeployerConfig.Timestamp())
// This should not return any error since the last accepted block is before the activation block.
- config, _, err := setupGenesisBlock(db, trie.NewDatabase(db, nil), &customg, bc.lastAccepted.Hash())
+ config, _, err := setupGenesisBlock(db, triedb.NewDatabase(db, nil), &customg, bc.lastAccepted.Hash())
require.NoError(err)
if !reflect.DeepEqual(config, customg.Config) {
t.Errorf("returned %v\nwant %v", config, customg.Config)
@@ -309,14 +310,14 @@ func TestGenesisWriteUpgradesRegression(t *testing.T) {
config := *params.TestChainConfig
genesis := &Genesis{
Config: &config,
- Alloc: GenesisAlloc{
+ Alloc: types.GenesisAlloc{
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
},
GasLimit: config.FeeConfig.GasLimit.Uint64(),
}
db := rawdb.NewMemoryDatabase()
- trieDB := trie.NewDatabase(db, trie.HashDefaults)
+ trieDB := triedb.NewDatabase(db, triedb.HashDefaults)
genesisBlock := genesis.MustCommit(db, trieDB)
_, _, err := SetupGenesisBlock(db, trieDB, genesis, genesisBlock.Hash(), false)
@@ -346,11 +347,11 @@ func TestGenesisWriteUpgradesRegression(t *testing.T) {
require.NoError(err)
}
-func newDbConfig(scheme string) *trie.Config {
+func newDbConfig(scheme string) *triedb.Config {
if scheme == rawdb.HashScheme {
- return trie.HashDefaults
+ return triedb.HashDefaults
}
- return &trie.Config{PathDB: pathdb.Defaults}
+ return &triedb.Config{PathDB: pathdb.Defaults}
}
func TestVerkleGenesisCommit(t *testing.T) {
@@ -375,7 +376,7 @@ func TestVerkleGenesisCommit(t *testing.T) {
Config: verkleConfig,
Timestamp: verkleTime,
Difficulty: big.NewInt(0),
- Alloc: GenesisAlloc{
+ Alloc: types.GenesisAlloc{
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
},
}
@@ -387,7 +388,7 @@ func TestVerkleGenesisCommit(t *testing.T) {
}
db := rawdb.NewMemoryDatabase()
- triedb := trie.NewDatabase(db, &trie.Config{IsVerkle: true, PathDB: pathdb.Defaults})
+ triedb := triedb.NewDatabase(db, &triedb.Config{IsVerkle: true, PathDB: pathdb.Defaults})
block := genesis.MustCommit(db, triedb)
if !bytes.Equal(block.Root().Bytes(), expected) {
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
diff --git a/core/rawdb/accessors_trie.go b/core/rawdb/accessors_trie.go
index 5633c01b7f..e148a4280b 100644
--- a/core/rawdb/accessors_trie.go
+++ b/core/rawdb/accessors_trie.go
@@ -303,7 +303,7 @@ func ReadStateScheme(db ethdb.Reader) string {
return PathScheme
}
// The root node might be deleted during the initial snap sync, check
- // the persistentstent state id then.
+ // the persistent state id then.
if id := ReadPersistentStateID(db); id != 0 {
return PathScheme
}
diff --git a/core/rawdb/chain_iterator.go b/core/rawdb/chain_iterator.go
index ee3f6b3978..88133218b5 100644
--- a/core/rawdb/chain_iterator.go
+++ b/core/rawdb/chain_iterator.go
@@ -135,7 +135,7 @@ func iterateTransactions(db ethdb.Database, from uint64, to uint64, reverse bool
//
// There is a passed channel, the whole procedure will be interrupted if any
// signal received.
-func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) {
+func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool, report bool) {
// short circuit for invalid range
if from >= to {
return
@@ -145,13 +145,13 @@ func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan
batch = db.NewBatch()
start = time.Now()
logged = start.Add(-7 * time.Second)
+
// Since we iterate in reverse, we expect the first number to come
// in to be [to-1]. Therefore, setting lastNum to means that the
- // prqueue gap-evaluation will work correctly
- lastNum = to
- queue = prque.New[int64, *blockTxHashes](nil)
- // for stats reporting
- blocks, txs = 0, 0
+ // queue gap-evaluation will work correctly
+ lastNum = to
+ queue = prque.New[int64, *blockTxHashes](nil)
+ blocks, txs = 0, 0 // for stats reporting
)
for chanDelivery := range hashesCh {
// Push the delivery into the queue and process contiguous ranges.
@@ -197,37 +197,41 @@ func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan
log.Crit("Failed writing batch to db", "error", err)
return
}
+ logger := log.Debug
+ if report {
+ logger = log.Info
+ }
select {
case <-interrupt:
- log.Debug("Transaction indexing interrupted", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start)))
+ logger("Transaction indexing interrupted", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start)))
default:
- log.Debug("Indexed transactions", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start)))
+ logger("Indexed transactions", "blocks", blocks, "txs", txs, "tail", lastNum, "elapsed", common.PrettyDuration(time.Since(start)))
}
}
-// // IndexTransactions creates txlookup indices of the specified block range. The from
-// // is included while to is excluded.
-// //
-// // This function iterates canonical chain in reverse order, it has one main advantage:
-// // We can write tx index tail flag periodically even without the whole indexing
-// // procedure is finished. So that we can resume indexing procedure next time quickly.
-// //
-// // There is a passed channel, the whole procedure will be interrupted if any
-// // signal received.
-// func IndexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}) {
-// indexTransactions(db, from, to, interrupt, nil)
-// }
+// IndexTransactions creates txlookup indices of the specified block range. The from
+// is included while to is excluded.
+//
+// This function iterates canonical chain in reverse order, it has one main advantage:
+// We can write tx index tail flag periodically even without the whole indexing
+// procedure is finished. So that we can resume indexing procedure next time quickly.
+//
+// There is a passed channel, the whole procedure will be interrupted if any
+// signal received.
+func IndexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, report bool) {
+ indexTransactions(db, from, to, interrupt, nil, report)
+}
// indexTransactionsForTesting is the internal debug version with an additional hook.
func indexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) {
- indexTransactions(db, from, to, interrupt, hook)
+ indexTransactions(db, from, to, interrupt, hook, false)
}
// unindexTransactions removes txlookup indices of the specified block range.
//
// There is a passed channel, the whole procedure will be interrupted if any
// signal received.
-func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) {
+func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool, report bool) {
// short circuit for invalid range
if from >= to {
return
@@ -237,12 +241,12 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch
batch = db.NewBatch()
start = time.Now()
logged = start.Add(-7 * time.Second)
+
// we expect the first number to come in to be [from]. Therefore, setting
- // nextNum to from means that the prqueue gap-evaluation will work correctly
- nextNum = from
- queue = prque.New[int64, *blockTxHashes](nil)
- // for stats reporting
- blocks, txs = 0, 0
+ // nextNum to from means that the queue gap-evaluation will work correctly
+ nextNum = from
+ queue = prque.New[int64, *blockTxHashes](nil)
+ blocks, txs = 0, 0 // for stats reporting
)
// Otherwise spin up the concurrent iterator and unindexer
for delivery := range hashesCh {
@@ -289,11 +293,15 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch
log.Crit("Failed writing batch to db", "error", err)
return
}
+ logger := log.Debug
+ if report {
+ logger = log.Info
+ }
select {
case <-interrupt:
- log.Debug("Transaction unindexing interrupted", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
+ logger("Transaction unindexing interrupted", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
default:
- log.Debug("Unindexed transactions", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
+ logger("Unindexed transactions", "blocks", blocks, "txs", txs, "tail", to, "elapsed", common.PrettyDuration(time.Since(start)))
}
}
@@ -302,11 +310,11 @@ func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt ch
//
// There is a passed channel, the whole procedure will be interrupted if any
// signal received.
-func UnindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}) {
- unindexTransactions(db, from, to, interrupt, nil)
+func UnindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, report bool) {
+ unindexTransactions(db, from, to, interrupt, nil, report)
}
// unindexTransactionsForTesting is the internal debug version with an additional hook.
func unindexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool) {
- unindexTransactions(db, from, to, interrupt, hook)
+ unindexTransactions(db, from, to, interrupt, hook, false)
}
diff --git a/core/rawdb/chain_iterator_test.go b/core/rawdb/chain_iterator_test.go
index 6242741a76..caca8d94af 100644
--- a/core/rawdb/chain_iterator_test.go
+++ b/core/rawdb/chain_iterator_test.go
@@ -172,18 +172,18 @@ func TestIndexTransactions(t *testing.T) {
t.Fatalf("Transaction tail mismatch")
}
}
- indexTransactionsForTesting(chainDb, 5, 11, nil, nil)
+ IndexTransactions(chainDb, 5, 11, nil, false)
verify(5, 11, true, 5)
verify(0, 5, false, 5)
- indexTransactionsForTesting(chainDb, 0, 5, nil, nil)
+ IndexTransactions(chainDb, 0, 5, nil, false)
verify(0, 11, true, 0)
- UnindexTransactions(chainDb, 0, 5, nil)
+ UnindexTransactions(chainDb, 0, 5, nil, false)
verify(5, 11, true, 5)
verify(0, 5, false, 5)
- UnindexTransactions(chainDb, 5, 11, nil)
+ UnindexTransactions(chainDb, 5, 11, nil, false)
verify(0, 11, false, 11)
// Testing corner cases
@@ -200,7 +200,7 @@ func TestIndexTransactions(t *testing.T) {
})
verify(9, 11, true, 9)
verify(0, 9, false, 9)
- indexTransactionsForTesting(chainDb, 0, 9, nil, nil)
+ IndexTransactions(chainDb, 0, 9, nil, false)
signal = make(chan struct{})
var once2 sync.Once
diff --git a/core/rawdb/database.go b/core/rawdb/database.go
index 85fa6ac114..edf26c6427 100644
--- a/core/rawdb/database.go
+++ b/core/rawdb/database.go
@@ -460,3 +460,5 @@ func ClearPrefix(db ethdb.KeyValueStore, prefix []byte, keyLen int) error {
}
return batch.Write()
}
+
+/// TODO: Consider adding ReadChainMetadata
diff --git a/core/rlp_test.go b/core/rlp_test.go
index f2b30c5908..56fb3ce3d5 100644
--- a/core/rlp_test.go
+++ b/core/rlp_test.go
@@ -51,7 +51,7 @@ func getBlock(transactions int, uncles int, dataSize int) *types.Block {
funds = big.NewInt(50000 * 225000000000 * 200)
gspec = &Genesis{
Config: params.TestChainConfig,
- Alloc: GenesisAlloc{address: {Balance: funds}},
+ Alloc: types.GenesisAlloc{address: {Balance: funds}},
}
)
// We need to generate as many blocks +1 as uncles
diff --git a/core/state/database.go b/core/state/database.go
index 1ef3dd27f2..2e310707ac 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -35,6 +35,7 @@ import (
"github.com/ava-labs/subnet-evm/trie"
"github.com/ava-labs/subnet-evm/trie/trienode"
"github.com/ava-labs/subnet-evm/trie/utils"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/crate-crypto/go-ipa/banderwagon"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
@@ -77,7 +78,7 @@ type Database interface {
DiskDB() ethdb.KeyValueStore
// TrieDB returns the underlying trie database for managing trie nodes.
- TrieDB() *trie.Database
+ TrieDB() *triedb.Database
}
// Trie is a Ethereum Merkle Patricia trie.
@@ -160,17 +161,17 @@ func NewDatabase(db ethdb.Database) Database {
// NewDatabaseWithConfig creates a backing store for state. The returned database
// is safe for concurrent use and retains a lot of collapsed RLP trie nodes in a
// large memory cache.
-func NewDatabaseWithConfig(db ethdb.Database, config *trie.Config) Database {
+func NewDatabaseWithConfig(db ethdb.Database, config *triedb.Config) Database {
return &cachingDB{
disk: db,
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
codeCache: lru.NewSizeConstrainedCache[common.Hash, []byte](codeCacheSize),
- triedb: trie.NewDatabase(db, config),
+ triedb: triedb.NewDatabase(db, config),
}
}
// NewDatabaseWithNodeDB creates a state database with an already initialized node database.
-func NewDatabaseWithNodeDB(db ethdb.Database, triedb *trie.Database) Database {
+func NewDatabaseWithNodeDB(db ethdb.Database, triedb *triedb.Database) Database {
return &cachingDB{
disk: db,
codeSizeCache: lru.NewCache[common.Hash, int](codeSizeCacheSize),
@@ -183,7 +184,7 @@ type cachingDB struct {
disk ethdb.KeyValueStore
codeSizeCache *lru.Cache[common.Hash, int]
codeCache *lru.SizeConstrainedCache[common.Hash, []byte]
- triedb *trie.Database
+ triedb *triedb.Database
}
// OpenTrie opens the main account trie at a specific root hash.
@@ -253,6 +254,6 @@ func (db *cachingDB) DiskDB() ethdb.KeyValueStore {
}
// TrieDB retrieves any intermediate trie-node caching layer.
-func (db *cachingDB) TrieDB() *trie.Database {
+func (db *cachingDB) TrieDB() *triedb.Database {
return db.triedb
}
diff --git a/core/state/journal.go b/core/state/journal.go
index 4ba90fba5f..5e19f2df79 100644
--- a/core/state/journal.go
+++ b/core/state/journal.go
@@ -27,9 +27,8 @@
package state
import (
- "math/big"
-
"github.com/ethereum/go-ethereum/common"
+ "github.com/holiman/uint256"
)
// journalEntry is a modification entry in the state change journal that can be
@@ -113,13 +112,13 @@ type (
selfDestructChange struct {
account *common.Address
prev bool // whether account had already self-destructed
- prevbalance *big.Int
+ prevbalance *uint256.Int
}
// Changes to individual accounts.
balanceChange struct {
account *common.Address
- prev *big.Int
+ prev *uint256.Int
}
nonceChange struct {
account *common.Address
diff --git a/core/state/pruner/bloom.go b/core/state/pruner/bloom.go
index ca9d8f0a0b..060a952f37 100644
--- a/core/state/pruner/bloom.go
+++ b/core/state/pruner/bloom.go
@@ -37,17 +37,10 @@ import (
bloomfilter "github.com/holiman/bloomfilter/v2"
)
-// stateBloomHasher is a wrapper around a byte blob to satisfy the interface API
-// requirements of the bloom library used. It's used to convert a trie hash or
-// contract code hash into a 64 bit mini hash.
-type stateBloomHasher []byte
-
-func (f stateBloomHasher) Write(p []byte) (n int, err error) { panic("not implemented") }
-func (f stateBloomHasher) Sum(b []byte) []byte { panic("not implemented") }
-func (f stateBloomHasher) Reset() { panic("not implemented") }
-func (f stateBloomHasher) BlockSize() int { panic("not implemented") }
-func (f stateBloomHasher) Size() int { return 8 }
-func (f stateBloomHasher) Sum64() uint64 { return binary.BigEndian.Uint64(f) }
+// stateBloomHash is used to convert a trie hash or contract code hash into a 64 bit mini hash.
+func stateBloomHash(f []byte) uint64 {
+ return binary.BigEndian.Uint64(f)
+}
// stateBloom is a bloom filter used during the state conversion(snapshot->state).
// The keys of all generated entries will be recorded here so that in the pruning
@@ -123,10 +116,10 @@ func (bloom *stateBloom) Put(key []byte, value []byte) error {
if !isCode {
return errors.New("invalid entry")
}
- bloom.bloom.Add(stateBloomHasher(codeKey))
+ bloom.bloom.AddHash(stateBloomHash(codeKey))
return nil
}
- bloom.bloom.Add(stateBloomHasher(key))
+ bloom.bloom.AddHash(stateBloomHash(key))
return nil
}
@@ -138,5 +131,5 @@ func (bloom *stateBloom) Delete(key []byte) error { panic("not supported") }
// - If it says yes, the key may be contained
// - If it says no, the key is definitely not contained.
func (bloom *stateBloom) Contain(key []byte) bool {
- return bloom.bloom.Contains(stateBloomHasher(key))
+ return bloom.bloom.ContainsHash(stateBloomHash(key))
}
diff --git a/core/state/pruner/pruner.go b/core/state/pruner/pruner.go
index 1fdd4ce22b..96e27d28cd 100644
--- a/core/state/pruner/pruner.go
+++ b/core/state/pruner/pruner.go
@@ -41,6 +41,7 @@ import (
"github.com/ava-labs/subnet-evm/core/state/snapshot"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
@@ -96,7 +97,7 @@ func NewPruner(db ethdb.Database, config Config) (*Pruner, error) {
return nil, errors.New("failed to load head block")
}
// Offline pruning is only supported in legacy hash based scheme.
- triedb := trie.NewDatabase(db, trie.HashDefaults)
+ triedb := triedb.NewDatabase(db, triedb.HashDefaults)
// Note: we refuse to start a pruning session unless the snapshot disk layer exists, which should prevent
// us from ever needing to enter RecoverPruning in an invalid pruning session (a session where we do not have
@@ -135,7 +136,7 @@ func prune(maindb ethdb.Database, stateBloom *stateBloom, bloomPath string, star
// the trie nodes(and codes) belong to the active state will be filtered
// out. A very small part of stale tries will also be filtered because of
// the false-positive rate of bloom filter. But the assumption is held here
- // that the false-positive is low enough(~0.05%). The probablity of the
+ // that the false-positive is low enough(~0.05%). The probability of the
// dangling node is the state root is super low. So the dangling nodes in
// theory will never ever be visited again.
var (
@@ -347,7 +348,7 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error {
if genesis == nil {
return errors.New("missing genesis block")
}
- t, err := trie.NewStateTrie(trie.StateTrieID(genesis.Root()), trie.NewDatabase(db, trie.HashDefaults))
+ t, err := trie.NewStateTrie(trie.StateTrieID(genesis.Root()), triedb.NewDatabase(db, triedb.HashDefaults))
if err != nil {
return err
}
@@ -371,7 +372,7 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error {
}
if acc.Root != types.EmptyRootHash {
id := trie.StorageTrieID(genesis.Root(), common.BytesToHash(accIter.LeafKey()), acc.Root)
- storageTrie, err := trie.NewStateTrie(id, trie.NewDatabase(db, trie.HashDefaults))
+ storageTrie, err := trie.NewStateTrie(id, triedb.NewDatabase(db, triedb.HashDefaults))
if err != nil {
return err
}
diff --git a/core/state/snapshot/context.go b/core/state/snapshot/context.go
index d513db4855..35b22c6ea3 100644
--- a/core/state/snapshot/context.go
+++ b/core/state/snapshot/context.go
@@ -38,6 +38,8 @@ import (
"github.com/ethereum/go-ethereum/log"
)
+// generatorStats is a collection of statistics gathered by the snapshot generator
+// for logging purposes.
type generatorStats struct {
wiping chan struct{} // Notification channel if wiping is in progress
origin uint64 // Origin prefix where generation started
diff --git a/core/state/snapshot/difflayer.go b/core/state/snapshot/difflayer.go
index 664cb91721..62175cea25 100644
--- a/core/state/snapshot/difflayer.go
+++ b/core/state/snapshot/difflayer.go
@@ -53,7 +53,7 @@ var (
aggregatorMemoryLimit = uint64(4 * 1024 * 1024)
// aggregatorItemLimit is an approximate number of items that will end up
- // in the agregator layer before it's flushed out to disk. A plain account
+ // in the aggregator layer before it's flushed out to disk. A plain account
// weighs around 14B (+hash), a storage slot 32B (+hash), a deleted slot
// 0B (+hash). Slots are mostly set/unset in lockstep, so that average at
// 16B (+hash). All in all, the average entry seems to be 15+32=47B. Use a
@@ -135,47 +135,20 @@ type diffLayer struct {
lock sync.RWMutex
}
-// destructBloomHasher is a wrapper around a common.Hash to satisfy the interface
-// API requirements of the bloom library used. It's used to convert a destruct
-// event into a 64 bit mini hash.
-type destructBloomHasher common.Hash
-
-func (h destructBloomHasher) Write(p []byte) (n int, err error) { panic("not implemented") }
-func (h destructBloomHasher) Sum(b []byte) []byte { panic("not implemented") }
-func (h destructBloomHasher) Reset() { panic("not implemented") }
-func (h destructBloomHasher) BlockSize() int { panic("not implemented") }
-func (h destructBloomHasher) Size() int { return 8 }
-func (h destructBloomHasher) Sum64() uint64 {
+// destructBloomHash is used to convert a destruct event into a 64 bit mini hash.
+func destructBloomHash(h common.Hash) uint64 {
return binary.BigEndian.Uint64(h[bloomDestructHasherOffset : bloomDestructHasherOffset+8])
}
-// accountBloomHasher is a wrapper around a common.Hash to satisfy the interface
-// API requirements of the bloom library used. It's used to convert an account
-// hash into a 64 bit mini hash.
-type accountBloomHasher common.Hash
-
-func (h accountBloomHasher) Write(p []byte) (n int, err error) { panic("not implemented") }
-func (h accountBloomHasher) Sum(b []byte) []byte { panic("not implemented") }
-func (h accountBloomHasher) Reset() { panic("not implemented") }
-func (h accountBloomHasher) BlockSize() int { panic("not implemented") }
-func (h accountBloomHasher) Size() int { return 8 }
-func (h accountBloomHasher) Sum64() uint64 {
+// accountBloomHash is used to convert an account hash into a 64 bit mini hash.
+func accountBloomHash(h common.Hash) uint64 {
return binary.BigEndian.Uint64(h[bloomAccountHasherOffset : bloomAccountHasherOffset+8])
}
-// storageBloomHasher is a wrapper around a [2]common.Hash to satisfy the interface
-// API requirements of the bloom library used. It's used to convert an account
-// hash into a 64 bit mini hash.
-type storageBloomHasher [2]common.Hash
-
-func (h storageBloomHasher) Write(p []byte) (n int, err error) { panic("not implemented") }
-func (h storageBloomHasher) Sum(b []byte) []byte { panic("not implemented") }
-func (h storageBloomHasher) Reset() { panic("not implemented") }
-func (h storageBloomHasher) BlockSize() int { panic("not implemented") }
-func (h storageBloomHasher) Size() int { return 8 }
-func (h storageBloomHasher) Sum64() uint64 {
- return binary.BigEndian.Uint64(h[0][bloomStorageHasherOffset:bloomStorageHasherOffset+8]) ^
- binary.BigEndian.Uint64(h[1][bloomStorageHasherOffset:bloomStorageHasherOffset+8])
+// storageBloomHash is used to convert an account hash and a storage hash into a 64 bit mini hash.
+func storageBloomHash(h0, h1 common.Hash) uint64 {
+ return binary.BigEndian.Uint64(h0[bloomStorageHasherOffset:bloomStorageHasherOffset+8]) ^
+ binary.BigEndian.Uint64(h1[bloomStorageHasherOffset:bloomStorageHasherOffset+8])
}
// newDiffLayer creates a new diff on top of an existing snapshot, whether that's a low
@@ -245,14 +218,14 @@ func (dl *diffLayer) rebloom(origin *diskLayer) {
}
// Iterate over all the accounts and storage slots and index them
for hash := range dl.destructSet {
- dl.diffed.Add(destructBloomHasher(hash))
+ dl.diffed.AddHash(destructBloomHash(hash))
}
for hash := range dl.accountData {
- dl.diffed.Add(accountBloomHasher(hash))
+ dl.diffed.AddHash(accountBloomHash(hash))
}
for accountHash, slots := range dl.storageData {
for storageHash := range slots {
- dl.diffed.Add(storageBloomHasher{accountHash, storageHash})
+ dl.diffed.AddHash(storageBloomHash(accountHash, storageHash))
}
}
// Calculate the current false positive rate and update the error rate meter.
@@ -318,9 +291,9 @@ func (dl *diffLayer) AccountRLP(hash common.Hash) ([]byte, error) {
}
// Check the bloom filter first whether there's even a point in reaching into
// all the maps in all the layers below
- hit := dl.diffed.Contains(accountBloomHasher(hash))
+ hit := dl.diffed.ContainsHash(accountBloomHash(hash))
if !hit {
- hit = dl.diffed.Contains(destructBloomHasher(hash))
+ hit = dl.diffed.ContainsHash(destructBloomHash(hash))
}
var origin *diskLayer
if !hit {
@@ -389,9 +362,9 @@ func (dl *diffLayer) Storage(accountHash, storageHash common.Hash) ([]byte, erro
dl.lock.RUnlock()
return nil, ErrSnapshotStale
}
- hit := dl.diffed.Contains(storageBloomHasher{accountHash, storageHash})
+ hit := dl.diffed.ContainsHash(storageBloomHash(accountHash, storageHash))
if !hit {
- hit = dl.diffed.Contains(destructBloomHasher(accountHash))
+ hit = dl.diffed.ContainsHash(destructBloomHash(accountHash))
}
var origin *diskLayer
if !hit {
diff --git a/core/state/snapshot/disklayer.go b/core/state/snapshot/disklayer.go
index 5e317b1a32..478b6716b1 100644
--- a/core/state/snapshot/disklayer.go
+++ b/core/state/snapshot/disklayer.go
@@ -33,7 +33,7 @@ import (
"github.com/ava-labs/subnet-evm/core/rawdb"
"github.com/ava-labs/subnet-evm/core/types"
- "github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ava-labs/subnet-evm/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
@@ -43,7 +43,7 @@ import (
// diskLayer is a low level persistent snapshot built on top of a key-value store.
type diskLayer struct {
diskdb ethdb.KeyValueStore // Key-value store containing the base snapshot
- triedb *trie.Database // Trie node cache for reconstruction purposes
+ triedb *triedb.Database // Trie node cache for reconstruction purposes
cache *utils.MeteredCache // Cache to avoid hitting the disk for direct access
blockHash common.Hash // Block hash of the base snapshot
diff --git a/core/state/snapshot/disklayer_test.go b/core/state/snapshot/disklayer_test.go
index c1fb2f5a36..c5ebf65f86 100644
--- a/core/state/snapshot/disklayer_test.go
+++ b/core/state/snapshot/disklayer_test.go
@@ -144,7 +144,7 @@ func TestDiskMerge(t *testing.T) {
// Retrieve all the data through the disk layer and validate it
base = snaps.Snapshot(diffRoot)
if _, ok := base.(*diskLayer); !ok {
- t.Fatalf("update not flattend into the disk layer")
+ t.Fatalf("update not flattened into the disk layer")
}
// assertAccount ensures that an account matches the given blob.
@@ -363,7 +363,7 @@ func TestDiskPartialMerge(t *testing.T) {
// Retrieve all the data through the disk layer and validate it
base = snaps.Snapshot(diffRoot)
if _, ok := base.(*diskLayer); !ok {
- t.Fatalf("test %d: update not flattend into the disk layer", i)
+ t.Fatalf("test %d: update not flattened into the disk layer", i)
}
assertAccount(accNoModNoCache, accNoModNoCache[:])
assertAccount(accNoModCache, accNoModCache[:])
diff --git a/core/state/snapshot/generate.go b/core/state/snapshot/generate.go
index 67d470530b..35539ec3a7 100644
--- a/core/state/snapshot/generate.go
+++ b/core/state/snapshot/generate.go
@@ -34,6 +34,7 @@ import (
"github.com/ava-labs/subnet-evm/core/rawdb"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ava-labs/subnet-evm/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
@@ -49,7 +50,7 @@ const (
// generateSnapshot regenerates a brand new snapshot based on an existing state
// database and head block asynchronously. The snapshot is returned immediately
// and generation is continued in the background until done.
-func generateSnapshot(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int, blockHash, root common.Hash, wiper chan struct{}) *diskLayer {
+func generateSnapshot(diskdb ethdb.KeyValueStore, triedb *triedb.Database, cache int, blockHash, root common.Hash, wiper chan struct{}) *diskLayer {
// Wipe any previously existing snapshot from the database if no wiper is
// currently in progress.
if wiper == nil {
diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go
index 956cc589a3..d50b31211f 100644
--- a/core/state/snapshot/generate_test.go
+++ b/core/state/snapshot/generate_test.go
@@ -28,7 +28,6 @@ package snapshot
import (
"fmt"
- "math/big"
"os"
"testing"
"time"
@@ -36,13 +35,15 @@ import (
"github.com/ava-labs/subnet-evm/core/rawdb"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/trie"
- "github.com/ava-labs/subnet-evm/trie/triedb/hashdb"
- "github.com/ava-labs/subnet-evm/trie/triedb/pathdb"
"github.com/ava-labs/subnet-evm/trie/trienode"
+ "github.com/ava-labs/subnet-evm/triedb"
+ "github.com/ava-labs/subnet-evm/triedb/hashdb"
+ "github.com/ava-labs/subnet-evm/triedb/pathdb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/holiman/uint256"
"golang.org/x/crypto/sha3"
)
@@ -70,9 +71,9 @@ func testGeneration(t *testing.T, scheme string) {
var helper = newHelper(scheme)
stRoot := helper.makeStorageTrie(common.Hash{}, []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, false)
- helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
- helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
- helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
@@ -110,16 +111,16 @@ func testGenerateExistentState(t *testing.T, scheme string) {
var helper = newHelper(scheme)
stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
- helper.addSnapAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addSnapAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
- helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
- helper.addSnapAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addSnapAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
- helper.addSnapAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addSnapAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
root, snap := helper.CommitAndGenerate()
@@ -166,20 +167,20 @@ func checkSnapRoot(t *testing.T, snap *diskLayer, trieRoot common.Hash) {
type testHelper struct {
diskdb ethdb.Database
- triedb *trie.Database
+ triedb *triedb.Database
accTrie *trie.StateTrie
nodes *trienode.MergedNodeSet
}
func newHelper(scheme string) *testHelper {
diskdb := rawdb.NewMemoryDatabase()
- config := &trie.Config{}
+ config := &triedb.Config{}
if scheme == rawdb.PathScheme {
config.PathDB = &pathdb.Config{} // disable caching
} else {
config.HashDB = &hashdb.Config{} // disable caching
}
- triedb := trie.NewDatabase(diskdb, config)
+ triedb := triedb.NewDatabase(diskdb, config)
accTrie, _ := trie.NewStateTrie(trie.StateTrieID(types.EmptyRootHash), triedb)
return &testHelper{
diskdb: diskdb,
@@ -270,28 +271,28 @@ func testGenerateExistentStateWithWrongStorage(t *testing.T, scheme string) {
helper := newHelper(scheme)
// Account one, empty root but non-empty database
- helper.addAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
// Account two, non empty root but empty database
stRoot := helper.makeStorageTrie(hashData([]byte("acc-2")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-2", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
// Miss slots
{
// Account three, non empty root but misses slots in the beginning
helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-3", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-3", []string{"key-2", "key-3"}, []string{"val-2", "val-3"})
// Account four, non empty root but misses slots in the middle
helper.makeStorageTrie(hashData([]byte("acc-4")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-4", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addAccount("acc-4", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-4", []string{"key-1", "key-3"}, []string{"val-1", "val-3"})
// Account five, non empty root but misses slots in the end
helper.makeStorageTrie(hashData([]byte("acc-5")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-5", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addAccount("acc-5", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-5", []string{"key-1", "key-2"}, []string{"val-1", "val-2"})
}
@@ -299,22 +300,22 @@ func testGenerateExistentStateWithWrongStorage(t *testing.T, scheme string) {
{
// Account six, non empty root but wrong slots in the beginning
helper.makeStorageTrie(hashData([]byte("acc-6")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-6", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addAccount("acc-6", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-6", []string{"key-1", "key-2", "key-3"}, []string{"badval-1", "val-2", "val-3"})
// Account seven, non empty root but wrong slots in the middle
helper.makeStorageTrie(hashData([]byte("acc-7")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-7", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addAccount("acc-7", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-7", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "badval-2", "val-3"})
// Account eight, non empty root but wrong slots in the end
helper.makeStorageTrie(hashData([]byte("acc-8")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-8", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addAccount("acc-8", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-8", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "badval-3"})
// Account 9, non empty root but rotated slots
helper.makeStorageTrie(hashData([]byte("acc-9")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-9", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addAccount("acc-9", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-9", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-3", "val-2"})
}
@@ -322,17 +323,17 @@ func testGenerateExistentStateWithWrongStorage(t *testing.T, scheme string) {
{
// Account 10, non empty root but extra slots in the beginning
helper.makeStorageTrie(hashData([]byte("acc-10")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-10", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addAccount("acc-10", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-10", []string{"key-0", "key-1", "key-2", "key-3"}, []string{"val-0", "val-1", "val-2", "val-3"})
// Account 11, non empty root but extra slots in the middle
helper.makeStorageTrie(hashData([]byte("acc-11")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-11", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addAccount("acc-11", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-11", []string{"key-1", "key-2", "key-2-1", "key-3"}, []string{"val-1", "val-2", "val-2-1", "val-3"})
// Account 12, non empty root but extra slots in the end
helper.makeStorageTrie(hashData([]byte("acc-12")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-12", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addAccount("acc-12", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-12", []string{"key-1", "key-2", "key-3", "key-4"}, []string{"val-1", "val-2", "val-3", "val-4"})
}
@@ -377,25 +378,25 @@ func testGenerateExistentStateWithWrongAccounts(t *testing.T, scheme string) {
// Missing accounts, only in the trie
{
- helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Beginning
- helper.addTrieAccount("acc-4", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Middle
- helper.addTrieAccount("acc-6", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // End
+ helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Beginning
+ helper.addTrieAccount("acc-4", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // Middle
+ helper.addTrieAccount("acc-6", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // End
}
// Wrong accounts
{
- helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
- helper.addSnapAccount("acc-2", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: common.Hex2Bytes("0x1234")})
+ helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addSnapAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: common.Hex2Bytes("0x1234")})
- helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
- helper.addSnapAccount("acc-3", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addSnapAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
}
// Extra accounts, only in the snap
{
- helper.addSnapAccount("acc-0", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // before the beginning
- helper.addSnapAccount("acc-5", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: common.Hex2Bytes("0x1234")}) // Middle
- helper.addSnapAccount("acc-7", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // after the end
+ helper.addSnapAccount("acc-0", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // before the beginning
+ helper.addSnapAccount("acc-5", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: common.Hex2Bytes("0x1234")}) // Middle
+ helper.addSnapAccount("acc-7", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // after the end
}
root, snap := helper.CommitAndGenerate()
@@ -429,9 +430,9 @@ func testGenerateCorruptAccountTrie(t *testing.T, scheme string) {
// without any storage slots to keep the test smaller.
helper := newHelper(scheme)
- helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0xc7a30f39aff471c95d8a837497ad0e49b65be475cc0953540f80cfcdbdcd9074
- helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
- helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x19ead688e907b0fab07176120dceec244a72aff2f0aa51e8b827584e378772f4
+ helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0xc7a30f39aff471c95d8a837497ad0e49b65be475cc0953540f80cfcdbdcd9074
+ helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
+ helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x19ead688e907b0fab07176120dceec244a72aff2f0aa51e8b827584e378772f4
root := helper.Commit() // Root: 0xa04693ea110a31037fb5ee814308a6f1d76bdab0b11676bdf4541d2de55ba978
@@ -473,11 +474,11 @@ func testGenerateMissingStorageTrie(t *testing.T, scheme string) {
acc3 = hashData([]byte("acc-3"))
helper = newHelper(scheme)
)
- stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
- helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
- helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
+ stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
+ helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
+ helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
+ helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
root := helper.Commit()
@@ -513,11 +514,11 @@ func testGenerateCorruptStorageTrie(t *testing.T, scheme string) {
// two of which also has the same 3-slot storage trie attached.
helper := newHelper(scheme)
- stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
- helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
- helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
+ stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true) // 0xddefcd9376dd029653ef384bd2f0a126bb755fe84fdcc9e7cf421ba454f2bc67
+ helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
+ helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x65145f923027566669a1ae5ccac66f945b55ff6eaeb17d2ea8e048b7d381f2d7
stRoot = helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
+ helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
root := helper.Commit()
@@ -557,7 +558,7 @@ func testGenerateWithExtraAccounts(t *testing.T, scheme string) {
[]string{"val-1", "val-2", "val-3", "val-4", "val-5"},
true,
)
- acc := &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}
+ acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc)
helper.accTrie.MustUpdate([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
@@ -577,7 +578,7 @@ func testGenerateWithExtraAccounts(t *testing.T, scheme string) {
[]string{"val-1", "val-2", "val-3", "val-4", "val-5"},
true,
)
- acc := &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}
+ acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc)
key := hashData([]byte("acc-2"))
rawdb.WriteAccountSnapshot(helper.diskdb, key, val)
@@ -633,7 +634,7 @@ func testGenerateWithManyExtraAccounts(t *testing.T, scheme string) {
[]string{"val-1", "val-2", "val-3"},
true,
)
- acc := &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}
+ acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc)
helper.accTrie.MustUpdate([]byte("acc-1"), val) // 0x9250573b9c18c664139f3b6a7a8081b7d8f8916a8fcc5d94feec6c29f5fd4e9e
@@ -647,7 +648,7 @@ func testGenerateWithManyExtraAccounts(t *testing.T, scheme string) {
{
// 100 accounts exist only in snapshot
for i := 0; i < 1000; i++ {
- acc := &types.StateAccount{Balance: big.NewInt(int64(i)), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}
+ acc := &types.StateAccount{Balance: uint256.NewInt(uint64(i)), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc)
key := hashData([]byte(fmt.Sprintf("acc-%d", i)))
rawdb.WriteAccountSnapshot(helper.diskdb, key, val)
@@ -688,7 +689,7 @@ func testGenerateWithExtraBeforeAndAfter(t *testing.T, scheme string) {
}
helper := newHelper(scheme)
{
- acc := &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}
+ acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc)
helper.accTrie.MustUpdate(common.HexToHash("0x03").Bytes(), val)
helper.accTrie.MustUpdate(common.HexToHash("0x07").Bytes(), val)
@@ -729,7 +730,7 @@ func testGenerateWithMalformedSnapdata(t *testing.T, scheme string) {
}
helper := newHelper(scheme)
{
- acc := &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}
+ acc := &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}
val, _ := rlp.EncodeToBytes(acc)
helper.accTrie.MustUpdate(common.HexToHash("0x03").Bytes(), val)
@@ -771,7 +772,7 @@ func testGenerateFromEmptySnap(t *testing.T, scheme string) {
for i := 0; i < 400; i++ {
stRoot := helper.makeStorageTrie(hashData([]byte(fmt.Sprintf("acc-%d", i))), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
helper.addTrieAccount(fmt.Sprintf("acc-%d", i),
- &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
}
root, snap := helper.CommitAndGenerate()
t.Logf("Root: %#x\n", root) // Root: 0x6f7af6d2e1a1bf2b84a3beb3f8b64388465fbc1e274ca5d5d3fc787ca78f59e4
@@ -812,7 +813,7 @@ func testGenerateWithIncompleteStorage(t *testing.T, scheme string) {
for i := 0; i < 8; i++ {
accKey := fmt.Sprintf("acc-%d", i)
stRoot := helper.makeStorageTrie(hashData([]byte(accKey)), stKeys, stVals, true)
- helper.addAccount(accKey, &types.StateAccount{Balance: big.NewInt(int64(i)), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addAccount(accKey, &types.StateAccount{Balance: uint256.NewInt(uint64(i)), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
var moddedKeys []string
var moddedVals []string
for ii := 0; ii < 8; ii++ {
@@ -909,11 +910,11 @@ func testGenerateCompleteSnapshotWithDanglingStorage(t *testing.T, scheme string
var helper = newHelper(scheme)
stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
- helper.addAccount("acc-2", &types.StateAccount{Balance: big.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addAccount("acc-3", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
helper.addSnapStorage("acc-1", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
helper.addSnapStorage("acc-3", []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"})
@@ -949,11 +950,11 @@ func testGenerateBrokenSnapshotWithDanglingStorage(t *testing.T, scheme string)
var helper = newHelper(scheme)
stRoot := helper.makeStorageTrie(hashData([]byte("acc-1")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addTrieAccount("acc-1", &types.StateAccount{Balance: big.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
- helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()})
helper.makeStorageTrie(hashData([]byte("acc-3")), []string{"key-1", "key-2", "key-3"}, []string{"val-1", "val-2", "val-3"}, true)
- helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
+ helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()})
populateDangling(helper.diskdb)
diff --git a/core/state/snapshot/journal.go b/core/state/snapshot/journal.go
index f31570f791..6f8d82311f 100644
--- a/core/state/snapshot/journal.go
+++ b/core/state/snapshot/journal.go
@@ -33,7 +33,7 @@ import (
"time"
"github.com/ava-labs/subnet-evm/core/rawdb"
- "github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
@@ -56,7 +56,7 @@ type journalGenerator struct {
// loadSnapshot loads a pre-existing state snapshot backed by a key-value
// store. If loading the snapshot from disk is successful, this function also
// returns a boolean indicating whether or not the snapshot is fully generated.
-func loadSnapshot(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache int, blockHash, root common.Hash, noBuild bool) (snapshot, bool, error) {
+func loadSnapshot(diskdb ethdb.KeyValueStore, triedb *triedb.Database, cache int, blockHash, root common.Hash, noBuild bool) (snapshot, bool, error) {
// Retrieve the block number and hash of the snapshot, failing if no snapshot
// is present in the database (or crashed mid-update).
baseBlockHash := rawdb.ReadSnapshotBlockHash(diskdb)
diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go
index 01effb2040..3490d743bf 100644
--- a/core/state/snapshot/snapshot.go
+++ b/core/state/snapshot/snapshot.go
@@ -37,7 +37,7 @@ import (
"github.com/ava-labs/subnet-evm/core/rawdb"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/metrics"
- "github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
@@ -186,7 +186,7 @@ type Config struct {
type Tree struct {
config Config // Snapshots configurations
diskdb ethdb.KeyValueStore // Persistent database to store the snapshot
- triedb *trie.Database // In-memory cache to access the trie through
+ triedb *triedb.Database // In-memory cache to access the trie through
// Collection of all known layers
// blockHash -> snapshot
blockLayers map[common.Hash]snapshot
@@ -208,7 +208,7 @@ type Tree struct {
// If the snapshot is missing or the disk layer is broken, the snapshot will be
// reconstructed using both the existing data and the state trie.
// The repair happens on a background thread.
-func New(config Config, diskdb ethdb.KeyValueStore, triedb *trie.Database, blockHash, root common.Hash) (*Tree, error) {
+func New(config Config, diskdb ethdb.KeyValueStore, triedb *triedb.Database, blockHash, root common.Hash) (*Tree, error) {
// Create a new, empty snapshot tree
snap := &Tree{
config: config,
diff --git a/core/state/snapshot/snapshot_test.go b/core/state/snapshot/snapshot_test.go
index 36a4bb55eb..4d4b476d55 100644
--- a/core/state/snapshot/snapshot_test.go
+++ b/core/state/snapshot/snapshot_test.go
@@ -29,7 +29,6 @@ package snapshot
import (
crand "crypto/rand"
"fmt"
- "math/big"
"math/rand"
"testing"
"time"
@@ -38,6 +37,7 @@ import (
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/holiman/uint256"
)
// randomHash generates a random blob of data and returns it as a hash.
@@ -52,7 +52,7 @@ func randomHash() common.Hash {
// randomAccount generates a random account and returns it RLP encoded.
func randomAccount() []byte {
a := &types.StateAccount{
- Balance: big.NewInt(rand.Int63()),
+ Balance: uint256.NewInt(rand.Uint64()),
Nonce: rand.Uint64(),
Root: randomHash(),
CodeHash: types.EmptyCodeHash[:],
@@ -610,19 +610,19 @@ func TestRebloomOnFlatten(t *testing.T) {
t.Fatal("snapshot should be a diffLayer")
}
- if hitsA != dl.diffed.Contains(accountBloomHasher(addrA)) {
+ if hitsA != dl.diffed.ContainsHash(accountBloomHash(addrA)) {
t.Errorf("expected bloom filter to return %t but got %t", hitsA, !hitsA)
}
- if hitsB != dl.diffed.Contains(accountBloomHasher(addrB)) {
+ if hitsB != dl.diffed.ContainsHash(accountBloomHash(addrB)) {
t.Errorf("expected bloom filter to return %t but got %t", hitsB, !hitsB)
}
- if hitsC != dl.diffed.Contains(accountBloomHasher(addrC)) {
+ if hitsC != dl.diffed.ContainsHash(accountBloomHash(addrC)) {
t.Errorf("expected bloom filter to return %t but got %t", hitsC, !hitsC)
}
- if hitsD != dl.diffed.Contains(accountBloomHasher(addrD)) {
+ if hitsD != dl.diffed.ContainsHash(accountBloomHash(addrD)) {
t.Errorf("expected bloom filter to return %t but got %t", hitsD, !hitsD)
}
}
diff --git a/core/state/state_object.go b/core/state/state_object.go
index 9d06e32a27..fbff5a1c48 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -30,7 +30,6 @@ import (
"bytes"
"fmt"
"io"
- "math/big"
"time"
"github.com/ava-labs/subnet-evm/core/types"
@@ -39,6 +38,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/holiman/uint256"
)
type Code []byte
@@ -103,7 +103,7 @@ type stateObject struct {
// empty returns whether the account is considered empty.
func (s *stateObject) empty() bool {
- return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, types.EmptyCodeHash.Bytes())
+ return s.data.Nonce == 0 && s.data.Balance.IsZero() && bytes.Equal(s.data.CodeHash, types.EmptyCodeHash.Bytes())
}
// newObject creates a state object.
@@ -415,36 +415,36 @@ func (s *stateObject) commit() (*trienode.NodeSet, error) {
// AddBalance adds amount to s's balance.
// It is used to add funds to the destination account of a transfer.
-func (s *stateObject) AddBalance(amount *big.Int) {
+func (s *stateObject) AddBalance(amount *uint256.Int) {
// EIP161: We must check emptiness for the objects such that the account
// clearing (0,0,0 objects) can take effect.
- if amount.Sign() == 0 {
+ if amount.IsZero() {
if s.empty() {
s.touch()
}
return
}
- s.SetBalance(new(big.Int).Add(s.Balance(), amount))
+ s.SetBalance(new(uint256.Int).Add(s.Balance(), amount))
}
// SubBalance removes amount from s's balance.
// It is used to remove funds from the origin account of a transfer.
-func (s *stateObject) SubBalance(amount *big.Int) {
- if amount.Sign() == 0 {
+func (s *stateObject) SubBalance(amount *uint256.Int) {
+ if amount.IsZero() {
return
}
- s.SetBalance(new(big.Int).Sub(s.Balance(), amount))
+ s.SetBalance(new(uint256.Int).Sub(s.Balance(), amount))
}
-func (s *stateObject) SetBalance(amount *big.Int) {
+func (s *stateObject) SetBalance(amount *uint256.Int) {
s.db.journal.append(balanceChange{
account: &s.address,
- prev: new(big.Int).Set(s.data.Balance),
+ prev: new(uint256.Int).Set(s.data.Balance),
})
s.setBalance(amount)
}
-func (s *stateObject) setBalance(amount *big.Int) {
+func (s *stateObject) setBalance(amount *uint256.Int) {
s.data.Balance = amount
}
@@ -543,7 +543,7 @@ func (s *stateObject) CodeHash() []byte {
return s.data.CodeHash
}
-func (s *stateObject) Balance() *big.Int {
+func (s *stateObject) Balance() *uint256.Int {
return s.data.Balance
}
diff --git a/core/state/state_test.go b/core/state/state_test.go
index 547a599611..e0c3cb41ac 100644
--- a/core/state/state_test.go
+++ b/core/state/state_test.go
@@ -29,15 +29,15 @@ package state
import (
"bytes"
"encoding/json"
- "math/big"
"testing"
"github.com/ava-labs/subnet-evm/core/rawdb"
"github.com/ava-labs/subnet-evm/core/types"
- "github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
+ "github.com/holiman/uint256"
)
type stateEnv struct {
@@ -51,21 +51,78 @@ func newStateEnv() *stateEnv {
return &stateEnv{db: db, state: sdb}
}
+func TestDump(t *testing.T) {
+ db := rawdb.NewMemoryDatabase()
+ tdb := NewDatabaseWithConfig(db, &triedb.Config{Preimages: true})
+ sdb, _ := New(types.EmptyRootHash, tdb, nil)
+ s := &stateEnv{db: db, state: sdb}
+
+ // generate a few entries
+ obj1 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01}))
+ obj1.AddBalance(uint256.NewInt(22))
+ obj2 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02}))
+ obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
+ obj3 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x02}))
+ obj3.SetBalance(uint256.NewInt(44))
+
+ // write some of them to the trie
+ s.state.updateStateObject(obj1)
+ s.state.updateStateObject(obj2)
+ root, _ := s.state.Commit(0, false, false)
+
+ // check that DumpToCollector contains the state objects that are in trie
+ s.state, _ = New(root, tdb, nil)
+ got := string(s.state.Dump(nil))
+ want := `{
+ "root": "71edff0130dd2385947095001c73d9e28d862fc286fca2b922ca6f6f3cddfdd2",
+ "accounts": {
+ "0x0000000000000000000000000000000000000001": {
+ "balance": "22",
+ "nonce": 0,
+ "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
+ "address": "0x0000000000000000000000000000000000000001",
+ "key": "0x1468288056310c82aa4c01a7e12a10f8111a0560e72b700555479031b86c357d"
+ },
+ "0x0000000000000000000000000000000000000002": {
+ "balance": "44",
+ "nonce": 0,
+ "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
+ "address": "0x0000000000000000000000000000000000000002",
+ "key": "0xd52688a8f926c816ca1e079067caba944f158e764817b83fc43594370ca9cf62"
+ },
+ "0x0000000000000000000000000000000000000102": {
+ "balance": "0",
+ "nonce": 0,
+ "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "codeHash": "0x87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3",
+ "code": "0x03030303030303",
+ "address": "0x0000000000000000000000000000000000000102",
+ "key": "0xa17eacbc25cda025e81db9c5c62868822c73ce097cee2a63e33a2e41268358a1"
+ }
+ }
+}`
+ if got != want {
+ t.Errorf("DumpToCollector mismatch:\ngot: %s\nwant: %s\n", got, want)
+ }
+}
+
func TestIterativeDump(t *testing.T) {
db := rawdb.NewMemoryDatabase()
- tdb := NewDatabaseWithConfig(db, &trie.Config{Preimages: true})
+ tdb := NewDatabaseWithConfig(db, &triedb.Config{Preimages: true})
sdb, _ := New(types.EmptyRootHash, tdb, nil)
s := &stateEnv{db: db, state: sdb}
// generate a few entries
- obj1 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01}))
- obj1.AddBalance(big.NewInt(22))
- obj2 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02}))
+ obj1 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01}))
+ obj1.AddBalance(uint256.NewInt(22))
+ obj2 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x01, 0x02}))
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
- obj3 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x02}))
- obj3.SetBalance(big.NewInt(44))
- obj4 := s.state.GetOrNewStateObject(common.BytesToAddress([]byte{0x00}))
- obj4.AddBalance(big.NewInt(1337))
+ obj3 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x02}))
+ obj3.SetBalance(uint256.NewInt(44))
+ obj4 := s.state.getOrNewStateObject(common.BytesToAddress([]byte{0x00}))
+ obj4.AddBalance(uint256.NewInt(1337))
// write some of them to the trie
s.state.updateStateObject(obj1)
@@ -87,3 +144,165 @@ func TestIterativeDump(t *testing.T) {
t.Errorf("DumpToCollector mismatch:\ngot: %s\nwant: %s\n", got, want)
}
}
+
+func TestNull(t *testing.T) {
+ s := newStateEnv()
+ address := common.HexToAddress("0x823140710bf13990e4500136726d8b55")
+ s.state.CreateAccount(address)
+ //value := common.FromHex("0x823140710bf13990e4500136726d8b55")
+ var value common.Hash
+
+ s.state.SetState(address, common.Hash{}, value)
+ s.state.Commit(0, false, false)
+
+ if value := s.state.GetState(address, common.Hash{}); value != (common.Hash{}) {
+ t.Errorf("expected empty current value, got %x", value)
+ }
+ if value := s.state.GetCommittedState(address, common.Hash{}); value != (common.Hash{}) {
+ t.Errorf("expected empty committed value, got %x", value)
+ }
+}
+
+func TestSnapshot(t *testing.T) {
+ stateobjaddr := common.BytesToAddress([]byte("aa"))
+ var storageaddr common.Hash
+ data1 := common.BytesToHash([]byte{42})
+ data2 := common.BytesToHash([]byte{43})
+ s := newStateEnv()
+
+ // snapshot the genesis state
+ genesis := s.state.Snapshot()
+
+ // set initial state object value
+ s.state.SetState(stateobjaddr, storageaddr, data1)
+ snapshot := s.state.Snapshot()
+
+ // set a new state object value, revert it and ensure correct content
+ s.state.SetState(stateobjaddr, storageaddr, data2)
+ s.state.RevertToSnapshot(snapshot)
+
+ if v := s.state.GetState(stateobjaddr, storageaddr); v != data1 {
+ t.Errorf("wrong storage value %v, want %v", v, data1)
+ }
+ if v := s.state.GetCommittedState(stateobjaddr, storageaddr); v != (common.Hash{}) {
+ t.Errorf("wrong committed storage value %v, want %v", v, common.Hash{})
+ }
+
+ // revert up to the genesis state and ensure correct content
+ s.state.RevertToSnapshot(genesis)
+ if v := s.state.GetState(stateobjaddr, storageaddr); v != (common.Hash{}) {
+ t.Errorf("wrong storage value %v, want %v", v, common.Hash{})
+ }
+ if v := s.state.GetCommittedState(stateobjaddr, storageaddr); v != (common.Hash{}) {
+ t.Errorf("wrong committed storage value %v, want %v", v, common.Hash{})
+ }
+}
+
+func TestSnapshotEmpty(t *testing.T) {
+ s := newStateEnv()
+ s.state.RevertToSnapshot(s.state.Snapshot())
+}
+
+func TestSnapshot2(t *testing.T) {
+ state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil)
+
+ stateobjaddr0 := common.BytesToAddress([]byte("so0"))
+ stateobjaddr1 := common.BytesToAddress([]byte("so1"))
+ var storageaddr common.Hash
+
+ data0 := common.BytesToHash([]byte{17})
+ data1 := common.BytesToHash([]byte{18})
+
+ state.SetState(stateobjaddr0, storageaddr, data0)
+ state.SetState(stateobjaddr1, storageaddr, data1)
+
+ // db, trie are already non-empty values
+ so0 := state.getStateObject(stateobjaddr0)
+ so0.SetBalance(uint256.NewInt(42))
+ so0.SetNonce(43)
+ so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'})
+ so0.selfDestructed = false
+ so0.deleted = false
+ state.setStateObject(so0)
+
+ root, _ := state.Commit(0, false, false)
+ state, _ = New(root, state.db, nil)
+
+ // and one with deleted == true
+ so1 := state.getStateObject(stateobjaddr1)
+ so1.SetBalance(uint256.NewInt(52))
+ so1.SetNonce(53)
+ so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'})
+ so1.selfDestructed = true
+ so1.deleted = true
+ state.setStateObject(so1)
+
+ so1 = state.getStateObject(stateobjaddr1)
+ if so1 != nil {
+ t.Fatalf("deleted object not nil when getting")
+ }
+
+ snapshot := state.Snapshot()
+ state.RevertToSnapshot(snapshot)
+
+ so0Restored := state.getStateObject(stateobjaddr0)
+ // Update lazily-loaded values before comparing.
+ so0Restored.GetState(storageaddr)
+ so0Restored.Code()
+ // non-deleted is equal (restored)
+ compareStateObjects(so0Restored, so0, t)
+
+ // deleted should be nil, both before and after restore of state copy
+ so1Restored := state.getStateObject(stateobjaddr1)
+ if so1Restored != nil {
+ t.Fatalf("deleted object not nil after restoring snapshot: %+v", so1Restored)
+ }
+}
+
+func compareStateObjects(so0, so1 *stateObject, t *testing.T) {
+ if so0.Address() != so1.Address() {
+ t.Fatalf("Address mismatch: have %v, want %v", so0.address, so1.address)
+ }
+ if so0.Balance().Cmp(so1.Balance()) != 0 {
+ t.Fatalf("Balance mismatch: have %v, want %v", so0.Balance(), so1.Balance())
+ }
+ if so0.Nonce() != so1.Nonce() {
+ t.Fatalf("Nonce mismatch: have %v, want %v", so0.Nonce(), so1.Nonce())
+ }
+ if so0.data.Root != so1.data.Root {
+ t.Errorf("Root mismatch: have %x, want %x", so0.data.Root[:], so1.data.Root[:])
+ }
+ if !bytes.Equal(so0.CodeHash(), so1.CodeHash()) {
+ t.Fatalf("CodeHash mismatch: have %v, want %v", so0.CodeHash(), so1.CodeHash())
+ }
+ if !bytes.Equal(so0.code, so1.code) {
+ t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code)
+ }
+
+ if len(so1.dirtyStorage) != len(so0.dirtyStorage) {
+ t.Errorf("Dirty storage size mismatch: have %d, want %d", len(so1.dirtyStorage), len(so0.dirtyStorage))
+ }
+ for k, v := range so1.dirtyStorage {
+ if so0.dirtyStorage[k] != v {
+ t.Errorf("Dirty storage key %x mismatch: have %v, want %v", k, so0.dirtyStorage[k], v)
+ }
+ }
+ for k, v := range so0.dirtyStorage {
+ if so1.dirtyStorage[k] != v {
+ t.Errorf("Dirty storage key %x mismatch: have %v, want none.", k, v)
+ }
+ }
+ if len(so1.originStorage) != len(so0.originStorage) {
+ t.Errorf("Origin storage size mismatch: have %d, want %d", len(so1.originStorage), len(so0.originStorage))
+ }
+ for k, v := range so1.originStorage {
+ if so0.originStorage[k] != v {
+ t.Errorf("Origin storage key %x mismatch: have %v, want %v", k, so0.originStorage[k], v)
+ }
+ }
+ for k, v := range so0.originStorage {
+ if so1.originStorage[k] != v {
+ t.Errorf("Origin storage key %x mismatch: have %v, want none.", k, v)
+ }
+ }
+}
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 67ea4dd458..9779bec6fe 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -29,7 +29,6 @@ package state
import (
"fmt"
- "math/big"
"sort"
"time"
@@ -45,6 +44,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
+ "github.com/holiman/uint256"
)
const (
@@ -333,12 +333,12 @@ func (s *StateDB) Empty(addr common.Address) bool {
}
// GetBalance retrieves the balance from the given address or 0 if object not found
-func (s *StateDB) GetBalance(addr common.Address) *big.Int {
+func (s *StateDB) GetBalance(addr common.Address) *uint256.Int {
stateObject := s.getStateObject(addr)
if stateObject != nil {
return stateObject.Balance()
}
- return new(big.Int).Set(common.Big0)
+ return common.U2560
}
// GetNonce retrieves the nonce from the given address or 0 if object not found
@@ -384,10 +384,10 @@ func (s *StateDB) GetCodeSize(addr common.Address) int {
func (s *StateDB) GetCodeHash(addr common.Address) common.Hash {
stateObject := s.getStateObject(addr)
- if stateObject == nil {
- return common.Hash{}
+ if stateObject != nil {
+ return common.BytesToHash(stateObject.CodeHash())
}
- return common.BytesToHash(stateObject.CodeHash())
+ return common.Hash{}
}
// GetState retrieves a value from the given account's storage trie.
@@ -426,44 +426,44 @@ func (s *StateDB) HasSelfDestructed(addr common.Address) bool {
*/
// AddBalance adds amount to the account associated with addr.
-func (s *StateDB) AddBalance(addr common.Address, amount *big.Int) {
- stateObject := s.GetOrNewStateObject(addr)
+func (s *StateDB) AddBalance(addr common.Address, amount *uint256.Int) {
+ stateObject := s.getOrNewStateObject(addr)
if stateObject != nil {
stateObject.AddBalance(amount)
}
}
// SubBalance subtracts amount from the account associated with addr.
-func (s *StateDB) SubBalance(addr common.Address, amount *big.Int) {
- stateObject := s.GetOrNewStateObject(addr)
+func (s *StateDB) SubBalance(addr common.Address, amount *uint256.Int) {
+ stateObject := s.getOrNewStateObject(addr)
if stateObject != nil {
stateObject.SubBalance(amount)
}
}
-func (s *StateDB) SetBalance(addr common.Address, amount *big.Int) {
- stateObject := s.GetOrNewStateObject(addr)
+func (s *StateDB) SetBalance(addr common.Address, amount *uint256.Int) {
+ stateObject := s.getOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetBalance(amount)
}
}
func (s *StateDB) SetNonce(addr common.Address, nonce uint64) {
- stateObject := s.GetOrNewStateObject(addr)
+ stateObject := s.getOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetNonce(nonce)
}
}
func (s *StateDB) SetCode(addr common.Address, code []byte) {
- stateObject := s.GetOrNewStateObject(addr)
+ stateObject := s.getOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetCode(crypto.Keccak256Hash(code), code)
}
}
func (s *StateDB) SetState(addr common.Address, key, value common.Hash) {
- stateObject := s.GetOrNewStateObject(addr)
+ stateObject := s.getOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetState(key, value)
}
@@ -484,7 +484,7 @@ func (s *StateDB) SetStorage(addr common.Address, storage map[common.Hash]common
if _, ok := s.stateObjectsDestruct[addr]; !ok {
s.stateObjectsDestruct[addr] = nil
}
- stateObject := s.GetOrNewStateObject(addr)
+ stateObject := s.getOrNewStateObject(addr)
for k, v := range storage {
stateObject.SetState(k, v)
}
@@ -503,10 +503,10 @@ func (s *StateDB) SelfDestruct(addr common.Address) {
s.journal.append(selfDestructChange{
account: &addr,
prev: stateObject.selfDestructed,
- prevbalance: new(big.Int).Set(stateObject.Balance()),
+ prevbalance: new(uint256.Int).Set(stateObject.Balance()),
})
stateObject.markSelfdestructed()
- stateObject.data.Balance = new(big.Int)
+ stateObject.data.Balance = new(uint256.Int)
}
func (s *StateDB) Selfdestruct6780(addr common.Address) {
@@ -667,8 +667,8 @@ func (s *StateDB) setStateObject(object *stateObject) {
s.stateObjects[object.Address()] = object
}
-// GetOrNewStateObject retrieves a state object or create a new state object if nil.
-func (s *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
+// getOrNewStateObject retrieves a state object or create a new state object if nil.
+func (s *StateDB) getOrNewStateObject(addr common.Address) *stateObject {
stateObject := s.getStateObject(addr)
if stateObject == nil {
stateObject, _ = s.createObject(addr)
diff --git a/core/state/statedb_fuzz_test.go b/core/state/statedb_fuzz_test.go
index c149d073ba..47b22b104e 100644
--- a/core/state/statedb_fuzz_test.go
+++ b/core/state/statedb_fuzz_test.go
@@ -32,7 +32,6 @@ import (
"errors"
"fmt"
"math"
- "math/big"
"math/rand"
"reflect"
"strings"
@@ -43,11 +42,13 @@ import (
"github.com/ava-labs/subnet-evm/core/state/snapshot"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/trie"
- "github.com/ava-labs/subnet-evm/trie/triedb/pathdb"
"github.com/ava-labs/subnet-evm/trie/triestate"
+ "github.com/ava-labs/subnet-evm/triedb"
+ "github.com/ava-labs/subnet-evm/triedb/pathdb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/holiman/uint256"
)
// A stateTest checks that the state changes are correctly captured. Instances
@@ -70,7 +71,7 @@ func newStateTestAction(addr common.Address, r *rand.Rand, index int) testAction
{
name: "SetBalance",
fn: func(a testAction, s *StateDB) {
- s.SetBalance(addr, big.NewInt(a.args[0]))
+ s.SetBalance(addr, uint256.NewInt(uint64(a.args[0])))
},
args: make([]int64, 1),
},
@@ -191,7 +192,7 @@ func (test *stateTest) run() bool {
storageList = append(storageList, copy2DSet(states.Storages))
}
disk = rawdb.NewMemoryDatabase()
- tdb = trie.NewDatabase(disk, &trie.Config{PathDB: pathdb.Defaults})
+ tdb = triedb.NewDatabase(disk, &triedb.Config{PathDB: pathdb.Defaults})
sdb = NewDatabaseWithNodeDB(disk, tdb)
byzantium = rand.Intn(2) == 0
)
@@ -261,7 +262,7 @@ func (test *stateTest) run() bool {
// - the account was indeed not present in trie
// - the account is present in new trie, nil->nil is regarded as invalid
// - the slots transition is correct
-func (test *stateTest) verifyAccountCreation(next common.Hash, db *trie.Database, otr, ntr *trie.Trie, addr common.Address, slots map[common.Hash][]byte) error {
+func (test *stateTest) verifyAccountCreation(next common.Hash, db *triedb.Database, otr, ntr *trie.Trie, addr common.Address, slots map[common.Hash][]byte) error {
// Verify account change
addrHash := crypto.Keccak256Hash(addr.Bytes())
oBlob, err := otr.Get(addrHash.Bytes())
@@ -312,7 +313,7 @@ func (test *stateTest) verifyAccountCreation(next common.Hash, db *trie.Database
// - the account was indeed present in trie
// - the account in old trie matches the provided value
// - the slots transition is correct
-func (test *stateTest) verifyAccountUpdate(next common.Hash, db *trie.Database, otr, ntr *trie.Trie, addr common.Address, origin []byte, slots map[common.Hash][]byte) error {
+func (test *stateTest) verifyAccountUpdate(next common.Hash, db *triedb.Database, otr, ntr *trie.Trie, addr common.Address, origin []byte, slots map[common.Hash][]byte) error {
// Verify account change
addrHash := crypto.Keccak256Hash(addr.Bytes())
oBlob, err := otr.Get(addrHash.Bytes())
@@ -366,7 +367,7 @@ func (test *stateTest) verifyAccountUpdate(next common.Hash, db *trie.Database,
return nil
}
-func (test *stateTest) verify(root common.Hash, next common.Hash, db *trie.Database, accountsOrigin map[common.Address][]byte, storagesOrigin map[common.Address]map[common.Hash][]byte) error {
+func (test *stateTest) verify(root common.Hash, next common.Hash, db *triedb.Database, accountsOrigin map[common.Address][]byte, storagesOrigin map[common.Address]map[common.Hash][]byte) error {
otr, err := trie.New(trie.StateTrieID(root), db)
if err != nil {
return err
diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go
index 7c603cbe4f..60d7ee3b41 100644
--- a/core/state/statedb_test.go
+++ b/core/state/statedb_test.go
@@ -32,7 +32,6 @@ import (
"errors"
"fmt"
"math"
- "math/big"
"math/rand"
"reflect"
"strings"
@@ -44,9 +43,10 @@ import (
"github.com/ava-labs/subnet-evm/core/state/snapshot"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/trie"
- "github.com/ava-labs/subnet-evm/trie/triedb/hashdb"
- "github.com/ava-labs/subnet-evm/trie/triedb/pathdb"
"github.com/ava-labs/subnet-evm/trie/trienode"
+ "github.com/ava-labs/subnet-evm/triedb"
+ "github.com/ava-labs/subnet-evm/triedb/hashdb"
+ "github.com/ava-labs/subnet-evm/triedb/pathdb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
@@ -59,14 +59,14 @@ func TestUpdateLeaks(t *testing.T) {
// Create an empty state database
var (
db = rawdb.NewMemoryDatabase()
- tdb = trie.NewDatabase(db, nil)
+ tdb = triedb.NewDatabase(db, nil)
)
state, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(db, tdb), nil)
// Update it with some accounts
for i := byte(0); i < 255; i++ {
addr := common.BytesToAddress([]byte{i})
- state.AddBalance(addr, big.NewInt(int64(11*i)))
+ state.AddBalance(addr, uint256.NewInt(uint64(11*i)))
state.SetNonce(addr, uint64(42*i))
if i%2 == 0 {
state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i}))
@@ -95,13 +95,13 @@ func TestIntermediateLeaks(t *testing.T) {
// Create two state databases, one transitioning to the final state, the other final from the beginning
transDb := rawdb.NewMemoryDatabase()
finalDb := rawdb.NewMemoryDatabase()
- transNdb := trie.NewDatabase(transDb, nil)
- finalNdb := trie.NewDatabase(finalDb, nil)
+ transNdb := triedb.NewDatabase(transDb, nil)
+ finalNdb := triedb.NewDatabase(finalDb, nil)
transState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(transDb, transNdb), nil)
finalState, _ := New(types.EmptyRootHash, NewDatabaseWithNodeDB(finalDb, finalNdb), nil)
modify := func(state *StateDB, addr common.Address, i, tweak byte) {
- state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak)))
+ state.SetBalance(addr, uint256.NewInt(uint64(11*i)+uint64(tweak)))
state.SetNonce(addr, uint64(42*i+tweak))
if i%2 == 0 {
state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{})
@@ -176,8 +176,8 @@ func TestCopy(t *testing.T) {
orig, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil)
for i := byte(0); i < 255; i++ {
- obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
- obj.AddBalance(big.NewInt(int64(i)))
+ obj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i}))
+ obj.AddBalance(uint256.NewInt(uint64(i)))
orig.updateStateObject(obj)
}
orig.Finalise(false)
@@ -190,13 +190,13 @@ func TestCopy(t *testing.T) {
// modify all in memory
for i := byte(0); i < 255; i++ {
- origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
- copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
- ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
+ origObj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i}))
+ copyObj := copy.getOrNewStateObject(common.BytesToAddress([]byte{i}))
+ ccopyObj := ccopy.getOrNewStateObject(common.BytesToAddress([]byte{i}))
- origObj.AddBalance(big.NewInt(2 * int64(i)))
- copyObj.AddBalance(big.NewInt(3 * int64(i)))
- ccopyObj.AddBalance(big.NewInt(4 * int64(i)))
+ origObj.AddBalance(uint256.NewInt(2 * uint64(i)))
+ copyObj.AddBalance(uint256.NewInt(3 * uint64(i)))
+ ccopyObj.AddBalance(uint256.NewInt(4 * uint64(i)))
orig.updateStateObject(origObj)
copy.updateStateObject(copyObj)
@@ -218,17 +218,17 @@ func TestCopy(t *testing.T) {
// Verify that the three states have been updated independently
for i := byte(0); i < 255; i++ {
- origObj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
- copyObj := copy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
- ccopyObj := ccopy.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
+ origObj := orig.getOrNewStateObject(common.BytesToAddress([]byte{i}))
+ copyObj := copy.getOrNewStateObject(common.BytesToAddress([]byte{i}))
+ ccopyObj := ccopy.getOrNewStateObject(common.BytesToAddress([]byte{i}))
- if want := big.NewInt(3 * int64(i)); origObj.Balance().Cmp(want) != 0 {
+ if want := uint256.NewInt(3 * uint64(i)); origObj.Balance().Cmp(want) != 0 {
t.Errorf("orig obj %d: balance mismatch: have %v, want %v", i, origObj.Balance(), want)
}
- if want := big.NewInt(4 * int64(i)); copyObj.Balance().Cmp(want) != 0 {
+ if want := uint256.NewInt(4 * uint64(i)); copyObj.Balance().Cmp(want) != 0 {
t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, copyObj.Balance(), want)
}
- if want := big.NewInt(5 * int64(i)); ccopyObj.Balance().Cmp(want) != 0 {
+ if want := uint256.NewInt(5 * uint64(i)); ccopyObj.Balance().Cmp(want) != 0 {
t.Errorf("copy obj %d: balance mismatch: have %v, want %v", i, ccopyObj.Balance(), want)
}
}
@@ -276,14 +276,14 @@ func newTestAction(addr common.Address, r *rand.Rand) testAction {
{
name: "SetBalance",
fn: func(a testAction, s *StateDB) {
- s.SetBalance(addr, big.NewInt(a.args[0]))
+ s.SetBalance(addr, uint256.NewInt(uint64(a.args[0])))
},
args: make([]int64, 1),
},
{
name: "AddBalance",
fn: func(a testAction, s *StateDB) {
- s.AddBalance(addr, big.NewInt(a.args[0]))
+ s.AddBalance(addr, uint256.NewInt(uint64(a.args[0])))
},
args: make([]int64, 1),
},
@@ -541,12 +541,12 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
func TestTouchDelete(t *testing.T) {
s := newStateEnv()
- s.state.GetOrNewStateObject(common.Address{})
+ s.state.getOrNewStateObject(common.Address{})
root, _ := s.state.Commit(0, false, false)
s.state, _ = NewWithSnapshot(root, s.state.db, s.state.snap)
snapshot := s.state.Snapshot()
- s.state.AddBalance(common.Address{}, new(big.Int))
+ s.state.AddBalance(common.Address{}, new(uint256.Int))
if len(s.state.journal.dirties) != 1 {
t.Fatal("expected one dirty state object")
@@ -562,7 +562,7 @@ func TestTouchDelete(t *testing.T) {
func TestCopyOfCopy(t *testing.T) {
state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil)
addr := common.HexToAddress("aaaa")
- state.SetBalance(addr, big.NewInt(42))
+ state.SetBalance(addr, uint256.NewInt(42))
if got := state.Copy().GetBalance(addr).Uint64(); got != 42 {
t.Fatalf("1st copy fail, expected 42, got %v", got)
@@ -585,11 +585,11 @@ func TestCopyCommitCopy(t *testing.T) {
skey := common.HexToHash("aaa")
sval := common.HexToHash("bbb")
- state.SetBalance(addr, big.NewInt(42)) // Change the account trie
- state.SetCode(addr, []byte("hello")) // Change an external metadata
- state.SetState(addr, skey, sval) // Change the storage trie
+ state.SetBalance(addr, uint256.NewInt(42)) // Change the account trie
+ state.SetCode(addr, []byte("hello")) // Change an external metadata
+ state.SetState(addr, skey, sval) // Change the storage trie
- if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
+ if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42)
}
if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@@ -603,7 +603,7 @@ func TestCopyCommitCopy(t *testing.T) {
}
// Copy the non-committed state database and check pre/post commit balance
copyOne := state.Copy()
- if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
+ if balance := copyOne.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("first copy pre-commit balance mismatch: have %v, want %v", balance, 42)
}
if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@@ -617,7 +617,7 @@ func TestCopyCommitCopy(t *testing.T) {
}
// Copy the copy and check the balance once more
copyTwo := copyOne.Copy()
- if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
+ if balance := copyTwo.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("second copy balance mismatch: have %v, want %v", balance, 42)
}
if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@@ -632,7 +632,7 @@ func TestCopyCommitCopy(t *testing.T) {
// Commit state, ensure states can be loaded from disk
root, _ := state.Commit(0, false, false)
state, _ = New(root, tdb, nil)
- if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
+ if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("state post-commit balance mismatch: have %v, want %v", balance, 42)
}
if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@@ -658,11 +658,11 @@ func TestCopyCopyCommitCopy(t *testing.T) {
skey := common.HexToHash("aaa")
sval := common.HexToHash("bbb")
- state.SetBalance(addr, big.NewInt(42)) // Change the account trie
- state.SetCode(addr, []byte("hello")) // Change an external metadata
- state.SetState(addr, skey, sval) // Change the storage trie
+ state.SetBalance(addr, uint256.NewInt(42)) // Change the account trie
+ state.SetCode(addr, []byte("hello")) // Change an external metadata
+ state.SetState(addr, skey, sval) // Change the storage trie
- if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
+ if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42)
}
if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@@ -676,7 +676,7 @@ func TestCopyCopyCommitCopy(t *testing.T) {
}
// Copy the non-committed state database and check pre/post commit balance
copyOne := state.Copy()
- if balance := copyOne.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
+ if balance := copyOne.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("first copy balance mismatch: have %v, want %v", balance, 42)
}
if code := copyOne.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@@ -690,7 +690,7 @@ func TestCopyCopyCommitCopy(t *testing.T) {
}
// Copy the copy and check the balance once more
copyTwo := copyOne.Copy()
- if balance := copyTwo.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
+ if balance := copyTwo.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("second copy pre-commit balance mismatch: have %v, want %v", balance, 42)
}
if code := copyTwo.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@@ -704,7 +704,7 @@ func TestCopyCopyCommitCopy(t *testing.T) {
}
// Copy the copy-copy and check the balance once more
copyThree := copyTwo.Copy()
- if balance := copyThree.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
+ if balance := copyThree.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("third copy balance mismatch: have %v, want %v", balance, 42)
}
if code := copyThree.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@@ -727,11 +727,11 @@ func TestCommitCopy(t *testing.T) {
skey := common.HexToHash("aaa")
sval := common.HexToHash("bbb")
- state.SetBalance(addr, big.NewInt(42)) // Change the account trie
- state.SetCode(addr, []byte("hello")) // Change an external metadata
- state.SetState(addr, skey, sval) // Change the storage trie
+ state.SetBalance(addr, uint256.NewInt(42)) // Change the account trie
+ state.SetCode(addr, []byte("hello")) // Change an external metadata
+ state.SetState(addr, skey, sval) // Change the storage trie
- if balance := state.GetBalance(addr); balance.Cmp(big.NewInt(42)) != 0 {
+ if balance := state.GetBalance(addr); balance.Cmp(uint256.NewInt(42)) != 0 {
t.Fatalf("initial balance mismatch: have %v, want %v", balance, 42)
}
if code := state.GetCode(addr); !bytes.Equal(code, []byte("hello")) {
@@ -746,7 +746,7 @@ func TestCommitCopy(t *testing.T) {
// Copy the committed state database, the copied one is not functional.
state.Commit(0, true, false)
copied := state.Copy()
- if balance := copied.GetBalance(addr); balance.Cmp(big.NewInt(0)) != 0 {
+ if balance := copied.GetBalance(addr); balance.Cmp(uint256.NewInt(0)) != 0 {
t.Fatalf("unexpected balance: have %v", balance)
}
if code := copied.GetCode(addr); code != nil {
@@ -776,7 +776,7 @@ func TestDeleteCreateRevert(t *testing.T) {
state, _ := New(types.EmptyRootHash, NewDatabase(rawdb.NewMemoryDatabase()), nil)
addr := common.BytesToAddress([]byte("so"))
- state.SetBalance(addr, big.NewInt(1))
+ state.SetBalance(addr, uint256.NewInt(1))
root, _ := state.Commit(0, false, false)
state, _ = NewWithSnapshot(root, state.db, state.snap)
@@ -786,7 +786,7 @@ func TestDeleteCreateRevert(t *testing.T) {
state.Finalise(true)
id := state.Snapshot()
- state.SetBalance(addr, big.NewInt(2))
+ state.SetBalance(addr, uint256.NewInt(2))
state.RevertToSnapshot(id)
// Commit the entire state and make sure we don't crash and have the correct state
@@ -809,34 +809,34 @@ func TestMissingTrieNodes(t *testing.T) {
func testMissingTrieNodes(t *testing.T, scheme string) {
// Create an initial state with a few accounts
var (
- triedb *trie.Database
- memDb = rawdb.NewMemoryDatabase()
+ tdb *triedb.Database
+ memDb = rawdb.NewMemoryDatabase()
)
if scheme == rawdb.PathScheme {
- triedb = trie.NewDatabase(memDb, &trie.Config{PathDB: &pathdb.Config{
+ tdb = triedb.NewDatabase(memDb, &triedb.Config{PathDB: &pathdb.Config{
CleanCacheSize: 0,
DirtyCacheSize: 0,
}}) // disable caching
} else {
- triedb = trie.NewDatabase(memDb, &trie.Config{HashDB: &hashdb.Config{
+ tdb = triedb.NewDatabase(memDb, &triedb.Config{HashDB: &hashdb.Config{
CleanCacheSize: 0,
}}) // disable caching
}
- db := NewDatabaseWithNodeDB(memDb, triedb)
+ db := NewDatabaseWithNodeDB(memDb, tdb)
var root common.Hash
state, _ := New(types.EmptyRootHash, db, nil)
addr := common.BytesToAddress([]byte("so"))
{
- state.SetBalance(addr, big.NewInt(1))
+ state.SetBalance(addr, uint256.NewInt(1))
state.SetCode(addr, []byte{1, 2, 3})
a2 := common.BytesToAddress([]byte("another"))
- state.SetBalance(a2, big.NewInt(100))
+ state.SetBalance(a2, uint256.NewInt(100))
state.SetCode(a2, []byte{1, 2, 4})
root, _ = state.Commit(0, false, false)
t.Logf("root: %x", root)
// force-flush
- triedb.Commit(root, false)
+ tdb.Commit(root, false)
}
// Create a new state on the old root
state, _ = New(root, db, nil)
@@ -856,7 +856,7 @@ func testMissingTrieNodes(t *testing.T, scheme string) {
t.Errorf("expected %d, got %d", exp, got)
}
// Modify the state
- state.SetBalance(addr, big.NewInt(2))
+ state.SetBalance(addr, uint256.NewInt(2))
root, err := state.Commit(0, false, false)
if err == nil {
t.Fatalf("expected error, got root :%x", root)
@@ -1043,7 +1043,7 @@ func TestFlushOrderDataLoss(t *testing.T) {
// Create a state trie with many accounts and slots
var (
memdb = rawdb.NewMemoryDatabase()
- triedb = trie.NewDatabase(memdb, nil)
+ triedb = triedb.NewDatabase(memdb, nil)
statedb = NewDatabaseWithNodeDB(memdb, triedb)
state, _ = New(types.EmptyRootHash, statedb, nil)
)
@@ -1115,7 +1115,7 @@ func TestStateDBTransientStorage(t *testing.T) {
func TestResetObject(t *testing.T) {
var (
disk = rawdb.NewMemoryDatabase()
- tdb = trie.NewDatabase(disk, nil)
+ tdb = triedb.NewDatabase(disk, nil)
db = NewDatabaseWithNodeDB(disk, tdb)
snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, common.Hash{}, types.EmptyRootHash)
state, _ = New(types.EmptyRootHash, db, snaps)
@@ -1124,13 +1124,13 @@ func TestResetObject(t *testing.T) {
slotB = common.HexToHash("0x2")
)
// Initialize account with balance and storage in first transaction.
- state.SetBalance(addr, big.NewInt(1))
+ state.SetBalance(addr, uint256.NewInt(1))
state.SetState(addr, slotA, common.BytesToHash([]byte{0x1}))
state.IntermediateRoot(true)
// Reset account and mutate balance and storages
state.CreateAccount(addr)
- state.SetBalance(addr, big.NewInt(2))
+ state.SetBalance(addr, uint256.NewInt(2))
state.SetState(addr, slotB, common.BytesToHash([]byte{0x2}))
root, _ := state.CommitWithSnap(0, true, snaps, common.Hash{}, common.Hash{}, false)
@@ -1149,14 +1149,14 @@ func TestResetObject(t *testing.T) {
func TestDeleteStorage(t *testing.T) {
var (
disk = rawdb.NewMemoryDatabase()
- tdb = trie.NewDatabase(disk, nil)
+ tdb = triedb.NewDatabase(disk, nil)
db = NewDatabaseWithNodeDB(disk, tdb)
snaps, _ = snapshot.New(snapshot.Config{CacheSize: 10}, disk, tdb, common.Hash{}, types.EmptyRootHash)
state, _ = New(types.EmptyRootHash, db, snaps)
addr = common.HexToAddress("0x1")
)
// Initialize account and populate storage
- state.SetBalance(addr, big.NewInt(1))
+ state.SetBalance(addr, uint256.NewInt(1))
state.CreateAccount(addr)
for i := 0; i < 1000; i++ {
slot := common.Hash(uint256.NewInt(uint64(i)).Bytes32())
@@ -1168,7 +1168,7 @@ func TestDeleteStorage(t *testing.T) {
fastState, _ := New(root, db, snaps)
slowState, _ := New(root, db, nil)
- obj := fastState.GetOrNewStateObject(addr)
+ obj := fastState.getOrNewStateObject(addr)
storageRoot := obj.data.Root
_, _, fastNodes, err := fastState.deleteStorage(addr, crypto.Keccak256Hash(addr[:]), storageRoot)
diff --git a/core/state/trie_prefetcher_test.go b/core/state/trie_prefetcher_test.go
index 588d251561..b8edcbb6a8 100644
--- a/core/state/trie_prefetcher_test.go
+++ b/core/state/trie_prefetcher_test.go
@@ -34,6 +34,7 @@ import (
"github.com/ava-labs/subnet-evm/core/rawdb"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/common"
+ "github.com/holiman/uint256"
)
const maxConcurrency = 4
@@ -46,9 +47,9 @@ func filledStateDB() *StateDB {
skey := common.HexToHash("aaa")
sval := common.HexToHash("bbb")
- state.SetBalance(addr, big.NewInt(42)) // Change the account trie
- state.SetCode(addr, []byte("hello")) // Change an external metadata
- state.SetState(addr, skey, sval) // Change the storage trie
+ state.SetBalance(addr, uint256.NewInt(42)) // Change the account trie
+ state.SetCode(addr, []byte("hello")) // Change an external metadata
+ state.SetState(addr, skey, sval) // Change the storage trie
for i := 0; i < 100; i++ {
sk := common.BigToHash(big.NewInt(int64(i)))
state.SetState(addr, sk, sk) // Change the storage trie
diff --git a/core/state_processor.go b/core/state_processor.go
index fb839cc187..7853810f43 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -199,7 +199,7 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb *stat
}
vmenv.Reset(NewEVMTxContext(msg), statedb)
statedb.AddAddressToAccessList(params.BeaconRootsStorageAddress)
- _, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.Big0)
+ _, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
statedb.Finalise(true)
}
diff --git a/core/state_processor_test.go b/core/state_processor_test.go
index ad35b811bd..f2e0a770d2 100644
--- a/core/state_processor_test.go
+++ b/core/state_processor_test.go
@@ -113,8 +113,8 @@ func TestStateProcessorErrors(t *testing.T) {
gspec = &Genesis{
Config: config,
Timestamp: uint64(upgrade.InitiallyActiveTime.Unix()),
- Alloc: GenesisAlloc{
- common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
+ Alloc: types.GenesisAlloc{
+ common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): types.Account{
Balance: big.NewInt(4000000000000000000), // 4 ether
Nonce: 0,
},
@@ -219,7 +219,7 @@ func TestStateProcessorErrors(t *testing.T) {
txs: []*types.Transaction{
mkDynamicTx(0, common.Address{}, params.TxGas, bigNumber, bigNumber),
},
- want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 have 4000000000000000000 want 2431633873983640103894990685182446064918669677978451844828609264166175722438635000",
+ want: "could not apply tx 0 [0xd82a0c2519acfeac9a948258c47e784acd20651d9d80f9a1c67b4137651c3a24]: insufficient funds for gas * price + value: address 0x71562b71999873DB5b286dF957af199Ec94617F7 required balance exceeds 256 bits",
},
{ // ErrMaxInitCodeSizeExceeded
txs: []*types.Transaction{
@@ -269,8 +269,8 @@ func TestStateProcessorErrors(t *testing.T) {
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
},
- Alloc: GenesisAlloc{
- common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
+ Alloc: types.GenesisAlloc{
+ common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): types.Account{
Balance: big.NewInt(1000000000000000000), // 1 ether
Nonce: 0,
},
@@ -308,8 +308,8 @@ func TestStateProcessorErrors(t *testing.T) {
db = rawdb.NewMemoryDatabase()
gspec = &Genesis{
Config: config,
- Alloc: GenesisAlloc{
- common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
+ Alloc: types.GenesisAlloc{
+ common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): types.Account{
Balance: big.NewInt(1000000000000000000), // 1 ether
Nonce: 0,
Code: common.FromHex("0xB0B0FACE"),
diff --git a/core/state_transition.go b/core/state_transition.go
index a71bacc192..a4aff4b6bc 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -27,7 +27,6 @@
package core
import (
- "errors"
"fmt"
"math"
"math/big"
@@ -40,6 +39,8 @@ import (
"github.com/ava-labs/subnet-evm/vmerrs"
"github.com/ethereum/go-ethereum/common"
cmath "github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/crypto/kzg4844"
+ "github.com/holiman/uint256"
)
// ExecutionResult includes all output after executing given evm
@@ -311,7 +312,11 @@ func (st *StateTransition) buyGas() error {
mgval.Add(mgval, blobFee)
}
}
- if have, want := st.state.GetBalance(st.msg.From), balanceCheck; have.Cmp(want) < 0 {
+ balanceCheckU256, overflow := uint256.FromBig(balanceCheck)
+ if overflow {
+ return fmt.Errorf("%w: address %v required balance exceeds 256 bits", ErrInsufficientFunds, st.msg.From.Hex())
+ }
+ if have, want := st.state.GetBalance(st.msg.From), balanceCheckU256; have.Cmp(want) < 0 {
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want)
}
if err := st.gp.SubGas(st.msg.GasLimit); err != nil {
@@ -320,7 +325,8 @@ func (st *StateTransition) buyGas() error {
st.gasRemaining += st.msg.GasLimit
st.initialGas = st.msg.GasLimit
- st.state.SubBalance(st.msg.From, mgval)
+ mgvalU256, _ := uint256.FromBig(mgval)
+ st.state.SubBalance(st.msg.From, mgvalU256)
return nil
}
@@ -386,13 +392,18 @@ func (st *StateTransition) preCheck() error {
}
// Check the blob version validity
if msg.BlobHashes != nil {
+ // The to field of a blob tx type is mandatory, and a `BlobTx` transaction internally
+ // has it as a non-nillable value, so any msg derived from blob transaction has it non-nil.
+ // However, messages created through RPC (eth_call) don't have this restriction.
+ if msg.To == nil {
+ return ErrBlobTxCreate
+ }
if len(msg.BlobHashes) == 0 {
- return errors.New("blob transaction missing blob hashes")
+ return ErrMissingBlobHashes
}
for i, hash := range msg.BlobHashes {
- if hash[0] != params.BlobTxHashVersion {
- return fmt.Errorf("blob %d hash version mismatch (have %d, supported %d)",
- i, hash[0], params.BlobTxHashVersion)
+ if !kzg4844.IsValidVersionedHash(hash[:]) {
+ return fmt.Errorf("blob %d has invalid hash version", i)
}
}
}
@@ -466,7 +477,11 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
st.gasRemaining -= gas
// Check clause 6
- if msg.Value.Sign() > 0 && !st.evm.Context.CanTransfer(st.state, msg.From, msg.Value) {
+ value, overflow := uint256.FromBig(msg.Value)
+ if overflow {
+ return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex())
+ }
+ if !value.IsZero() && !st.evm.Context.CanTransfer(st.state, msg.From, value) {
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex())
}
@@ -485,14 +500,20 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) {
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
)
if contractCreation {
- ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, msg.Value)
+ ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, value)
} else {
// Increment the nonce for the next transaction
st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1)
- ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, msg.Value)
+ ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, value)
+ }
+ price, overflow := uint256.FromBig(msg.GasPrice)
+ if overflow {
+ return nil, ErrGasUintOverflow
}
gasRefund := st.refundGas(rules.IsSubnetEVM)
- st.state.AddBalance(st.evm.Context.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), msg.GasPrice))
+ fee := new(uint256.Int).SetUint64(st.gasUsed())
+ fee.Mul(fee, price)
+ st.state.AddBalance(st.evm.Context.Coinbase, fee)
return &ExecutionResult{
UsedGas: st.gasUsed(),
@@ -515,7 +536,8 @@ func (st *StateTransition) refundGas(subnetEVM bool) uint64 {
}
// Return ETH for remaining gas, exchanged at the original rate.
- remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gasRemaining), st.msg.GasPrice)
+ remaining := uint256.NewInt(st.gasRemaining)
+ remaining = remaining.Mul(remaining, uint256.MustFromBig(st.msg.GasPrice))
st.state.AddBalance(st.msg.From, remaining)
// Also return remaining gas to the block gas counter so it is
diff --git a/core/test_blockchain.go b/core/test_blockchain.go
index 3a45592614..02f98b61cf 100644
--- a/core/test_blockchain.go
+++ b/core/test_blockchain.go
@@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
+ "github.com/holiman/uint256"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -238,9 +239,10 @@ func TestInsertChainAcceptSingleBlock(t *testing.T, create func(db ethdb.Databas
if nonce != 1 {
return fmt.Errorf("expected nonce addr1: 1, found nonce: %d", nonce)
}
- transferredFunds := big.NewInt(10000)
+ transferredFunds := uint256.MustFromBig(big.NewInt(10000))
balance1 := sdb.GetBalance(addr1)
- expectedBalance1 := new(big.Int).Sub(genesisBalance, transferredFunds)
+ genesisBalance := uint256.MustFromBig(genesisBalance)
+ expectedBalance1 := new(uint256.Int).Sub(genesisBalance, transferredFunds)
if balance1.Cmp(expectedBalance1) != 0 {
return fmt.Errorf("expected addr1 balance: %d, found balance: %d", expectedBalance1, balance1)
}
@@ -274,7 +276,7 @@ func TestInsertLongForkedChain(t *testing.T, create func(db ethdb.Database, gspe
genesisBalance := big.NewInt(1000000000)
gspec := &Genesis{
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
- Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}},
+ Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}},
}
blockchain, err := create(chainDB, gspec, common.Hash{})
@@ -405,8 +407,9 @@ func TestInsertLongForkedChain(t *testing.T, create func(db ethdb.Database, gspe
return fmt.Errorf("expected addr1 nonce: 129, found nonce %d", nonce1)
}
balance1 := sdb.GetBalance(addr1)
- transferredFunds := new(big.Int).Mul(big.NewInt(129), big.NewInt(10000))
- expectedBalance := new(big.Int).Sub(genesisBalance, transferredFunds)
+ transferredFunds := uint256.NewInt(129 * 10_000)
+ genesisBalance := uint256.MustFromBig(genesisBalance)
+ expectedBalance := new(uint256.Int).Sub(genesisBalance, transferredFunds)
if balance1.Cmp(expectedBalance) != 0 {
return fmt.Errorf("expected addr1 balance: %d, found balance: %d", expectedBalance, balance1)
}
@@ -437,7 +440,7 @@ func TestAcceptNonCanonicalBlock(t *testing.T, create func(db ethdb.Database, gs
genesisBalance := big.NewInt(1000000000)
gspec := &Genesis{
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
- Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}},
+ Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}},
}
blockchain, err := create(chainDB, gspec, common.Hash{})
@@ -513,8 +516,9 @@ func TestAcceptNonCanonicalBlock(t *testing.T, create func(db ethdb.Database, gs
return fmt.Errorf("expected addr1 nonce: 1, found nonce: %d", nonce1)
}
balance1 := sdb.GetBalance(addr1)
- transferredFunds := big.NewInt(5000)
- expectedBalance := new(big.Int).Sub(genesisBalance, transferredFunds)
+ transferredFunds := uint256.NewInt(5000)
+ genesisBalance := uint256.MustFromBig(genesisBalance)
+ expectedBalance := new(uint256.Int).Sub(genesisBalance, transferredFunds)
if balance1.Cmp(expectedBalance) != 0 {
return fmt.Errorf("expected balance1: %d, found balance: %d", expectedBalance, balance1)
}
@@ -545,7 +549,7 @@ func TestSetPreferenceRewind(t *testing.T, create func(db ethdb.Database, gspec
genesisBalance := big.NewInt(1000000000)
gspec := &Genesis{
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
- Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}},
+ Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}},
}
blockchain, err := create(chainDB, gspec, common.Hash{})
@@ -607,6 +611,7 @@ func TestSetPreferenceRewind(t *testing.T, create func(db ethdb.Database, gspec
return fmt.Errorf("expected addr1 nonce: 0, found nonce: %d", nonce1)
}
balance1 := sdb.GetBalance(addr1)
+ genesisBalance := uint256.MustFromBig(genesisBalance)
if balance1.Cmp(genesisBalance) != 0 {
return fmt.Errorf("expected addr1 balance: %d, found balance: %d", genesisBalance, balance1)
}
@@ -615,7 +620,7 @@ func TestSetPreferenceRewind(t *testing.T, create func(db ethdb.Database, gspec
return fmt.Errorf("expected addr2 nonce: 0, found nonce: %d", nonce2)
}
balance2 := sdb.GetBalance(addr2)
- if balance2.Cmp(big.NewInt(0)) != 0 {
+ if balance2.Cmp(common.U2560) != 0 {
return fmt.Errorf("expected addr2 balance: 0, found balance %d", balance2)
}
return nil
@@ -640,9 +645,10 @@ func TestSetPreferenceRewind(t *testing.T, create func(db ethdb.Database, gspec
if nonce != 1 {
return fmt.Errorf("expected addr1 nonce: 1, found nonce: %d", nonce)
}
- transferredFunds := big.NewInt(10000)
+ transferredFunds := uint256.NewInt(10000)
balance1 := sdb.GetBalance(addr1)
- expectedBalance1 := new(big.Int).Sub(genesisBalance, transferredFunds)
+ genesisBalance := uint256.MustFromBig(genesisBalance)
+ expectedBalance1 := new(uint256.Int).Sub(genesisBalance, transferredFunds)
if balance1.Cmp(expectedBalance1) != 0 {
return fmt.Errorf("expected addr1 balance: %d, found balance %d", expectedBalance1, balance1)
}
@@ -796,6 +802,7 @@ func TestBuildOnVariousStages(t *testing.T, create func(db ethdb.Database, gspec
return fmt.Errorf("expected nonce addr1: 5, found nonce: %d", nonce)
}
balance1 := sdb.GetBalance(addr1)
+ genesisBalance := uint256.MustFromBig(genesisBalance)
expectedBalance1 := genesisBalance
if balance1.Cmp(expectedBalance1) != 0 {
return fmt.Errorf("expected addr1 balance: %d, found balance: %d", expectedBalance1, balance1)
@@ -813,7 +820,7 @@ func TestBuildOnVariousStages(t *testing.T, create func(db ethdb.Database, gspec
}
balance3 := sdb.GetBalance(addr3)
- expectedBalance3 := common.Big0
+ expectedBalance3 := common.U2560
if balance3.Cmp(expectedBalance3) != 0 {
return fmt.Errorf("expected addr3 balance: %d, found balance: %d", expectedBalance3, balance3)
}
@@ -879,7 +886,7 @@ func TestReorgReInsert(t *testing.T, create func(db ethdb.Database, gspec *Genes
genesisBalance := big.NewInt(1000000000)
gspec := &Genesis{
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
- Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}},
+ Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}},
}
blockchain, err := create(chainDB, gspec, common.Hash{})
@@ -939,8 +946,9 @@ func TestReorgReInsert(t *testing.T, create func(db ethdb.Database, gspec *Genes
return fmt.Errorf("expected addr1 nonce: 3, found nonce: %d", nonce1)
}
balance1 := sdb.GetBalance(addr1)
- transferredFunds := big.NewInt(30000)
- expectedBalance := new(big.Int).Sub(genesisBalance, transferredFunds)
+ transferredFunds := uint256.NewInt(30000)
+ genesisBalance := uint256.MustFromBig(genesisBalance)
+ expectedBalance := new(uint256.Int).Sub(genesisBalance, transferredFunds)
if balance1.Cmp(expectedBalance) != 0 {
return fmt.Errorf("expected balance1: %d, found balance: %d", expectedBalance, balance1)
}
@@ -984,7 +992,7 @@ func TestAcceptBlockIdenticalStateRoot(t *testing.T, create func(db ethdb.Databa
genesisBalance := big.NewInt(1000000000)
gspec := &Genesis{
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
- Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}},
+ Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}},
}
blockchain, err := create(chainDB, gspec, common.Hash{})
@@ -1082,7 +1090,7 @@ func TestAcceptBlockIdenticalStateRoot(t *testing.T, create func(db ethdb.Databa
return fmt.Errorf("expected addr1 nonce: 2, found nonce: %d", nonce1)
}
balance1 := sdb.GetBalance(addr1)
- expectedBalance := common.Big0
+ expectedBalance := common.U2560
if balance1.Cmp(expectedBalance) != 0 {
return fmt.Errorf("expected balance1: %d, found balance: %d", expectedBalance, balance1)
}
@@ -1091,6 +1099,7 @@ func TestAcceptBlockIdenticalStateRoot(t *testing.T, create func(db ethdb.Databa
return fmt.Errorf("expected addr2 nonce: 0, found nonce %d", nonce2)
}
balance2 := sdb.GetBalance(addr2)
+ genesisBalance := uint256.MustFromBig(genesisBalance)
if balance2.Cmp(genesisBalance) != 0 {
return fmt.Errorf("expected balance2: %d, found %d", genesisBalance, balance2)
}
@@ -1127,7 +1136,7 @@ func TestReprocessAcceptBlockIdenticalStateRoot(t *testing.T, create func(db eth
genesisBalance := big.NewInt(1000000000)
gspec := &Genesis{
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
- Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}},
+ Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}},
}
blockchain, err := create(chainDB, gspec, common.Hash{})
@@ -1249,7 +1258,7 @@ func TestReprocessAcceptBlockIdenticalStateRoot(t *testing.T, create func(db eth
return fmt.Errorf("expected addr1 nonce: 2, found nonce: %d", nonce1)
}
balance1 := sdb.GetBalance(addr1)
- expectedBalance := common.Big0
+ expectedBalance := common.U2560
if balance1.Cmp(expectedBalance) != 0 {
return fmt.Errorf("expected balance1: %d, found balance: %d", expectedBalance, balance1)
}
@@ -1258,6 +1267,7 @@ func TestReprocessAcceptBlockIdenticalStateRoot(t *testing.T, create func(db eth
return fmt.Errorf("expected addr2 nonce: 0, found nonce %d", nonce2)
}
balance2 := sdb.GetBalance(addr2)
+ genesisBalance := uint256.MustFromBig(genesisBalance)
if balance2.Cmp(genesisBalance) != 0 {
return fmt.Errorf("expected balance2: %d, found %d", genesisBalance, balance2)
}
@@ -1383,7 +1393,7 @@ func TestInsertChainValidBlockFee(t *testing.T, create func(db ethdb.Database, g
genesisBalance := new(big.Int).Mul(big.NewInt(1000000), big.NewInt(params.Ether))
gspec := &Genesis{
Config: params.TestChainConfig,
- Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}},
+ Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}},
}
blockchain, err := create(chainDB, gspec, common.Hash{})
@@ -1435,10 +1445,12 @@ func TestInsertChainValidBlockFee(t *testing.T, create func(db ethdb.Database, g
return fmt.Errorf("expected nonce addr1: 1, found nonce: %d", nonce)
}
balance1 := sdb.GetBalance(addr1)
- expectedBalance1 := new(big.Int).Sub(genesisBalance, transfer)
+ transfer := uint256.MustFromBig(transfer)
+ genesisBalance := uint256.MustFromBig(genesisBalance)
+ expectedBalance1 := new(uint256.Int).Sub(genesisBalance, transfer)
baseFee := params.DefaultFeeConfig.MinBaseFee
feeSpend := new(big.Int).Mul(new(big.Int).Add(baseFee, tip), new(big.Int).SetUint64(params.TxGas))
- expectedBalance1.Sub(expectedBalance1, feeSpend)
+ expectedBalance1.Sub(expectedBalance1, uint256.MustFromBig(feeSpend))
if balance1.Cmp(expectedBalance1) != 0 {
return fmt.Errorf("expected addr1 balance: %d, found balance: %d", expectedBalance1, balance1)
}
diff --git a/core/txindexer.go b/core/txindexer.go
new file mode 100644
index 0000000000..178f31d05f
--- /dev/null
+++ b/core/txindexer.go
@@ -0,0 +1,218 @@
+// Copyright 2024 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
+
+package core
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/ava-labs/subnet-evm/core/rawdb"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// TxIndexProgress is the struct describing the progress for transaction indexing.
+type TxIndexProgress struct {
+ Indexed uint64 // number of blocks whose transactions are indexed
+ Remaining uint64 // number of blocks whose transactions are not indexed yet
+}
+
+// Done returns an indicator if the transaction indexing is finished.
+func (progress TxIndexProgress) Done() bool {
+ return progress.Remaining == 0
+}
+
+// txIndexer is the module responsible for maintaining transaction indexes
+// according to the configured indexing range by users.
+type txIndexer struct {
+ // limit is the maximum number of blocks from head whose tx indexes
+ // are reserved:
+ // * 0: means the entire chain should be indexed
+ // * N: means the latest N blocks [HEAD-N+1, HEAD] should be indexed
+ // and all others shouldn't.
+ limit uint64
+ db ethdb.Database
+ progress chan chan TxIndexProgress
+ term chan chan struct{}
+ closed chan struct{}
+
+ chain *BlockChain
+}
+
+// newTxIndexer initializes the transaction indexer.
+func newTxIndexer(limit uint64, chain *BlockChain) *txIndexer {
+ indexer := &txIndexer{
+ limit: limit,
+ db: chain.db,
+ progress: make(chan chan TxIndexProgress),
+ term: make(chan chan struct{}),
+ closed: make(chan struct{}),
+ chain: chain,
+ }
+ chain.wg.Add(1)
+ go func() {
+ defer chain.wg.Done()
+ indexer.loop(chain)
+ }()
+
+ var msg string
+ if limit == 0 {
+ msg = "entire chain"
+ } else {
+ msg = fmt.Sprintf("last %d blocks", limit)
+ }
+ log.Info("Initialized transaction indexer", "range", msg)
+
+ return indexer
+}
+
+// run executes the scheduled indexing/unindexing task in a separate thread.
+// If the stop channel is closed, the task should be terminated as soon as
+// possible, the done channel will be closed once the task is finished.
+func (indexer *txIndexer) run(tail *uint64, head uint64, stop chan struct{}, done chan struct{}) {
+ start := time.Now()
+ defer func() {
+ txUnindexTimer.Inc(time.Since(start).Milliseconds())
+ close(done)
+ }()
+
+ // Short circuit if chain is empty and nothing to index.
+ if head == 0 {
+ return
+ }
+
+ // Defensively ensure tail is not nil.
+ tailValue := uint64(0)
+ if tail != nil {
+ // use intermediate variable to avoid modifying the pointer
+ tailValue = *tail
+ }
+
+ if head-indexer.limit+1 >= tailValue {
+ // Unindex a part of stale indices and forward index tail to HEAD-limit
+ rawdb.UnindexTransactions(indexer.db, tailValue, head-indexer.limit+1, stop, false)
+ }
+}
+
+// loop is the scheduler of the indexer, assigning indexing/unindexing tasks depending
+// on the received chain event.
+func (indexer *txIndexer) loop(chain *BlockChain) {
+ defer close(indexer.closed)
+ // 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.
+ lastHead uint64 // The latest announced chain head (whose tx indexes are assumed created)
+
+ headCh = make(chan ChainEvent)
+ sub = chain.SubscribeChainAcceptedEvent(headCh)
+ )
+ if sub == nil {
+ log.Warn("could not create chain accepted subscription to unindex txs")
+ return
+ }
+ defer sub.Unsubscribe()
+
+ log.Info("Initialized transaction unindexer", "limit", indexer.limit)
+
+ // Launch the initial processing if chain is not empty (head != genesis).
+ // This step is useful in these scenarios that chain has no progress.
+ if head := indexer.chain.CurrentBlock(); head != nil && head.Number.Uint64() > indexer.limit {
+ stop = make(chan struct{})
+ done = make(chan struct{})
+ lastHead = head.Number.Uint64()
+ indexer.chain.wg.Add(1)
+ go func() {
+ indexer.lockedRun(head.Number.Uint64(), stop, done)
+ }()
+ }
+ for {
+ select {
+ case head := <-headCh:
+ headNum := head.Block.NumberU64()
+ if headNum < indexer.limit {
+ break
+ }
+
+ if done == nil {
+ stop = make(chan struct{})
+ done = make(chan struct{})
+ indexer.chain.wg.Add(1)
+ go func() {
+ indexer.lockedRun(headNum, stop, done)
+ }()
+ }
+ lastHead = head.Block.NumberU64()
+ case <-done:
+ stop = nil
+ done = nil
+ case ch := <-indexer.progress:
+ ch <- indexer.report(lastHead, rawdb.ReadTxIndexTail(indexer.db))
+ case ch := <-indexer.term:
+ if stop != nil {
+ close(stop)
+ }
+ if done != nil {
+ log.Info("Waiting background transaction unindexer to exit")
+ <-done
+ }
+ close(ch)
+ return
+ }
+ }
+}
+
+// report returns the tx indexing progress.
+func (indexer *txIndexer) report(head uint64, tail *uint64) TxIndexProgress {
+ total := indexer.limit
+ if indexer.limit == 0 || total > head {
+ total = head + 1 // genesis included
+ }
+ var indexed uint64
+ if tail != nil {
+ indexed = head - *tail + 1
+ }
+ // The value of indexed might be larger than total if some blocks need
+ // to be unindexed, avoiding a negative remaining.
+ var remaining uint64
+ if indexed < total {
+ remaining = total - indexed
+ }
+ return TxIndexProgress{
+ Indexed: indexed,
+ Remaining: remaining,
+ }
+}
+
+// close shutdown the indexer. Safe to be called for multiple times.
+func (indexer *txIndexer) close() {
+ ch := make(chan struct{})
+ select {
+ case indexer.term <- ch:
+ <-ch
+ case <-indexer.closed:
+ }
+}
+
+// lockedRun runs the indexing/unindexing task in a locked manner. It reads
+// the current tail index from the database.
+func (indexer *txIndexer) lockedRun(head uint64, stop chan struct{}, done chan struct{}) {
+ indexer.chain.txIndexTailLock.Lock()
+ indexer.run(rawdb.ReadTxIndexTail(indexer.db), head, stop, done)
+ indexer.chain.txIndexTailLock.Unlock()
+ indexer.chain.wg.Done()
+}
diff --git a/core/txindexer_test.go b/core/txindexer_test.go
new file mode 100644
index 0000000000..39d71d49ce
--- /dev/null
+++ b/core/txindexer_test.go
@@ -0,0 +1,258 @@
+// Copyright 2024 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
+
+package core
+
+import (
+ "fmt"
+ "math/big"
+ "testing"
+
+ "github.com/ava-labs/subnet-evm/consensus/dummy"
+ "github.com/ava-labs/subnet-evm/core/rawdb"
+ "github.com/ava-labs/subnet-evm/core/types"
+ "github.com/ava-labs/subnet-evm/params"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/stretchr/testify/require"
+)
+
+// Should we try to use the TestTxIndexer from upstream here instead
+// or move this test to a new file eg, blockchain_extra_test.go?
+func TestTransactionIndices(t *testing.T) {
+ // Configure and generate a sample block chain
+ require := require.New(t)
+ var (
+ key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
+ addr1 = crypto.PubkeyToAddress(key1.PublicKey)
+ addr2 = crypto.PubkeyToAddress(key2.PublicKey)
+ funds = big.NewInt(10000000000000)
+ gspec = &Genesis{
+ Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
+ Alloc: GenesisAlloc{addr1: {Balance: funds}},
+ }
+ signer = types.LatestSigner(gspec.Config)
+ )
+ genDb, blocks, _, err := GenerateChainWithGenesis(gspec, dummy.NewFaker(), 128, 10, func(i int, block *BlockGen) {
+ tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
+ require.NoError(err)
+ block.AddTx(tx)
+ })
+ require.NoError(err)
+
+ blocks2, _, err := GenerateChain(gspec.Config, blocks[len(blocks)-1], dummy.NewFaker(), genDb, 10, 10, func(i int, block *BlockGen) {
+ tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
+ require.NoError(err)
+ block.AddTx(tx)
+ })
+ require.NoError(err)
+
+ conf := &CacheConfig{
+ TrieCleanLimit: 256,
+ TrieDirtyLimit: 256,
+ TrieDirtyCommitTarget: 20,
+ TriePrefetcherParallelism: 4,
+ Pruning: true,
+ CommitInterval: 4096,
+ SnapshotLimit: 256,
+ SnapshotNoBuild: true, // Ensure the test errors if snapshot initialization fails
+ AcceptorQueueLimit: 64,
+ }
+
+ // Init block chain and check all needed indices has been indexed.
+ chainDB := rawdb.NewMemoryDatabase()
+ chain, err := createBlockChain(chainDB, conf, gspec, common.Hash{})
+ require.NoError(err)
+
+ _, err = chain.InsertChain(blocks)
+ require.NoError(err)
+
+ for _, block := range blocks {
+ err := chain.Accept(block)
+ require.NoError(err)
+ }
+ chain.DrainAcceptorQueue()
+
+ lastAcceptedBlock := blocks[len(blocks)-1]
+ require.Equal(lastAcceptedBlock.Hash(), chain.CurrentHeader().Hash())
+
+ CheckTxIndices(t, nil, lastAcceptedBlock.NumberU64(), chain.db, false) // check all indices has been indexed
+ chain.Stop()
+
+ // Reconstruct a block chain which only reserves limited tx indices
+ // 128 blocks were previously indexed. Now we add a new block at each test step.
+ limits := []uint64{
+ 0, /* tip: 129 reserve all (don't run) */
+ 131, /* tip: 130 reserve all */
+ 140, /* tip: 131 reserve all */
+ 64, /* tip: 132, limit:64 */
+ 32, /* tip: 133, limit:32 */
+ }
+ for i, l := range limits {
+ t.Run(fmt.Sprintf("test-%d, limit: %d", i+1, l), func(t *testing.T) {
+ conf.TransactionHistory = l
+
+ chain, err := createBlockChain(chainDB, conf, gspec, lastAcceptedBlock.Hash())
+ require.NoError(err)
+
+ tail := getTail(l, lastAcceptedBlock.NumberU64())
+ // check if startup indices are correct
+ CheckTxIndices(t, tail, lastAcceptedBlock.NumberU64(), chain.db, false)
+
+ newBlks := blocks2[i : i+1]
+ _, err = chain.InsertChain(newBlks) // Feed chain a higher block to trigger indices updater.
+ require.NoError(err)
+
+ lastAcceptedBlock = newBlks[0]
+ err = chain.Accept(lastAcceptedBlock) // Accept the block to trigger indices updater.
+ require.NoError(err)
+ chain.DrainAcceptorQueue()
+
+ tail = getTail(l, lastAcceptedBlock.NumberU64())
+ // check if indices are updated correctly
+ CheckTxIndices(t, tail, lastAcceptedBlock.NumberU64(), chain.db, false)
+ chain.Stop()
+ })
+ }
+}
+
+func getTail(limit uint64, lastAccepted uint64) *uint64 {
+ if limit == 0 {
+ return nil
+ }
+ var tail uint64
+ if lastAccepted > limit {
+ // tail should be the oldest block number which is indexed
+ // i.e the first block number that's in the lookup range
+ tail = lastAccepted - limit + 1
+ }
+ return &tail
+}
+
+func TestTransactionSkipIndexing(t *testing.T) {
+ // Configure and generate a sample block chain
+ require := require.New(t)
+ var (
+ key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
+ addr1 = crypto.PubkeyToAddress(key1.PublicKey)
+ addr2 = crypto.PubkeyToAddress(key2.PublicKey)
+ funds = big.NewInt(10000000000000)
+ gspec = &Genesis{
+ Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
+ Alloc: GenesisAlloc{addr1: {Balance: funds}},
+ }
+ signer = types.LatestSigner(gspec.Config)
+ )
+ genDb, blocks, _, err := GenerateChainWithGenesis(gspec, dummy.NewCoinbaseFaker(), 5, 10, func(i int, block *BlockGen) {
+ tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
+ require.NoError(err)
+ block.AddTx(tx)
+ })
+ require.NoError(err)
+
+ blocks2, _, err := GenerateChain(gspec.Config, blocks[len(blocks)-1], dummy.NewCoinbaseFaker(), genDb, 5, 10, func(i int, block *BlockGen) {
+ tx, err := types.SignTx(types.NewTransaction(block.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
+ require.NoError(err)
+ block.AddTx(tx)
+ })
+ require.NoError(err)
+
+ conf := &CacheConfig{
+ TrieCleanLimit: 256,
+ TrieDirtyLimit: 256,
+ TrieDirtyCommitTarget: 20,
+ TriePrefetcherParallelism: 4,
+ Pruning: true,
+ CommitInterval: 4096,
+ SnapshotLimit: 256,
+ SnapshotNoBuild: true, // Ensure the test errors if snapshot initialization fails
+ AcceptorQueueLimit: 64,
+ SkipTxIndexing: true,
+ }
+
+ // test1: Init block chain and check all indices has been skipped.
+ chainDB := rawdb.NewMemoryDatabase()
+ chain, err := createAndInsertChain(chainDB, conf, gspec, blocks, common.Hash{},
+ func(b *types.Block) {
+ bNumber := b.NumberU64()
+ checkTxIndicesHelper(t, nil, bNumber+1, bNumber+1, bNumber, chainDB, false) // check all indices has been skipped
+ })
+ require.NoError(err)
+ chain.Stop()
+
+ // test2: specify lookuplimit with tx index skipping enabled. Blocks should not be indexed but tail should be updated.
+ conf.TransactionHistory = 2
+ chainDB = rawdb.NewMemoryDatabase()
+ chain, err = createAndInsertChain(chainDB, conf, gspec, blocks, common.Hash{},
+ func(b *types.Block) {
+ bNumber := b.NumberU64()
+ tail := bNumber - conf.TransactionHistory + 1
+ checkTxIndicesHelper(t, &tail, bNumber+1, bNumber+1, bNumber, chainDB, false) // check all indices has been skipped
+ })
+ require.NoError(err)
+ chain.Stop()
+
+ // test3: tx index skipping and unindexer disabled. Blocks should be indexed and tail should be updated.
+ conf.TransactionHistory = 0
+ conf.SkipTxIndexing = false
+ chainDB = rawdb.NewMemoryDatabase()
+ chain, err = createAndInsertChain(chainDB, conf, gspec, blocks, common.Hash{},
+ func(b *types.Block) {
+ bNumber := b.NumberU64()
+ checkTxIndicesHelper(t, nil, 0, bNumber, bNumber, chainDB, false) // check all indices has been indexed
+ })
+ require.NoError(err)
+ chain.Stop()
+
+ // now change tx index skipping to true and check that the indices are skipped for the last block
+ // and old indices are removed up to the tail, but [tail, current) indices are still there.
+ conf.TransactionHistory = 2
+ conf.SkipTxIndexing = true
+ chain, err = createAndInsertChain(chainDB, conf, gspec, blocks2[0:1], chain.CurrentHeader().Hash(),
+ func(b *types.Block) {
+ bNumber := b.NumberU64()
+ tail := bNumber - conf.TransactionHistory + 1
+ checkTxIndicesHelper(t, &tail, tail, bNumber-1, bNumber, chainDB, false)
+ })
+ require.NoError(err)
+ chain.Stop()
+}
+
+func createAndInsertChain(db ethdb.Database, cacheConfig *CacheConfig, gspec *Genesis, blocks types.Blocks, lastAcceptedHash common.Hash, accepted func(*types.Block)) (*BlockChain, error) {
+ chain, err := createBlockChain(db, cacheConfig, gspec, lastAcceptedHash)
+ if err != nil {
+ return nil, err
+ }
+ _, err = chain.InsertChain(blocks)
+ if err != nil {
+ return nil, err
+ }
+ for _, block := range blocks {
+ err := chain.Accept(block)
+ if err != nil {
+ return nil, err
+ }
+ chain.DrainAcceptorQueue()
+ if accepted != nil {
+ accepted(block)
+ }
+ }
+
+ return chain, nil
+}
diff --git a/core/txpool/blobpool/blobpool.go b/core/txpool/blobpool/blobpool.go
index 8de9a0765b..470a2b4e39 100644
--- a/core/txpool/blobpool/blobpool.go
+++ b/core/txpool/blobpool/blobpool.go
@@ -278,7 +278,7 @@ func newBlobTxMeta(id uint64, size uint32, tx *types.Transaction) *blobTxMeta {
// going up, crossing the smaller positive jump counter). As such, the pool
// cares only about the min of the two delta values for eviction priority.
//
-// priority = min(delta-basefee, delta-blobfee)
+// priority = min(deltaBasefee, deltaBlobfee)
//
// - The above very aggressive dimensionality and noise reduction should result
// in transaction being grouped into a small number of buckets, the further
@@ -290,7 +290,7 @@ func newBlobTxMeta(id uint64, size uint32, tx *types.Transaction) *blobTxMeta {
// with high fee caps since it could enable pool wars. As such, any positive
// priority will be grouped together.
//
-// priority = min(delta-basefee, delta-blobfee, 0)
+// priority = min(deltaBasefee, deltaBlobfee, 0)
//
// Optimisation tradeoffs:
//
@@ -352,7 +352,7 @@ func (p *BlobPool) Filter(tx *types.Transaction) bool {
// Init sets the gas price needed to keep a transaction in the pool and the chain
// head to allow balance / nonce checks. The transaction journal will be loaded
// from disk and filtered based on the provided starting settings.
-func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.AddressReserver) error {
+func (p *BlobPool) Init(gasTip uint64, head *types.Header, reserve txpool.AddressReserver) error {
p.reserve = reserve
var (
@@ -370,7 +370,7 @@ func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.Addr
}
}
// Initialize the state with head block, or fallback to empty one in
- // case the head state is not available(might occur when node is not
+ // case the head state is not available (might occur when node is not
// fully synced).
state, err := p.chain.StateAt(head.Root)
if err != nil {
@@ -381,14 +381,14 @@ func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.Addr
}
p.head, p.state = head, state
- // Index all transactions on disk and delete anything inprocessable
+ // Index all transactions on disk and delete anything unprocessable
var fails []uint64
index := func(id uint64, size uint32, blob []byte) {
if p.parseTransaction(id, size, blob) != nil {
fails = append(fails, id)
}
}
- store, err := billy.Open(billy.Options{Path: queuedir}, newSlotter(), index)
+ store, err := billy.Open(billy.Options{Path: queuedir, Repair: true}, newSlotter(), index)
if err != nil {
return err
}
@@ -396,6 +396,8 @@ func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.Addr
if len(fails) > 0 {
log.Warn("Dropping invalidated blob transactions", "ids", fails)
+ dropInvalidMeter.Mark(int64(len(fails)))
+
for _, id := range fails {
if err := p.store.Delete(id); err != nil {
p.Close()
@@ -426,7 +428,7 @@ func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.Addr
var (
// basefee = uint256.MustFromBig(eip1559.CalcBaseFee(p.chain.Config(), p.head))
basefee = uint256.MustFromBig(baseFee)
- blobfee = uint256.MustFromBig(big.NewInt(params.BlobTxMinBlobGasprice))
+ blobfee = uint256.NewInt(params.BlobTxMinBlobGasprice)
)
if p.head.ExcessBlobGas != nil {
blobfee = uint256.MustFromBig(eip4844.CalcBlobFee(*p.head.ExcessBlobGas))
@@ -444,7 +446,7 @@ func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.Addr
basefeeGauge.Update(int64(basefee.Uint64()))
blobfeeGauge.Update(int64(blobfee.Uint64()))
- p.SetGasTip(gasTip)
+ p.SetGasTip(new(big.Int).SetUint64(gasTip))
// Since the user might have modified their pool's capacity, evict anything
// above the current allowance
@@ -460,7 +462,7 @@ func (p *BlobPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.Addr
// Close closes down the underlying persistent store.
func (p *BlobPool) Close() error {
var errs []error
- if p.limbo != nil {
+ if p.limbo != nil { // Close might be invoked due to error in constructor, before p,limbo is set
if err := p.limbo.Close(); err != nil {
errs = append(errs, err)
}
@@ -484,7 +486,7 @@ func (p *BlobPool) parseTransaction(id uint64, size uint32, blob []byte) error {
tx := new(types.Transaction)
if err := rlp.DecodeBytes(blob, tx); err != nil {
// This path is impossible unless the disk data representation changes
- // across restarts. For that ever unprobable case, recover gracefully
+ // across restarts. For that ever improbable case, recover gracefully
// by ignoring this data entry.
log.Error("Failed to decode blob pool entry", "id", id, "err", err)
return err
@@ -495,11 +497,17 @@ func (p *BlobPool) parseTransaction(id uint64, size uint32, blob []byte) error {
}
meta := newBlobTxMeta(id, size, tx)
-
+ if _, exists := p.lookup[meta.hash]; exists {
+ // This path is only possible after a crash, where deleted items are not
+ // removed via the normal shutdown-startup procedure and thus may get
+ // partially resurrected.
+ log.Error("Rejecting duplicate blob pool entry", "id", id, "hash", tx.Hash())
+ return errors.New("duplicate blob entry")
+ }
sender, err := p.signer.Sender(tx)
if err != nil {
// This path is impossible unless the signature validity changes across
- // restarts. For that ever unprobable case, recover gracefully by ignoring
+ // restarts. For that ever improbable case, recover gracefully by ignoring
// this data entry.
log.Error("Failed to recover blob tx sender", "id", id, "hash", tx.Hash(), "err", err)
return err
@@ -558,15 +566,17 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6
}
delete(p.index, addr)
delete(p.spent, addr)
- if inclusions != nil { // only during reorgs will the heap will be initialized
+ if inclusions != nil { // only during reorgs will the heap be initialized
heap.Remove(p.evict, p.evict.index[addr])
}
p.reserve(addr, false)
if gapped {
log.Warn("Dropping dangling blob transactions", "from", addr, "missing", next, "drop", nonces, "ids", ids)
+ dropDanglingMeter.Mark(int64(len(ids)))
} else {
log.Trace("Dropping filled blob transactions", "from", addr, "filled", nonces, "ids", ids)
+ dropFilledMeter.Mark(int64(len(ids)))
}
for _, id := range ids {
if err := p.store.Delete(id); err != nil {
@@ -597,6 +607,8 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6
txs = txs[1:]
}
log.Trace("Dropping overlapped blob transactions", "from", addr, "overlapped", nonces, "ids", ids, "left", len(txs))
+ dropOverlappedMeter.Mark(int64(len(ids)))
+
for _, id := range ids {
if err := p.store.Delete(id); err != nil {
log.Error("Failed to delete blob transaction", "from", addr, "id", id, "err", err)
@@ -611,7 +623,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6
txs[0].evictionBlobFeeJumps = txs[0].blobfeeJumps
for i := 1; i < len(txs); i++ {
- // If there's no nonce gap, initialize the evicion thresholds as the
+ // If there's no nonce gap, initialize the eviction thresholds as the
// minimum between the cumulative thresholds and the current tx fees
if txs[i].nonce == txs[i-1].nonce+1 {
txs[i].evictionExecTip = txs[i-1].evictionExecTip
@@ -628,10 +640,30 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6
}
continue
}
- // Sanity check that there's no double nonce. This case would be a coding
- // error, but better know about it
+ // Sanity check that there's no double nonce. This case would generally
+ // be a coding error, so better know about it.
+ //
+ // Also, Billy behind the blobpool does not journal deletes. A process
+ // crash would result in previously deleted entities being resurrected.
+ // That could potentially cause a duplicate nonce to appear.
if txs[i].nonce == txs[i-1].nonce {
- log.Error("Duplicate nonce blob transaction", "from", addr, "nonce", txs[i].nonce)
+ id := p.lookup[txs[i].hash]
+
+ log.Error("Dropping repeat nonce blob transaction", "from", addr, "nonce", txs[i].nonce, "id", id)
+ dropRepeatedMeter.Mark(1)
+
+ p.spent[addr] = new(uint256.Int).Sub(p.spent[addr], txs[i].costCap)
+ p.stored -= uint64(txs[i].size)
+ delete(p.lookup, txs[i].hash)
+
+ if err := p.store.Delete(id); err != nil {
+ log.Error("Failed to delete blob transaction", "from", addr, "id", id, "err", err)
+ }
+ txs = append(txs[:i], txs[i+1:]...)
+ p.index[addr] = txs
+
+ i--
+ continue
}
// Otherwise if there's a nonce gap evict all later transactions
var (
@@ -649,6 +681,8 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6
txs = txs[:i]
log.Error("Dropping gapped blob transactions", "from", addr, "missing", txs[i-1].nonce+1, "drop", nonces, "ids", ids)
+ dropGappedMeter.Mark(int64(len(ids)))
+
for _, id := range ids {
if err := p.store.Delete(id); err != nil {
log.Error("Failed to delete blob transaction", "from", addr, "id", id, "err", err)
@@ -660,7 +694,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6
// Ensure that there's no over-draft, this is expected to happen when some
// transactions get included without publishing on the network
var (
- balance = uint256.MustFromBig(p.state.GetBalance(addr))
+ balance = p.state.GetBalance(addr)
spent = p.spent[addr]
)
if spent.Cmp(balance) > 0 {
@@ -685,7 +719,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6
if len(txs) == 0 {
delete(p.index, addr)
delete(p.spent, addr)
- if inclusions != nil { // only during reorgs will the heap will be initialized
+ if inclusions != nil { // only during reorgs will the heap be initialized
heap.Remove(p.evict, p.evict.index[addr])
}
p.reserve(addr, false)
@@ -693,6 +727,8 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6
p.index[addr] = txs
}
log.Warn("Dropping overdrafted blob transactions", "from", addr, "balance", balance, "spent", spent, "drop", nonces, "ids", ids)
+ dropOverdraftedMeter.Mark(int64(len(ids)))
+
for _, id := range ids {
if err := p.store.Delete(id); err != nil {
log.Error("Failed to delete blob transaction", "from", addr, "id", id, "err", err)
@@ -723,6 +759,8 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6
p.index[addr] = txs
log.Warn("Dropping overcapped blob transactions", "from", addr, "kept", len(txs), "drop", nonces, "ids", ids)
+ dropOvercappedMeter.Mark(int64(len(ids)))
+
for _, id := range ids {
if err := p.store.Delete(id); err != nil {
log.Error("Failed to delete blob transaction", "from", addr, "id", id, "err", err)
@@ -739,7 +777,7 @@ func (p *BlobPool) recheck(addr common.Address, inclusions map[common.Hash]uint6
// offload removes a tracked blob transaction from the pool and moves it into the
// limbo for tracking until finality.
//
-// The method may log errors for various unexpcted scenarios but will not return
+// The method may log errors for various unexpected scenarios but will not return
// any of it since there's no clear error case. Some errors may be due to coding
// issues, others caused by signers mining MEV stuff or swapping transactions. In
// all cases, the pool needs to continue operating.
@@ -797,7 +835,7 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) {
}
}
// Recheck the account's pooled transactions to drop included and
- // invalidated one
+ // invalidated ones
p.recheck(addr, inclusions)
}
if len(adds) > 0 {
@@ -813,7 +851,7 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) {
log.Error("Failed to get fee config to reset blobpool fees", "err", err)
return
}
- _, baseFee, err := dummy.EstimateNextBaseFee(
+ _, baseFeeBig, err := dummy.EstimateNextBaseFee(
p.chain.Config(),
feeConfig,
p.head,
@@ -826,7 +864,7 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) {
// Reset the price heap for the new set of basefee/blobfee pairs
var (
// basefee = uint256.MustFromBig(eip1559.CalcBaseFee(p.chain.Config(), newHead))
- basefee = uint256.MustFromBig(baseFee)
+ basefee = uint256.MustFromBig(baseFeeBig)
blobfee = uint256.MustFromBig(big.NewInt(params.BlobTxMinBlobGasprice))
)
if newHead.ExcessBlobGas != nil {
@@ -996,7 +1034,7 @@ func (p *BlobPool) reinject(addr common.Address, txhash common.Hash) error {
return err
}
- // Update the indixes and metrics
+ // Update the indices and metrics
meta := newBlobTxMeta(id, p.store.Size(id), tx)
if _, ok := p.index[addr]; !ok {
if err := p.reserve(addr, true); err != nil {
@@ -1063,6 +1101,8 @@ func (p *BlobPool) SetGasTip(tip *big.Int) {
}
// Clear out the transactions from the data store
log.Warn("Dropping underpriced blob transaction", "from", addr, "rejected", tx.nonce, "tip", tx.execTipCap, "want", tip, "drop", nonces, "ids", ids)
+ dropUnderpricedMeter.Mark(int64(len(ids)))
+
for _, id := range ids {
if err := p.store.Delete(id); err != nil {
log.Error("Failed to delete dropped transaction", "id", id, "err", err)
@@ -1210,7 +1250,7 @@ func (p *BlobPool) Get(hash common.Hash) *types.Transaction {
}
// Add inserts a set of blob transactions into the pool if they pass validation (both
-// consensus validity and pool restictions).
+// consensus validity and pool restrictions).
func (p *BlobPool) Add(txs []*types.Transaction, local bool, sync bool) []error {
var (
adds = make([]*types.Transaction, 0, len(txs))
@@ -1230,10 +1270,10 @@ func (p *BlobPool) Add(txs []*types.Transaction, local bool, sync bool) []error
}
// Add inserts a new blob transaction into the pool if it passes validation (both
-// consensus validity and pool restictions).
+// consensus validity and pool restrictions).
func (p *BlobPool) add(tx *types.Transaction) (err error) {
// The blob pool blocks on adding a transaction. This is because blob txs are
- // only even pulled form the network, so this method will act as the overload
+ // only even pulled from the network, so this method will act as the overload
// protection for fetches.
waitStart := time.Now()
p.lock.Lock()
@@ -1247,6 +1287,22 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) {
// Ensure the transaction is valid from all perspectives
if err := p.validateTx(tx); err != nil {
log.Trace("Transaction validation failed", "hash", tx.Hash(), "err", err)
+ switch {
+ case errors.Is(err, txpool.ErrUnderpriced):
+ addUnderpricedMeter.Mark(1)
+ case errors.Is(err, core.ErrNonceTooLow):
+ addStaleMeter.Mark(1)
+ case errors.Is(err, core.ErrNonceTooHigh):
+ addGappedMeter.Mark(1)
+ case errors.Is(err, core.ErrInsufficientFunds):
+ addOverdraftedMeter.Mark(1)
+ case errors.Is(err, txpool.ErrAccountLimitExceeded):
+ addOvercappedMeter.Mark(1)
+ case errors.Is(err, txpool.ErrReplaceUnderpriced):
+ addNoreplaceMeter.Mark(1)
+ default:
+ addInvalidMeter.Mark(1)
+ }
return err
}
// If the address is not yet known, request exclusivity to track the account
@@ -1254,6 +1310,7 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) {
from, _ := types.Sender(p.signer, tx) // already validated above
if _, ok := p.index[from]; !ok {
if err := p.reserve(from, true); err != nil {
+ addNonExclusiveMeter.Mark(1)
return err
}
defer func() {
@@ -1293,6 +1350,8 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) {
}
if len(p.index[from]) > offset {
// Transaction replaces a previously queued one
+ dropReplacedMeter.Mark(1)
+
prev := p.index[from][offset]
if err := p.store.Delete(prev.id); err != nil {
// Shitty situation, but try to recover gracefully instead of going boom
@@ -1371,6 +1430,7 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) {
}
p.updateStorageMetrics()
+ addValidMeter.Mark(1)
return nil
}
@@ -1404,7 +1464,7 @@ func (p *BlobPool) drop() {
p.stored -= uint64(drop.size)
delete(p.lookup, drop.hash)
- // Remove the transaction from the pool's evicion heap:
+ // Remove the transaction from the pool's eviction heap:
// - If the entire account was dropped, pop off the address
// - Otherwise, if the new tail has better eviction caps, fix the heap
if last {
@@ -1420,7 +1480,9 @@ func (p *BlobPool) drop() {
}
}
// Remove the transaction from the data store
- log.Warn("Evicting overflown blob transaction", "from", from, "evicted", drop.nonce, "id", drop.id)
+ log.Debug("Evicting overflown blob transaction", "from", from, "evicted", drop.nonce, "id", drop.id)
+ dropOverflownMeter.Mark(1)
+
if err := p.store.Delete(drop.id); err != nil {
log.Error("Failed to drop evicted transaction", "id", drop.id, "err", err)
}
@@ -1428,7 +1490,15 @@ func (p *BlobPool) drop() {
// Pending retrieves all currently processable transactions, grouped by origin
// account and sorted by nonce.
-func (p *BlobPool) Pending(enforceTips bool) map[common.Address][]*txpool.LazyTransaction {
+//
+// The transactions can also be pre-filtered by the dynamic fee components to
+// reduce allocations and load on downstream subsystems.
+func (p *BlobPool) Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction {
+ // If only plain transactions are requested, this pool is unsuitable as it
+ // contains none, don't even bother.
+ if filter.OnlyPlainTxs {
+ return nil
+ }
// Track the amount of time waiting to retrieve the list of pending blob txs
// from the pool and the amount of time actually spent on assembling the data.
// The latter will be pretty much moot, but we've kept it to have symmetric
@@ -1438,20 +1508,40 @@ func (p *BlobPool) Pending(enforceTips bool) map[common.Address][]*txpool.LazyTr
pendwaitHist.Update(time.Since(pendStart).Nanoseconds())
defer p.lock.RUnlock()
- defer func(start time.Time) {
- pendtimeHist.Update(time.Since(start).Nanoseconds())
- }(time.Now())
+ execStart := time.Now()
+ defer func() {
+ pendtimeHist.Update(time.Since(execStart).Nanoseconds())
+ }()
- pending := make(map[common.Address][]*txpool.LazyTransaction)
+ pending := make(map[common.Address][]*txpool.LazyTransaction, len(p.index))
for addr, txs := range p.index {
- var lazies []*txpool.LazyTransaction
+ lazies := make([]*txpool.LazyTransaction, 0, len(txs))
for _, tx := range txs {
+ // If transaction filtering was requested, discard badly priced ones
+ if filter.MinTip != nil && filter.BaseFee != nil {
+ if tx.execFeeCap.Lt(filter.BaseFee) {
+ break // basefee too low, cannot be included, discard rest of txs from the account
+ }
+ tip := new(uint256.Int).Sub(tx.execFeeCap, filter.BaseFee)
+ if tip.Gt(tx.execTipCap) {
+ tip = tx.execTipCap
+ }
+ if tip.Lt(filter.MinTip) {
+ break // allowed or remaining tip too low, cannot be included, discard rest of txs from the account
+ }
+ }
+ if filter.BlobFee != nil {
+ if tx.blobFeeCap.Lt(filter.BlobFee) {
+ break // blobfee too low, cannot be included, discard rest of txs from the account
+ }
+ }
+ // Transaction was accepted according to the filter, append to the pending list
lazies = append(lazies, &txpool.LazyTransaction{
Pool: p,
Hash: tx.hash,
- Time: time.Now(), // TODO(karalabe): Maybe save these and use that?
- GasFeeCap: tx.execFeeCap.ToBig(),
- GasTipCap: tx.execTipCap.ToBig(),
+ Time: execStart, // TODO(karalabe): Maybe save these and use that?
+ GasFeeCap: tx.execFeeCap,
+ GasTipCap: tx.execTipCap,
Gas: tx.execGas,
BlobGas: tx.blobGas,
})
@@ -1463,10 +1553,6 @@ func (p *BlobPool) Pending(enforceTips bool) map[common.Address][]*txpool.LazyTr
return pending
}
-func (p *BlobPool) PendingWithBaseFee(enforceTips bool, baseFee *big.Int) map[common.Address][]*txpool.LazyTransaction {
- return p.Pending(enforceTips)
-}
-
// IteratePending iterates over [pool.pending] until [f] returns false.
// The caller must not modify [tx]. Returns false if iteration was interrupted.
func (pool *BlobPool) IteratePending(f func(tx *types.Transaction) bool) bool {
@@ -1537,7 +1623,7 @@ func (p *BlobPool) updateStorageMetrics() {
}
// updateLimboMetrics retrieves a bunch of stats from the limbo store and pushes
-// // them out as metrics.
+// them out as metrics.
func (p *BlobPool) updateLimboMetrics() {
stats := p.limbo.store.Infos()
diff --git a/core/txpool/blobpool/blobpool_test.go b/core/txpool/blobpool/blobpool_test.go
index 95b3e48c68..27bf9f0eab 100644
--- a/core/txpool/blobpool/blobpool_test.go
+++ b/core/txpool/blobpool/blobpool_test.go
@@ -62,21 +62,9 @@ var (
emptyBlob = kzg4844.Blob{}
emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob)
emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit)
- emptyBlobVHash = blobHash(emptyBlobCommit)
+ emptyBlobVHash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit)
)
-func blobHash(commit kzg4844.Commitment) common.Hash {
- hasher := sha256.New()
- hasher.Write(commit[:])
- hash := hasher.Sum(nil)
-
- var vhash common.Hash
- vhash[0] = params.BlobTxHashVersion
- copy(vhash[1:], hash[1:])
-
- return vhash
-}
-
// Chain configuration with Cancun enabled.
//
// TODO(karalabe): replace with params.MainnetChainConfig after Cancun.
@@ -222,7 +210,7 @@ func makeTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64,
return types.MustSignNewTx(key, types.LatestSigner(testChainConfig), blobtx)
}
-// makeUnsignedTx is a utility method to construct a random blob tranasaction
+// makeUnsignedTx is a utility method to construct a random blob transaction
// without signing it.
func makeUnsignedTx(nonce uint64, gasTipCap uint64, gasFeeCap uint64, blobFeeCap uint64) *types.BlobTx {
return &types.BlobTx{
@@ -342,7 +330,16 @@ func verifyPoolInternals(t *testing.T, pool *BlobPool) {
// - 1. A transaction that cannot be decoded must be dropped
// - 2. A transaction that cannot be recovered (bad signature) must be dropped
// - 3. All transactions after a nonce gap must be dropped
-// - 4. All transactions after an underpriced one (including it) must be dropped
+// - 4. All transactions after an already included nonce must be dropped
+// - 5. All transactions after an underpriced one (including it) must be dropped
+// - 6. All transactions after an overdrafting sequence must be dropped
+// - 7. All transactions exceeding the per-account limit must be dropped
+//
+// Furthermore, some strange corner-cases can also occur after a crash, as Billy's
+// simplicity also allows it to resurrect past deleted entities:
+//
+// - 8. Fully duplicate transactions (matching hash) must be dropped
+// - 9. Duplicate nonces from the same account must be dropped
func TestOpenDrops(t *testing.T) {
log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true)))
@@ -375,7 +372,7 @@ func TestOpenDrops(t *testing.T) {
badsig, _ := store.Put(blob)
// Insert a sequence of transactions with a nonce gap in between to verify
- // that anything gapped will get evicted (case 3)
+ // that anything gapped will get evicted (case 3).
var (
gapper, _ = crypto.GenerateKey()
@@ -394,7 +391,7 @@ func TestOpenDrops(t *testing.T) {
}
}
// Insert a sequence of transactions with a gapped starting nonce to verify
- // that the entire set will get dropped.
+ // that the entire set will get dropped (case 3).
var (
dangler, _ = crypto.GenerateKey()
dangling = make(map[uint64]struct{})
@@ -407,7 +404,7 @@ func TestOpenDrops(t *testing.T) {
dangling[id] = struct{}{}
}
// Insert a sequence of transactions with already passed nonces to veirfy
- // that the entire set will get dropped.
+ // that the entire set will get dropped (case 4).
var (
filler, _ = crypto.GenerateKey()
filled = make(map[uint64]struct{})
@@ -419,8 +416,8 @@ func TestOpenDrops(t *testing.T) {
id, _ := store.Put(blob)
filled[id] = struct{}{}
}
- // Insert a sequence of transactions with partially passed nonces to veirfy
- // that the included part of the set will get dropped
+ // Insert a sequence of transactions with partially passed nonces to verify
+ // that the included part of the set will get dropped (case 4).
var (
overlapper, _ = crypto.GenerateKey()
overlapped = make(map[uint64]struct{})
@@ -437,7 +434,7 @@ func TestOpenDrops(t *testing.T) {
}
}
// Insert a sequence of transactions with an underpriced first to verify that
- // the entire set will get dropped (case 4).
+ // the entire set will get dropped (case 5).
var (
underpayer, _ = crypto.GenerateKey()
underpaid = make(map[uint64]struct{})
@@ -456,7 +453,7 @@ func TestOpenDrops(t *testing.T) {
}
// Insert a sequence of transactions with an underpriced in between to verify
- // that it and anything newly gapped will get evicted (case 4).
+ // that it and anything newly gapped will get evicted (case 5).
var (
outpricer, _ = crypto.GenerateKey()
outpriced = make(map[uint64]struct{})
@@ -478,7 +475,7 @@ func TestOpenDrops(t *testing.T) {
}
}
// Insert a sequence of transactions fully overdrafted to verify that the
- // entire set will get invalidated.
+ // entire set will get invalidated (case 6).
var (
exceeder, _ = crypto.GenerateKey()
exceeded = make(map[uint64]struct{})
@@ -496,7 +493,7 @@ func TestOpenDrops(t *testing.T) {
exceeded[id] = struct{}{}
}
// Insert a sequence of transactions partially overdrafted to verify that part
- // of the set will get invalidated.
+ // of the set will get invalidated (case 6).
var (
overdrafter, _ = crypto.GenerateKey()
overdrafted = make(map[uint64]struct{})
@@ -518,7 +515,7 @@ func TestOpenDrops(t *testing.T) {
}
}
// Insert a sequence of transactions overflowing the account cap to verify
- // that part of the set will get invalidated.
+ // that part of the set will get invalidated (case 7).
var (
overcapper, _ = crypto.GenerateKey()
overcapped = make(map[uint64]struct{})
@@ -533,21 +530,59 @@ func TestOpenDrops(t *testing.T) {
overcapped[id] = struct{}{}
}
}
+ // Insert a batch of duplicated transactions to verify that only one of each
+ // version will remain (case 8).
+ var (
+ duplicater, _ = crypto.GenerateKey()
+ duplicated = make(map[uint64]struct{})
+ )
+ for _, nonce := range []uint64{0, 1, 2} {
+ blob, _ := rlp.EncodeToBytes(makeTx(nonce, 1, 1, 1, duplicater))
+
+ for i := 0; i < int(nonce)+1; i++ {
+ id, _ := store.Put(blob)
+ if i == 0 {
+ valids[id] = struct{}{}
+ } else {
+ duplicated[id] = struct{}{}
+ }
+ }
+ }
+ // Insert a batch of duplicated nonces to verify that only one of each will
+ // remain (case 9).
+ var (
+ repeater, _ = crypto.GenerateKey()
+ repeated = make(map[uint64]struct{})
+ )
+ for _, nonce := range []uint64{0, 1, 2} {
+ for i := 0; i < int(nonce)+1; i++ {
+ blob, _ := rlp.EncodeToBytes(makeTx(nonce, 1, uint64(i)+1 /* unique hashes */, 1, repeater))
+
+ id, _ := store.Put(blob)
+ if i == 0 {
+ valids[id] = struct{}{}
+ } else {
+ repeated[id] = struct{}{}
+ }
+ }
+ }
store.Close()
// Create a blob pool out of the pre-seeded data
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
- statedb.AddBalance(crypto.PubkeyToAddress(gapper.PublicKey), big.NewInt(1000000))
- statedb.AddBalance(crypto.PubkeyToAddress(dangler.PublicKey), big.NewInt(1000000))
- statedb.AddBalance(crypto.PubkeyToAddress(filler.PublicKey), big.NewInt(1000000))
+ statedb.AddBalance(crypto.PubkeyToAddress(gapper.PublicKey), uint256.NewInt(1000000))
+ statedb.AddBalance(crypto.PubkeyToAddress(dangler.PublicKey), uint256.NewInt(1000000))
+ statedb.AddBalance(crypto.PubkeyToAddress(filler.PublicKey), uint256.NewInt(1000000))
statedb.SetNonce(crypto.PubkeyToAddress(filler.PublicKey), 3)
- statedb.AddBalance(crypto.PubkeyToAddress(overlapper.PublicKey), big.NewInt(1000000))
+ statedb.AddBalance(crypto.PubkeyToAddress(overlapper.PublicKey), uint256.NewInt(1000000))
statedb.SetNonce(crypto.PubkeyToAddress(overlapper.PublicKey), 2)
- statedb.AddBalance(crypto.PubkeyToAddress(underpayer.PublicKey), big.NewInt(1000000))
- statedb.AddBalance(crypto.PubkeyToAddress(outpricer.PublicKey), big.NewInt(1000000))
- statedb.AddBalance(crypto.PubkeyToAddress(exceeder.PublicKey), big.NewInt(1000000))
- statedb.AddBalance(crypto.PubkeyToAddress(overdrafter.PublicKey), big.NewInt(1000000))
- statedb.AddBalance(crypto.PubkeyToAddress(overcapper.PublicKey), big.NewInt(10000000))
+ statedb.AddBalance(crypto.PubkeyToAddress(underpayer.PublicKey), uint256.NewInt(1000000))
+ statedb.AddBalance(crypto.PubkeyToAddress(outpricer.PublicKey), uint256.NewInt(1000000))
+ statedb.AddBalance(crypto.PubkeyToAddress(exceeder.PublicKey), uint256.NewInt(1000000))
+ statedb.AddBalance(crypto.PubkeyToAddress(overdrafter.PublicKey), uint256.NewInt(1000000))
+ statedb.AddBalance(crypto.PubkeyToAddress(overcapper.PublicKey), uint256.NewInt(10000000))
+ statedb.AddBalance(crypto.PubkeyToAddress(duplicater.PublicKey), uint256.NewInt(1000000))
+ statedb.AddBalance(crypto.PubkeyToAddress(repeater.PublicKey), uint256.NewInt(1000000))
statedb.Commit(0, true, false)
chain := &testBlockChain{
@@ -557,7 +592,7 @@ func TestOpenDrops(t *testing.T) {
statedb: statedb,
}
pool := New(Config{Datadir: storage}, chain)
- if err := pool.Init(big.NewInt(1), chain.CurrentBlock(), makeAddressReserver()); err != nil {
+ if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
t.Fatalf("failed to create blob pool: %v", err)
}
defer pool.Close()
@@ -591,6 +626,10 @@ func TestOpenDrops(t *testing.T) {
t.Errorf("partially overdrafted transaction remained in storage: %d", tx.id)
} else if _, ok := overcapped[tx.id]; ok {
t.Errorf("overcapped transaction remained in storage: %d", tx.id)
+ } else if _, ok := duplicated[tx.id]; ok {
+ t.Errorf("duplicated transaction remained in storage: %d", tx.id)
+ } else if _, ok := repeated[tx.id]; ok {
+ t.Errorf("repeated nonce transaction remained in storage: %d", tx.id)
} else {
alive[tx.id] = struct{}{}
}
@@ -621,7 +660,7 @@ func TestOpenDrops(t *testing.T) {
// Tests that transactions loaded from disk are indexed correctly.
//
-// - 1. Transactions must be groupped by sender, sorted by nonce
+// - 1. Transactions must be grouped by sender, sorted by nonce
// - 2. Eviction thresholds are calculated correctly for the sequences
// - 3. Balance usage of an account is totals across all transactions
func TestOpenIndex(t *testing.T) {
@@ -635,7 +674,7 @@ func TestOpenIndex(t *testing.T) {
store, _ := billy.Open(billy.Options{Path: filepath.Join(storage, pendingTransactionStore)}, newSlotter(), nil)
// Insert a sequence of transactions with varying price points to check that
- // the cumulative minimumw will be maintained.
+ // the cumulative minimum will be maintained.
var (
key, _ = crypto.GenerateKey()
addr = crypto.PubkeyToAddress(key.PublicKey)
@@ -662,7 +701,7 @@ func TestOpenIndex(t *testing.T) {
// Create a blob pool out of the pre-seeded data
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
- statedb.AddBalance(addr, big.NewInt(1_000_000_000))
+ statedb.AddBalance(addr, uint256.NewInt(1_000_000_000))
statedb.Commit(0, true, false)
chain := &testBlockChain{
@@ -672,7 +711,7 @@ func TestOpenIndex(t *testing.T) {
statedb: statedb,
}
pool := New(Config{Datadir: storage}, chain)
- if err := pool.Init(big.NewInt(1), chain.CurrentBlock(), makeAddressReserver()); err != nil {
+ if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
t.Fatalf("failed to create blob pool: %v", err)
}
defer pool.Close()
@@ -762,9 +801,9 @@ func TestOpenHeap(t *testing.T) {
// Create a blob pool out of the pre-seeded data
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
- statedb.AddBalance(addr1, big.NewInt(1_000_000_000))
- statedb.AddBalance(addr2, big.NewInt(1_000_000_000))
- statedb.AddBalance(addr3, big.NewInt(1_000_000_000))
+ statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000))
+ statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000))
+ statedb.AddBalance(addr3, uint256.NewInt(1_000_000_000))
statedb.Commit(0, true, false)
chain := &testBlockChain{
@@ -774,7 +813,7 @@ func TestOpenHeap(t *testing.T) {
statedb: statedb,
}
pool := New(Config{Datadir: storage}, chain)
- if err := pool.Init(big.NewInt(1), chain.CurrentBlock(), makeAddressReserver()); err != nil {
+ if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
t.Fatalf("failed to create blob pool: %v", err)
}
defer pool.Close()
@@ -842,9 +881,9 @@ func TestOpenCap(t *testing.T) {
for _, datacap := range []uint64{2 * (txAvgSize + blobSize), 100 * (txAvgSize + blobSize)} {
// Create a blob pool out of the pre-seeded data, but cap it to 2 blob transaction
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
- statedb.AddBalance(addr1, big.NewInt(1_000_000_000))
- statedb.AddBalance(addr2, big.NewInt(1_000_000_000))
- statedb.AddBalance(addr3, big.NewInt(1_000_000_000))
+ statedb.AddBalance(addr1, uint256.NewInt(1_000_000_000))
+ statedb.AddBalance(addr2, uint256.NewInt(1_000_000_000))
+ statedb.AddBalance(addr3, uint256.NewInt(1_000_000_000))
statedb.Commit(0, true, false)
chain := &testBlockChain{
@@ -854,7 +893,7 @@ func TestOpenCap(t *testing.T) {
statedb: statedb,
}
pool := New(Config{Datadir: storage, Datacap: datacap}, chain)
- if err := pool.Init(big.NewInt(1), chain.CurrentBlock(), makeAddressReserver()); err != nil {
+ if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
t.Fatalf("failed to create blob pool: %v", err)
}
// Verify that enough transactions have been dropped to get the pool's size
@@ -1214,6 +1253,24 @@ func TestAdd(t *testing.T) {
},
},
},
+ // Blob transactions that don't meet the min blob gas price should be rejected
+ {
+ seeds: map[string]seed{
+ "alice": {balance: 10000000},
+ },
+ adds: []addtx{
+ { // New account, no previous txs, nonce 0, but blob fee cap too low
+ from: "alice",
+ tx: makeUnsignedTx(0, 1, 1, 0),
+ err: txpool.ErrUnderpriced,
+ },
+ { // Same as above but blob fee cap equals minimum, should be accepted
+ from: "alice",
+ tx: makeUnsignedTx(0, 1, 1, params.BlobTxMinBlobGasprice),
+ err: nil,
+ },
+ },
+ },
}
for i, tt := range tests {
// Create a temporary folder for the persistent backend
@@ -1234,8 +1291,8 @@ func TestAdd(t *testing.T) {
keys[acc], _ = crypto.GenerateKey()
addrs[acc] = crypto.PubkeyToAddress(keys[acc].PublicKey)
- // Seed the state database with this acocunt
- statedb.AddBalance(addrs[acc], new(big.Int).SetUint64(seed.balance))
+ // Seed the state database with this account
+ statedb.AddBalance(addrs[acc], new(uint256.Int).SetUint64(seed.balance))
statedb.SetNonce(addrs[acc], seed.nonce)
// Sign the seed transactions and store them in the data store
@@ -1256,7 +1313,7 @@ func TestAdd(t *testing.T) {
statedb: statedb,
}
pool := New(Config{Datadir: storage}, chain)
- if err := pool.Init(big.NewInt(1), chain.CurrentBlock(), makeAddressReserver()); err != nil {
+ if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
t.Fatalf("test %d: failed to create blob pool: %v", i, err)
}
verifyPoolInternals(t, pool)
@@ -1274,3 +1331,65 @@ func TestAdd(t *testing.T) {
pool.Close()
}
}
+
+// Benchmarks the time it takes to assemble the lazy pending transaction list
+// from the pool contents.
+func BenchmarkPoolPending100Mb(b *testing.B) { benchmarkPoolPending(b, 100_000_000) }
+func BenchmarkPoolPending1GB(b *testing.B) { benchmarkPoolPending(b, 1_000_000_000) }
+func BenchmarkPoolPending10GB(b *testing.B) { benchmarkPoolPending(b, 10_000_000_000) }
+
+func benchmarkPoolPending(b *testing.B, datacap uint64) {
+ // Calculate the maximum number of transaction that would fit into the pool
+ // and generate a set of random accounts to seed them with.
+ capacity := datacap / params.BlobTxBlobGasPerBlob
+
+ var (
+ basefee = uint64(1050)
+ blobfee = uint64(105)
+ signer = types.LatestSigner(testChainConfig)
+ statedb, _ = state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewDatabase(memorydb.New())), nil)
+ chain = &testBlockChain{
+ config: testChainConfig,
+ basefee: uint256.NewInt(basefee),
+ blobfee: uint256.NewInt(blobfee),
+ statedb: statedb,
+ }
+ pool = New(Config{Datadir: ""}, chain)
+ )
+
+ if err := pool.Init(1, chain.CurrentBlock(), makeAddressReserver()); err != nil {
+ b.Fatalf("failed to create blob pool: %v", err)
+ }
+ // Fill the pool up with one random transaction from each account with the
+ // same price and everything to maximize the worst case scenario
+ for i := 0; i < int(capacity); i++ {
+ blobtx := makeUnsignedTx(0, 10, basefee+10, blobfee)
+ blobtx.R = uint256.NewInt(1)
+ blobtx.S = uint256.NewInt(uint64(100 + i))
+ blobtx.V = uint256.NewInt(0)
+ tx := types.NewTx(blobtx)
+ addr, err := types.Sender(signer, tx)
+ if err != nil {
+ b.Fatal(err)
+ }
+ statedb.AddBalance(addr, uint256.NewInt(1_000_000_000))
+ pool.add(tx)
+ }
+ statedb.Commit(0, true, false)
+ defer pool.Close()
+
+ // Benchmark assembling the pending
+ b.ResetTimer()
+ b.ReportAllocs()
+
+ for i := 0; i < b.N; i++ {
+ p := pool.Pending(txpool.PendingFilter{
+ MinTip: uint256.NewInt(1),
+ BaseFee: chain.basefee,
+ BlobFee: chain.blobfee,
+ })
+ if len(p) != int(capacity) {
+ b.Fatalf("have %d want %d", len(p), capacity)
+ }
+ }
+}
diff --git a/core/txpool/blobpool/config.go b/core/txpool/blobpool/config.go
index 6015b1baf6..5df7885fb3 100644
--- a/core/txpool/blobpool/config.go
+++ b/core/txpool/blobpool/config.go
@@ -40,8 +40,8 @@ type Config struct {
// DefaultConfig contains the default configurations for the transaction pool.
var DefaultConfig = Config{
Datadir: "blobpool",
- Datacap: 10 * 1024 * 1024 * 1024,
- PriceBump: 100, // either have patience or be aggressive, no mushy ground
+ Datacap: 10 * 1024 * 1024 * 1024 / 4, // TODO(karalabe): /4 handicap for rollout, gradually bump back up to 10GB
+ PriceBump: 100, // either have patience or be aggressive, no mushy ground
}
// sanitize checks the provided user configurations and changes anything that's
diff --git a/core/txpool/blobpool/evictheap.go b/core/txpool/blobpool/evictheap.go
index fcdded9abd..0824ddf735 100644
--- a/core/txpool/blobpool/evictheap.go
+++ b/core/txpool/blobpool/evictheap.go
@@ -40,7 +40,7 @@ import (
// transaction from each account to determine which account to evict from.
//
// The heap internally tracks a slice of cheapest transactions from each account
-// and a mapping from addresses to indices for direct removals/udates.
+// and a mapping from addresses to indices for direct removals/updates.
//
// The goal of the heap is to decide which account has the worst bottleneck to
// evict transactions from.
diff --git a/core/txpool/blobpool/limbo.go b/core/txpool/blobpool/limbo.go
index 9d97562f51..26d28f36cc 100644
--- a/core/txpool/blobpool/limbo.go
+++ b/core/txpool/blobpool/limbo.go
@@ -63,7 +63,7 @@ func newLimbo(datadir string) (*limbo, error) {
index: make(map[common.Hash]uint64),
groups: make(map[uint64]map[uint64]common.Hash),
}
- // Index all limboed blobs on disk and delete anything inprocessable
+ // Index all limboed blobs on disk and delete anything unprocessable
var fails []uint64
index := func(id uint64, size uint32, data []byte) {
if l.parseBlob(id, data) != nil {
@@ -99,7 +99,7 @@ func (l *limbo) parseBlob(id uint64, data []byte) error {
item := new(limboBlob)
if err := rlp.DecodeBytes(data, item); err != nil {
// This path is impossible unless the disk data representation changes
- // across restarts. For that ever unprobable case, recover gracefully
+ // across restarts. For that ever improbable case, recover gracefully
// by ignoring this data entry.
log.Error("Failed to decode blob limbo entry", "id", id, "err", err)
return err
@@ -182,7 +182,7 @@ func (l *limbo) pull(tx common.Hash) (*types.Transaction, error) {
// update changes the block number under which a blob transaction is tracked. This
// method should be used when a reorg changes a transaction's inclusion block.
//
-// The method may log errors for various unexpcted scenarios but will not return
+// The method may log errors for various unexpected scenarios but will not return
// any of it since there's no clear error case. Some errors may be due to coding
// issues, others caused by signers mining MEV stuff or swapping transactions. In
// all cases, the pool needs to continue operating.
diff --git a/core/txpool/blobpool/metrics.go b/core/txpool/blobpool/metrics.go
index 41fb0e50ed..a65a345b22 100644
--- a/core/txpool/blobpool/metrics.go
+++ b/core/txpool/blobpool/metrics.go
@@ -75,8 +75,8 @@ var (
pooltipGauge = metrics.NewRegisteredGauge("blobpool/pooltip", nil)
// addwait/time, resetwait/time and getwait/time track the rough health of
- // the pool and whether or not it's capable of keeping up with the load from
- // the network.
+ // the pool and whether it's capable of keeping up with the load from the
+ // network.
addwaitHist = metrics.NewRegisteredHistogram("blobpool/addwait", nil, metrics.NewExpDecaySample(1028, 0.015))
addtimeHist = metrics.NewRegisteredHistogram("blobpool/addtime", nil, metrics.NewExpDecaySample(1028, 0.015))
getwaitHist = metrics.NewRegisteredHistogram("blobpool/getwait", nil, metrics.NewExpDecaySample(1028, 0.015))
@@ -85,4 +85,31 @@ var (
pendtimeHist = metrics.NewRegisteredHistogram("blobpool/pendtime", nil, metrics.NewExpDecaySample(1028, 0.015))
resetwaitHist = metrics.NewRegisteredHistogram("blobpool/resetwait", nil, metrics.NewExpDecaySample(1028, 0.015))
resettimeHist = metrics.NewRegisteredHistogram("blobpool/resettime", nil, metrics.NewExpDecaySample(1028, 0.015))
+
+ // The below metrics track various cases where transactions are dropped out
+ // of the pool. Most are exceptional, some are chain progression and some
+ // threshold cappings.
+ dropInvalidMeter = metrics.NewRegisteredMeter("blobpool/drop/invalid", nil) // Invalid transaction, consensus change or bugfix, neutral-ish
+ dropDanglingMeter = metrics.NewRegisteredMeter("blobpool/drop/dangling", nil) // First nonce gapped, bad
+ dropFilledMeter = metrics.NewRegisteredMeter("blobpool/drop/filled", nil) // State full-overlap, chain progress, ok
+ dropOverlappedMeter = metrics.NewRegisteredMeter("blobpool/drop/overlapped", nil) // State partial-overlap, chain progress, ok
+ dropRepeatedMeter = metrics.NewRegisteredMeter("blobpool/drop/repeated", nil) // Repeated nonce, bad
+ dropGappedMeter = metrics.NewRegisteredMeter("blobpool/drop/gapped", nil) // Non-first nonce gapped, bad
+ dropOverdraftedMeter = metrics.NewRegisteredMeter("blobpool/drop/overdrafted", nil) // Balance exceeded, bad
+ dropOvercappedMeter = metrics.NewRegisteredMeter("blobpool/drop/overcapped", nil) // Per-account cap exceeded, bad
+ dropOverflownMeter = metrics.NewRegisteredMeter("blobpool/drop/overflown", nil) // Global disk cap exceeded, neutral-ish
+ dropUnderpricedMeter = metrics.NewRegisteredMeter("blobpool/drop/underpriced", nil) // Gas tip changed, neutral
+ dropReplacedMeter = metrics.NewRegisteredMeter("blobpool/drop/replaced", nil) // Transaction replaced, neutral
+
+ // The below metrics track various outcomes of transactions being added to
+ // the pool.
+ addInvalidMeter = metrics.NewRegisteredMeter("blobpool/add/invalid", nil) // Invalid transaction, reject, neutral
+ addUnderpricedMeter = metrics.NewRegisteredMeter("blobpool/add/underpriced", nil) // Gas tip too low, neutral
+ addStaleMeter = metrics.NewRegisteredMeter("blobpool/add/stale", nil) // Nonce already filled, reject, bad-ish
+ addGappedMeter = metrics.NewRegisteredMeter("blobpool/add/gapped", nil) // Nonce gapped, reject, bad-ish
+ addOverdraftedMeter = metrics.NewRegisteredMeter("blobpool/add/overdrafted", nil) // Balance exceeded, reject, neutral
+ addOvercappedMeter = metrics.NewRegisteredMeter("blobpool/add/overcapped", nil) // Per-account cap exceeded, reject, neutral
+ addNoreplaceMeter = metrics.NewRegisteredMeter("blobpool/add/noreplace", nil) // Replacement fees or tips too low, neutral
+ addNonExclusiveMeter = metrics.NewRegisteredMeter("blobpool/add/nonexclusive", nil) // Plain transaction from same account exists, reject, neutral
+ addValidMeter = metrics.NewRegisteredMeter("blobpool/add/valid", nil) // Valid transaction, add, neutral
)
diff --git a/core/txpool/blobpool/priority_test.go b/core/txpool/blobpool/priority_test.go
index 3c9523d512..7ff5c86bca 100644
--- a/core/txpool/blobpool/priority_test.go
+++ b/core/txpool/blobpool/priority_test.go
@@ -74,7 +74,7 @@ func BenchmarkDynamicFeeJumpCalculation(b *testing.B) {
// Benchmarks how many priority recalculations can be done.
func BenchmarkPriorityCalculation(b *testing.B) {
// The basefee and blob fee is constant for all transactions across a block,
- // so we can assume theit absolute jump counts can be pre-computed.
+ // so we can assume their absolute jump counts can be pre-computed.
basefee := uint256.NewInt(17_200_000_000) // 17.2 Gwei is the 22.03.2023 zero-emission basefee, random number
blobfee := uint256.NewInt(123_456_789_000) // Completely random, no idea what this will be
diff --git a/core/txpool/errors.go b/core/txpool/errors.go
index d9aa1527a3..80a12e76ed 100644
--- a/core/txpool/errors.go
+++ b/core/txpool/errors.go
@@ -64,4 +64,10 @@ var (
// ErrFutureReplacePending is returned if a future transaction replaces a pending
// one. Future transactions should only be able to replace other future transactions.
ErrFutureReplacePending = errors.New("future transaction tries to replace pending")
+
+ // ErrAlreadyReserved is returned if the sender address has a pending transaction
+ // in a different subpool. For example, this error is returned in response to any
+ // input transaction of non-blob type when a blob transaction from this sender
+ // remains pending (and vice-versa).
+ ErrAlreadyReserved = errors.New("address already reserved")
)
diff --git a/core/txpool/legacypool/journal.go b/core/txpool/legacypool/journal.go
index 2065fb36e0..3e5d43e123 100644
--- a/core/txpool/legacypool/journal.go
+++ b/core/txpool/legacypool/journal.go
@@ -174,7 +174,12 @@ func (journal *journal) rotate(all map[common.Address]types.Transactions) error
return err
}
journal.writer = sink
- log.Info("Regenerated local transaction journal", "transactions", journaled, "accounts", len(all))
+
+ logger := log.Info
+ if len(all) == 0 {
+ logger = log.Debug
+ }
+ logger("Regenerated local transaction journal", "transactions", journaled, "accounts", len(all))
return nil
}
diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go
index 669c483e9f..31e9fdd95e 100644
--- a/core/txpool/legacypool/legacypool.go
+++ b/core/txpool/legacypool/legacypool.go
@@ -50,6 +50,7 @@ import (
"github.com/ethereum/go-ethereum/common/prque"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
+ "github.com/holiman/uint256"
)
const (
@@ -223,7 +224,7 @@ type LegacyPool struct {
config Config
chainconfig *params.ChainConfig
chain BlockChain
- gasTip atomic.Pointer[big.Int]
+ gasTip atomic.Pointer[uint256.Int]
minimumFee *big.Int
txFeed event.Feed
signer types.Signer
@@ -317,15 +318,15 @@ func (pool *LegacyPool) Filter(tx *types.Transaction) bool {
// head to allow balance / nonce checks. The transaction journal will be loaded
// from disk and filtered based on the provided starting settings. The internal
// goroutines will be spun up and the pool deemed operational afterwards.
-func (pool *LegacyPool) Init(gasTip *big.Int, head *types.Header, reserve txpool.AddressReserver) error {
+func (pool *LegacyPool) Init(gasTip uint64, head *types.Header, reserve txpool.AddressReserver) error {
// Set the address reserver to request exclusive access to pooled accounts
pool.reserve = reserve
// Set the basic pool parameters
- pool.gasTip.Store(gasTip)
+ pool.gasTip.Store(uint256.NewInt(gasTip))
// Initialize the state with head block, or fallback to empty one in
- // case the head state is not available(might occur when node is not
+ // case the head state is not available (might occur when node is not
// fully synced).
statedb, err := pool.chain.StateAt(head.Root)
if err != nil {
@@ -468,11 +469,13 @@ func (pool *LegacyPool) SetGasTip(tip *big.Int) {
pool.mu.Lock()
defer pool.mu.Unlock()
- old := pool.gasTip.Load()
- pool.gasTip.Store(new(big.Int).Set(tip))
-
+ var (
+ newTip = uint256.MustFromBig(tip)
+ old = pool.gasTip.Load()
+ )
+ pool.gasTip.Store(newTip)
// If the min miner fee increased, remove transactions below the new threshold
- if tip.Cmp(old) > 0 {
+ if newTip.Cmp(old) > 0 {
// pool.priced is sorted by GasFeeCap, so we have to iterate through pool.all instead
drop := pool.all.RemotesBelowTip(tip)
for _, tx := range drop {
@@ -480,7 +483,7 @@ func (pool *LegacyPool) SetGasTip(tip *big.Int) {
}
pool.priced.Removed(len(drop))
}
- log.Info("Legacy pool tip threshold updated", "tip", tip)
+ log.Info("Legacy pool tip threshold updated", "tip", newTip)
}
func (pool *LegacyPool) SetMinFee(minFee *big.Int) {
@@ -557,23 +560,33 @@ func (pool *LegacyPool) ContentFrom(addr common.Address) ([]*types.Transaction,
}
// Pending retrieves all currently processable transactions, grouped by origin
-// account and sorted by nonce. The returned transaction set is a copy and can be
-// freely modified by calling code.
+// account and sorted by nonce.
//
-// The enforceTips parameter can be used to do an extra filtering on the pending
-// transactions and only return those whose **effective** tip is large enough in
-// the next pending execution environment.
-func (pool *LegacyPool) Pending(enforceTips bool) map[common.Address][]*txpool.LazyTransaction {
- return pool.PendingWithBaseFee(enforceTips, nil)
-}
-
-// If baseFee is nil, then pool.priced.urgent.baseFee is used.
-func (pool *LegacyPool) PendingWithBaseFee(enforceTips bool, baseFee *big.Int) map[common.Address][]*txpool.LazyTransaction {
+// The transactions can also be pre-filtered by the dynamic fee components to
+// reduce allocations and load on downstream subsystems.
+func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction {
+ // If only blob transactions are requested, this pool is unsuitable as it
+ // contains none, don't even bother.
+ if filter.OnlyBlobTxs {
+ return nil
+ }
pool.mu.Lock()
defer pool.mu.Unlock()
- if baseFee == nil {
- baseFee = pool.priced.urgent.baseFee
+ // Convert the new uint256.Int types to the old big.Int ones used by the legacy pool
+ var (
+ minTipBig *big.Int
+ baseFeeBig *big.Int
+ )
+ if filter.MinTip != nil {
+ minTipBig = filter.MinTip.ToBig()
+ }
+ if filter.BaseFee != nil {
+ baseFeeBig = filter.BaseFee.ToBig()
+ }
+
+ if baseFeeBig == nil {
+ baseFeeBig = pool.priced.urgent.baseFee
}
pending := make(map[common.Address][]*txpool.LazyTransaction, len(pool.pending))
@@ -581,9 +594,9 @@ func (pool *LegacyPool) PendingWithBaseFee(enforceTips bool, baseFee *big.Int) m
txs := list.Flatten()
// If the miner requests tip enforcement, cap the lists now
- if enforceTips && !pool.locals.contains(addr) {
+ if minTipBig != nil && !pool.locals.contains(addr) {
for i, tx := range txs {
- if tx.EffectiveGasTipIntCmp(pool.gasTip.Load(), baseFee) < 0 {
+ if tx.EffectiveGasTipIntCmp(minTipBig, baseFeeBig) < 0 {
txs = txs[:i]
break
}
@@ -597,8 +610,8 @@ func (pool *LegacyPool) PendingWithBaseFee(enforceTips bool, baseFee *big.Int) m
Hash: txs[i].Hash(),
Tx: txs[i].WithoutBlobTxSidecar(),
Time: txs[i].Time(),
- GasFeeCap: txs[i].GasFeeCap(),
- GasTipCap: txs[i].GasTipCap(),
+ GasFeeCap: uint256.MustFromBig(txs[i].GasFeeCap()),
+ GasTipCap: uint256.MustFromBig(txs[i].GasTipCap()),
Gas: txs[i].Gas(),
BlobGas: txs[i].BlobGas(),
}
@@ -661,7 +674,7 @@ func (pool *LegacyPool) validateTxBasics(tx *types.Transaction, local bool) erro
1< threshold; size-- {
drops = append(drops, m.items[(*m.index)[size-1]])
delete(m.items, (*m.index)[size-1])
}
*m.index = (*m.index)[:threshold]
- heap.Init(m.index)
+ // The sorted m.index slice is still a valid heap, so there is no need to
+ // reheap after deleting tail items.
// If we had a cache, shift the back
m.cacheMu.Lock()
@@ -281,19 +283,19 @@ type list struct {
strict bool // Whether nonces are strictly continuous or not
txs *sortedMap // Heap indexed sorted hash map of the transactions
- costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance)
- gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit)
- totalcost *big.Int // Total cost of all transactions in the list
+ costcap *uint256.Int // Price of the highest costing transaction (reset only if exceeds balance)
+ gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit)
+ totalcost *uint256.Int // Total cost of all transactions in the list
}
-// newList create a new transaction list for maintaining nonce-indexable fast,
+// newList creates a new transaction list for maintaining nonce-indexable fast,
// gapped, sortable transaction lists.
func newList(strict bool) *list {
return &list{
strict: strict,
txs: newSortedMap(),
- costcap: new(big.Int),
- totalcost: new(big.Int),
+ costcap: new(uint256.Int),
+ totalcost: new(uint256.Int),
}
}
@@ -335,10 +337,15 @@ func (l *list) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transa
l.subTotalCost([]*types.Transaction{old})
}
// Add new tx cost to totalcost
- l.totalcost.Add(l.totalcost, tx.Cost())
+ cost, overflow := uint256.FromBig(tx.Cost())
+ if overflow {
+ return false, nil
+ }
+ l.totalcost.Add(l.totalcost, cost)
+
// Otherwise overwrite the old transaction with the current one
l.txs.Put(tx)
- if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 {
+ if l.costcap.Cmp(cost) < 0 {
l.costcap = cost
}
if gas := tx.Gas(); l.gascap < gas {
@@ -365,17 +372,17 @@ func (l *list) Forward(threshold uint64) types.Transactions {
// a point in calculating all the costs or if the balance covers all. If the threshold
// is lower than the costgas cap, the caps will be reset to a new high after removing
// the newly invalidated transactions.
-func (l *list) Filter(costLimit *big.Int, gasLimit uint64) (types.Transactions, types.Transactions) {
+func (l *list) Filter(costLimit *uint256.Int, gasLimit uint64) (types.Transactions, types.Transactions) {
// If all transactions are below the threshold, short circuit
if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit {
return nil, nil
}
- l.costcap = new(big.Int).Set(costLimit) // Lower the caps to the thresholds
+ l.costcap = new(uint256.Int).Set(costLimit) // Lower the caps to the thresholds
l.gascap = gasLimit
// Filter out all the transactions above the account's funds
removed := l.txs.Filter(func(tx *types.Transaction) bool {
- return tx.Gas() > gasLimit || tx.Cost().Cmp(costLimit) > 0
+ return tx.Gas() > gasLimit || tx.Cost().Cmp(costLimit.ToBig()) > 0
})
if len(removed) == 0 {
@@ -466,7 +473,10 @@ func (l *list) LastElement() *types.Transaction {
// total cost of all transactions.
func (l *list) subTotalCost(txs []*types.Transaction) {
for _, tx := range txs {
- l.totalcost.Sub(l.totalcost, tx.Cost())
+ _, underflow := l.totalcost.SubOverflow(l.totalcost, uint256.MustFromBig(tx.Cost()))
+ if underflow {
+ panic("totalcost underflow")
+ }
}
}
diff --git a/core/txpool/legacypool/list_test.go b/core/txpool/legacypool/list_test.go
index d7ca91844e..28670fe953 100644
--- a/core/txpool/legacypool/list_test.go
+++ b/core/txpool/legacypool/list_test.go
@@ -32,7 +32,9 @@ import (
"testing"
"github.com/ava-labs/subnet-evm/core/types"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/holiman/uint256"
)
// Tests that transactions can be added to strict lists and list contents and
@@ -61,6 +63,21 @@ func TestStrictListAdd(t *testing.T) {
}
}
+// TestListAddVeryExpensive tests adding txs which exceed 256 bits in cost. It is
+// expected that the list does not panic.
+func TestListAddVeryExpensive(t *testing.T) {
+ key, _ := crypto.GenerateKey()
+ list := newList(true)
+ for i := 0; i < 3; i++ {
+ value := big.NewInt(100)
+ gasprice, _ := new(big.Int).SetString("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 0)
+ gaslimit := uint64(i)
+ tx, _ := types.SignTx(types.NewTransaction(uint64(i), common.Address{}, value, gaslimit, gasprice, nil), types.HomesteadSigner{}, key)
+ t.Logf("cost: %x bitlen: %d\n", tx.Cost(), tx.Cost().BitLen())
+ list.Add(tx, DefaultConfig.PriceBump)
+ }
+}
+
func BenchmarkListAdd(b *testing.B) {
// Generate a list of transactions to insert
key, _ := crypto.GenerateKey()
@@ -70,7 +87,7 @@ func BenchmarkListAdd(b *testing.B) {
txs[i] = transaction(uint64(i), 0, key)
}
// Insert the transactions in a random order
- priceLimit := big.NewInt(int64(DefaultConfig.PriceLimit))
+ priceLimit := uint256.NewInt(DefaultConfig.PriceLimit)
b.ResetTimer()
for i := 0; i < b.N; i++ {
list := newList(true)
@@ -80,3 +97,25 @@ func BenchmarkListAdd(b *testing.B) {
}
}
}
+
+func BenchmarkListCapOneTx(b *testing.B) {
+ // Generate a list of transactions to insert
+ key, _ := crypto.GenerateKey()
+
+ txs := make(types.Transactions, 32)
+ for i := 0; i < len(txs); i++ {
+ txs[i] = transaction(uint64(i), 0, key)
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ list := newList(true)
+ // Insert the transactions in a random order
+ for _, v := range rand.Perm(len(txs)) {
+ list.Add(txs[v], DefaultConfig.PriceBump)
+ }
+ b.StartTimer()
+ list.Cap(list.Len() - 1)
+ b.StopTimer()
+ }
+}
diff --git a/core/txpool/subpool.go b/core/txpool/subpool.go
index 0a597c779d..5459154090 100644
--- a/core/txpool/subpool.go
+++ b/core/txpool/subpool.go
@@ -34,6 +34,7 @@ import (
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/event"
+ "github.com/holiman/uint256"
)
// LazyTransaction contains a small subset of the transaction properties that is
@@ -44,9 +45,9 @@ type LazyTransaction struct {
Hash common.Hash // Transaction hash to pull up if needed
Tx *types.Transaction // Transaction if already resolved
- Time time.Time // Time when the transaction was first seen
- GasFeeCap *big.Int // Maximum fee per gas the transaction may consume
- GasTipCap *big.Int // Maximum miner tip per gas the transaction can pay
+ Time time.Time // Time when the transaction was first seen
+ GasFeeCap *uint256.Int // Maximum fee per gas the transaction may consume
+ GasTipCap *uint256.Int // Maximum miner tip per gas the transaction can pay
Gas uint64 // Amount of gas required by the transaction
BlobGas uint64 // Amount of blob gas required by the transaction
@@ -54,11 +55,17 @@ type LazyTransaction struct {
// Resolve retrieves the full transaction belonging to a lazy handle if it is still
// maintained by the transaction pool.
+//
+// Note, the method will *not* cache the retrieved transaction if the original
+// pool has not cached it. The idea being, that if the tx was too big to insert
+// originally, silently saving it will cause more trouble down the line (and
+// indeed seems to have caused a memory bloat in the original implementation
+// which did just that).
func (ltx *LazyTransaction) Resolve() *types.Transaction {
- if ltx.Tx == nil {
- ltx.Tx = ltx.Pool.Get(ltx.Hash)
+ if ltx.Tx != nil {
+ return ltx.Tx
}
- return ltx.Tx
+ return ltx.Pool.Get(ltx.Hash)
}
// LazyResolver is a minimal interface needed for a transaction pool to satisfy
@@ -73,13 +80,28 @@ type LazyResolver interface {
// may request (and relinquish) exclusive access to certain addresses.
type AddressReserver func(addr common.Address, reserve bool) error
+// PendingFilter is a collection of filter rules to allow retrieving a subset
+// of transactions for announcement or mining.
+//
+// Note, the entries here are not arbitrary useful filters, rather each one has
+// a very specific call site in mind and each one can be evaluated very cheaply
+// by the pool implementations. Only add new ones that satisfy those constraints.
+type PendingFilter struct {
+ MinTip *uint256.Int // Minimum miner tip required to include a transaction
+ BaseFee *uint256.Int // Minimum 1559 basefee needed to include a transaction
+ BlobFee *uint256.Int // Minimum 4844 blobfee needed to include a blob transaction
+
+ OnlyPlainTxs bool // Return only plain EVM transactions (peer-join announces, block space filling)
+ OnlyBlobTxs bool // Return only blob transactions (block blob-space filling)
+}
+
// SubPool represents a specialized transaction pool that lives on its own (e.g.
// blob pool). Since independent of how many specialized pools we have, they do
// need to be updated in lockstep and assemble into one coherent view for block
// production, this interface defines the common methods that allow the primary
// transaction pool to manage the subpools.
type SubPool interface {
- // Filter is a selector used to decide whether a transaction whould be added
+ // Filter is a selector used to decide whether a transaction would be added
// to this particular subpool.
Filter(tx *types.Transaction) bool
@@ -90,7 +112,7 @@ type SubPool interface {
// These should not be passed as a constructor argument - nor should the pools
// start by themselves - in order to keep multiple subpools in lockstep with
// one another.
- Init(gasTip *big.Int, head *types.Header, reserve AddressReserver) error
+ Init(gasTip uint64, head *types.Header, reserve AddressReserver) error
// Close terminates any background processing threads and releases any held
// resources.
@@ -120,8 +142,10 @@ type SubPool interface {
// Pending retrieves all currently processable transactions, grouped by origin
// account and sorted by nonce.
- Pending(enforceTips bool) map[common.Address][]*LazyTransaction
- PendingWithBaseFee(enforceTips bool, baseFee *big.Int) map[common.Address][]*LazyTransaction
+ //
+ // The transactions can also be pre-filtered by the dynamic fee components to
+ // reduce allocations and load on downstream subsystems.
+ Pending(filter PendingFilter) map[common.Address][]*LazyTransaction
IteratePending(f func(tx *types.Transaction) bool) bool // Returns false if iteration was interrupted.
// SubscribeTransactions subscribes to new transaction events. The subscriber
diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go
index 7539001ac0..e65babe5a3 100644
--- a/core/txpool/txpool.go
+++ b/core/txpool/txpool.go
@@ -88,6 +88,9 @@ type TxPool struct {
subs event.SubscriptionScope // Subscription scope to unsubscribe all on shutdown
quit chan chan error // Quit channel to tear down the head updater
+ term chan struct{} // Termination channel to detect a closed pool
+
+ sync chan chan error // Testing / simulator channel to block until internal reset is done
gasTip atomic.Pointer[big.Int] // Remember last value set so it can be retrieved
reorgFeed event.Feed
@@ -95,7 +98,7 @@ type TxPool struct {
// New creates a new transaction pool to gather, sort and filter inbound
// transactions from the network.
-func New(gasTip *big.Int, chain BlockChain, subpools []SubPool) (*TxPool, error) {
+func New(gasTip uint64, chain BlockChain, subpools []SubPool) (*TxPool, error) {
// Retrieve the current head so that all subpools and this main coordinator
// pool will have the same starting state, even if the chain moves forward
// during initialization.
@@ -105,7 +108,11 @@ func New(gasTip *big.Int, chain BlockChain, subpools []SubPool) (*TxPool, error)
subpools: subpools,
reservations: make(map[common.Address]SubPool),
quit: make(chan chan error),
+ term: make(chan struct{}),
+ sync: make(chan chan error),
}
+ pool.gasTip.Store(new(big.Int).SetUint64(gasTip))
+
for i, subpool := range subpools {
if err := subpool.Init(gasTip, head, pool.reserver(i, subpool)); err != nil {
for j := i - 1; j >= 0; j-- {
@@ -114,7 +121,6 @@ func New(gasTip *big.Int, chain BlockChain, subpools []SubPool) (*TxPool, error)
return nil, err
}
}
- pool.gasTip.Store(gasTip)
// Subscribe to chain head events to trigger subpool resets
var (
@@ -146,7 +152,7 @@ func (p *TxPool) reserver(id int, subpool SubPool) AddressReserver {
log.Error("pool attempted to reserve already-owned address", "address", addr)
return nil // Ignore fault to give the pool a chance to recover while the bug gets fixed
}
- return errors.New("address already reserved")
+ return ErrAlreadyReserved
}
p.reservations[addr] = subpool
if metrics.Enabled {
@@ -205,6 +211,9 @@ func (p *TxPool) Close() error {
// outside blockchain events as well as for various reporting and transaction
// eviction events.
func (p *TxPool) loop(head *types.Header, newHeadCh <-chan core.ChainHeadEvent) {
+ // Close the termination marker when the pool stops
+ defer close(p.term)
+
// Track the previous and current head to feed to an idle reset
var (
oldHead = head
@@ -214,13 +223,23 @@ func (p *TxPool) loop(head *types.Header, newHeadCh <-chan core.ChainHeadEvent)
var (
resetBusy = make(chan struct{}, 1) // Allow 1 reset to run concurrently
resetDone = make(chan *types.Header)
+
+ resetForced bool // Whether a forced reset was requested, only used in simulator mode
+ resetWaiter chan error // Channel waiting on a forced reset, only used in simulator mode
)
+ // Notify the live reset waiter to not block if the txpool is closed.
+ defer func() {
+ if resetWaiter != nil {
+ resetWaiter <- errors.New("pool already terminated")
+ resetWaiter = nil
+ }
+ }()
var errc chan error
for errc == nil {
// Something interesting might have happened, run a reset if there is
// one needed but none is running. The resetter will run on its own
// goroutine to allow chain head events to be consumed contiguously.
- if newHead != oldHead {
+ if newHead != oldHead || resetForced {
// Try to inject a busy marker and start a reset if successful
select {
case resetBusy <- struct{}{}:
@@ -233,8 +252,17 @@ func (p *TxPool) loop(head *types.Header, newHeadCh <-chan core.ChainHeadEvent)
resetDone <- newHead
}(oldHead, newHead)
+ // If the reset operation was explicitly requested, consider it
+ // being fulfilled and drop the request marker. If it was not,
+ // this is a noop.
+ resetForced = false
+
default:
- // Reset already running, wait until it finishes
+ // Reset already running, wait until it finishes.
+ //
+ // Note, this will not drop any forced reset request. If a forced
+ // reset was requested, but we were busy, then when the currently
+ // running reset finishes, a new one will be spun up.
}
}
// Wait for the next chain head event or a previous reset finish
@@ -248,8 +276,26 @@ func (p *TxPool) loop(head *types.Header, newHeadCh <-chan core.ChainHeadEvent)
oldHead = head
<-resetBusy
+ // If someone is waiting for a reset to finish, notify them, unless
+ // the forced op is still pending. In that case, wait another round
+ // of resets.
+ if resetWaiter != nil && !resetForced {
+ resetWaiter <- nil
+ resetWaiter = nil
+ }
+
case errc = <-p.quit:
// Termination requested, break out on the next loop round
+
+ case syncc := <-p.sync:
+ // Transaction pool is running inside a simulator, and we are about
+ // to create a new block. Request a forced sync operation to ensure
+ // that any running reset operation finishes to make block imports
+ // deterministic. On top of that, run a new reset operation to make
+ // transaction insertions deterministic instead of being stuck in a
+ // queue waiting for a reset.
+ resetForced = true
+ resetWaiter = syncc
}
}
// Notify the closer of termination (no error possible for now)
@@ -365,19 +411,12 @@ func (p *TxPool) AddRemotesSync(txs []*types.Transaction) []error {
// account and sorted by nonce. The returned transaction set is a copy and can be
// freely modified by calling code.
//
-// The enforceTips parameter can be used to do an extra filtering on the pending
-// transactions and only return those whose **effective** tip is large enough in
-// the next pending execution environment.
-// account and sorted by nonce.
-func (p *TxPool) Pending(enforceTips bool) map[common.Address][]*LazyTransaction {
- return p.PendingWithBaseFee(enforceTips, nil)
-}
-
-// If baseFee is nil, then pool.priced.urgent.baseFee is used.
-func (p *TxPool) PendingWithBaseFee(enforceTips bool, baseFee *big.Int) map[common.Address][]*LazyTransaction {
+// The transactions can also be pre-filtered by the dynamic fee components to
+// reduce allocations and load on downstream subsystems.
+func (p *TxPool) Pending(filter PendingFilter) map[common.Address][]*LazyTransaction {
txs := make(map[common.Address][]*LazyTransaction)
for _, subpool := range p.subpools {
- for addr, set := range subpool.PendingWithBaseFee(enforceTips, baseFee) {
+ for addr, set := range subpool.Pending(filter) {
txs[addr] = set
}
}
@@ -386,13 +425,12 @@ func (p *TxPool) PendingWithBaseFee(enforceTips bool, baseFee *big.Int) map[comm
// PendingSize returns the number of pending txs in the tx pool.
//
-// The enforceTips parameter can be used to do an extra filtering on the pending
-// transactions and only return those whose **effective** tip is large enough in
-// the next pending execution environment.
-func (p *TxPool) PendingSize(enforceTips bool) int {
+// The filter parameter can be used to do an extra filtering on the pending
+// transactions.
+func (p *TxPool) PendingSize(filter PendingFilter) int {
count := 0
for _, subpool := range p.subpools {
- for _, txs := range subpool.Pending(enforceTips) {
+ for _, txs := range subpool.Pending(filter) {
count += len(txs)
}
}
@@ -412,9 +450,13 @@ func (p *TxPool) IteratePending(f func(tx *types.Transaction) bool) {
// SubscribeTransactions registers a subscription for new transaction events,
// supporting feeding only newly seen or also resurrected transactions.
func (p *TxPool) SubscribeTransactions(ch chan<- core.NewTxsEvent, reorgs bool) event.Subscription {
- subs := make([]event.Subscription, len(p.subpools))
- for i, subpool := range p.subpools {
- subs[i] = subpool.SubscribeTransactions(ch, reorgs)
+ subs := make([]event.Subscription, 0, len(p.subpools))
+ for _, subpool := range p.subpools {
+ subpool := subpool.SubscribeTransactions(ch, reorgs)
+ if subpool == nil {
+ continue
+ }
+ subs = append(subs, subpool)
}
return p.subs.Track(event.JoinSubscriptions(subs...))
}
@@ -512,3 +554,20 @@ func (p *TxPool) Status(hash common.Hash) TxStatus {
}
return TxStatusUnknown
}
+
+// Sync is a helper method for unit tests or simulator runs where the chain events
+// are arriving in quick succession, without any time in between them to run the
+// internal background reset operations. This method will run an explicit reset
+// operation to ensure the pool stabilises, thus avoiding flakey behavior.
+//
+// Note, do not use this in production / live code. In live code, the pool is
+// meant to reset on a separate thread to avoid DoS vectors.
+func (p *TxPool) Sync() error {
+ sync := make(chan error)
+ select {
+ case p.sync <- sync:
+ return <-sync
+ case <-p.term:
+ return errors.New("pool already terminated")
+ }
+}
diff --git a/core/txpool/validation.go b/core/txpool/validation.go
index 6e80ac9a91..0e07f87fc3 100644
--- a/core/txpool/validation.go
+++ b/core/txpool/validation.go
@@ -42,6 +42,12 @@ import (
"github.com/ethereum/go-ethereum/log"
)
+var (
+ // blobTxMinBlobGasPrice is the big.Int version of the configured protocol
+ // parameter to avoid constucting a new big integer for every transaction.
+ blobTxMinBlobGasPrice = big.NewInt(params.BlobTxMinBlobGasprice)
+)
+
// ValidationOptions define certain differences between transaction validation
// across the different pools without having to duplicate those checks.
type ValidationOptions struct {
@@ -119,15 +125,17 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
return err
}
if txGas := tx.Gas(); txGas < intrGas {
- return fmt.Errorf("%w: address %v tx gas (%v) < intrinsic gas (%v)", core.ErrIntrinsicGas, from.Hex(), tx.Gas(), intrGas)
+ return fmt.Errorf("%w: address %v tx gas (%v), minimum needed %v", core.ErrIntrinsicGas, from.Hex(), txGas, intrGas)
}
- // Ensure the gasprice is high enough to cover the requirement of the calling
- // pool and/or block producer
+ // Ensure the gasprice is high enough to cover the requirement of the calling pool
if tx.GasTipCapIntCmp(opts.MinTip) < 0 {
- return fmt.Errorf("%w: tip needed %v, tip permitted %v", ErrUnderpriced, opts.MinTip, tx.GasTipCap())
+ return fmt.Errorf("%w: gas tip cap %v, minimum needed %v", ErrUnderpriced, tx.GasTipCap(), opts.MinTip)
}
- // Ensure blob transactions have valid commitments
if tx.Type() == types.BlobTxType {
+ // Ensure the blob fee cap satisfies the minimum blob gas price
+ if tx.BlobGasFeeCapIntCmp(blobTxMinBlobGasPrice) < 0 {
+ return fmt.Errorf("%w: blob fee cap %v, minimum needed %v", ErrUnderpriced, tx.BlobGasFeeCap(), blobTxMinBlobGasPrice)
+ }
sidecar := tx.BlobTxSidecar()
if sidecar == nil {
return fmt.Errorf("missing sidecar in blob transaction")
@@ -141,6 +149,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types
if len(hashes) > params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob {
return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob)
}
+ // Ensure commitments, proofs and hashes are valid
if err := validateBlobSidecar(hashes, sidecar); err != nil {
return err
}
@@ -161,17 +170,10 @@ func validateBlobSidecar(hashes []common.Hash, sidecar *types.BlobTxSidecar) err
// Blob quantities match up, validate that the provers match with the
// transaction hash before getting to the cryptography
hasher := sha256.New()
- for i, want := range hashes {
- hasher.Write(sidecar.Commitments[i][:])
- hash := hasher.Sum(nil)
- hasher.Reset()
-
- var vhash common.Hash
- vhash[0] = params.BlobTxHashVersion
- copy(vhash[1:], hash[1:])
-
- if vhash != want {
- return fmt.Errorf("blob %d: computed hash %#x mismatches transaction one %#x", i, vhash, want)
+ for i, vhash := range hashes {
+ computed := kzg4844.CalcBlobHashV1(hasher, &sidecar.Commitments[i])
+ if vhash != computed {
+ return fmt.Errorf("blob %d: computed hash %#x mismatches transaction one %#x", i, computed, vhash)
}
}
// Blob commitments match with the hashes in the transaction, verify the
@@ -243,7 +245,7 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op
}
// Ensure the transactor has enough funds to cover the transaction costs
var (
- balance = opts.State.GetBalance(from)
+ balance = opts.State.GetBalance(from).ToBig()
cost = tx.Cost()
)
if balance.Cmp(cost) < 0 {
diff --git a/core/types/account.go b/core/types/account.go
new file mode 100644
index 0000000000..bb0f4ca02e
--- /dev/null
+++ b/core/types/account.go
@@ -0,0 +1,87 @@
+// Copyright 2024 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 .
+
+package types
+
+import (
+ "bytes"
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/common/math"
+)
+
+//go:generate go run github.com/fjl/gencodec -type Account -field-override accountMarshaling -out gen_account.go
+
+// Account represents an Ethereum account and its attached data.
+// This type is used to specify accounts in the genesis block state, and
+// is also useful for JSON encoding/decoding of accounts.
+type Account struct {
+ Code []byte `json:"code,omitempty"`
+ Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
+ Balance *big.Int `json:"balance" gencodec:"required"`
+ Nonce uint64 `json:"nonce,omitempty"`
+
+ // used in tests
+ PrivateKey []byte `json:"secretKey,omitempty"`
+}
+
+type accountMarshaling struct {
+ Code hexutil.Bytes
+ Balance *math.HexOrDecimal256
+ Nonce math.HexOrDecimal64
+ Storage map[storageJSON]storageJSON
+ PrivateKey hexutil.Bytes
+}
+
+// storageJSON represents a 256 bit byte array, but allows less than 256 bits when
+// unmarshaling from hex.
+type storageJSON common.Hash
+
+func (h *storageJSON) UnmarshalText(text []byte) error {
+ text = bytes.TrimPrefix(text, []byte("0x"))
+ if len(text) > 64 {
+ return fmt.Errorf("too many hex characters in storage key/value %q", text)
+ }
+ offset := len(h) - len(text)/2 // pad on the left
+ if _, err := hex.Decode(h[offset:], text); err != nil {
+ return fmt.Errorf("invalid hex storage key/value %q", text)
+ }
+ return nil
+}
+
+func (h storageJSON) MarshalText() ([]byte, error) {
+ return hexutil.Bytes(h[:]).MarshalText()
+}
+
+// GenesisAlloc specifies the initial state of a genesis block.
+type GenesisAlloc map[common.Address]Account
+
+func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error {
+ m := make(map[common.UnprefixedAddress]Account)
+ if err := json.Unmarshal(data, &m); err != nil {
+ return err
+ }
+ *ga = make(GenesisAlloc)
+ for addr, a := range m {
+ (*ga)[common.Address(addr)] = a
+ }
+ return nil
+}
diff --git a/core/gen_genesis_account.go b/core/types/gen_account.go
similarity index 61%
rename from core/gen_genesis_account.go
rename to core/types/gen_account.go
index a9d47e6ba3..4e475896a7 100644
--- a/core/gen_genesis_account.go
+++ b/core/types/gen_account.go
@@ -1,6 +1,6 @@
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
-package core
+package types
import (
"encoding/json"
@@ -12,62 +12,62 @@ import (
"github.com/ethereum/go-ethereum/common/math"
)
-var _ = (*genesisAccountMarshaling)(nil)
+var _ = (*accountMarshaling)(nil)
// MarshalJSON marshals as JSON.
-func (g GenesisAccount) MarshalJSON() ([]byte, error) {
- type GenesisAccount struct {
+func (a Account) MarshalJSON() ([]byte, error) {
+ type Account struct {
Code hexutil.Bytes `json:"code,omitempty"`
Storage map[storageJSON]storageJSON `json:"storage,omitempty"`
Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"`
Nonce math.HexOrDecimal64 `json:"nonce,omitempty"`
PrivateKey hexutil.Bytes `json:"secretKey,omitempty"`
}
- var enc GenesisAccount
- enc.Code = g.Code
- if g.Storage != nil {
- enc.Storage = make(map[storageJSON]storageJSON, len(g.Storage))
- for k, v := range g.Storage {
+ var enc Account
+ enc.Code = a.Code
+ if a.Storage != nil {
+ enc.Storage = make(map[storageJSON]storageJSON, len(a.Storage))
+ for k, v := range a.Storage {
enc.Storage[storageJSON(k)] = storageJSON(v)
}
}
- enc.Balance = (*math.HexOrDecimal256)(g.Balance)
- enc.Nonce = math.HexOrDecimal64(g.Nonce)
- enc.PrivateKey = g.PrivateKey
+ enc.Balance = (*math.HexOrDecimal256)(a.Balance)
+ enc.Nonce = math.HexOrDecimal64(a.Nonce)
+ enc.PrivateKey = a.PrivateKey
return json.Marshal(&enc)
}
// UnmarshalJSON unmarshals from JSON.
-func (g *GenesisAccount) UnmarshalJSON(input []byte) error {
- type GenesisAccount struct {
+func (a *Account) UnmarshalJSON(input []byte) error {
+ type Account struct {
Code *hexutil.Bytes `json:"code,omitempty"`
Storage map[storageJSON]storageJSON `json:"storage,omitempty"`
Balance *math.HexOrDecimal256 `json:"balance" gencodec:"required"`
Nonce *math.HexOrDecimal64 `json:"nonce,omitempty"`
PrivateKey *hexutil.Bytes `json:"secretKey,omitempty"`
}
- var dec GenesisAccount
+ var dec Account
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.Code != nil {
- g.Code = *dec.Code
+ a.Code = *dec.Code
}
if dec.Storage != nil {
- g.Storage = make(map[common.Hash]common.Hash, len(dec.Storage))
+ a.Storage = make(map[common.Hash]common.Hash, len(dec.Storage))
for k, v := range dec.Storage {
- g.Storage[common.Hash(k)] = common.Hash(v)
+ a.Storage[common.Hash(k)] = common.Hash(v)
}
}
if dec.Balance == nil {
- return errors.New("missing required field 'balance' for GenesisAccount")
+ return errors.New("missing required field 'balance' for Account")
}
- g.Balance = (*big.Int)(dec.Balance)
+ a.Balance = (*big.Int)(dec.Balance)
if dec.Nonce != nil {
- g.Nonce = uint64(*dec.Nonce)
+ a.Nonce = uint64(*dec.Nonce)
}
if dec.PrivateKey != nil {
- g.PrivateKey = *dec.PrivateKey
+ a.PrivateKey = *dec.PrivateKey
}
return nil
}
diff --git a/core/types/gen_account_rlp.go b/core/types/gen_account_rlp.go
index 951632fb58..3d2f67ab0f 100644
--- a/core/types/gen_account_rlp.go
+++ b/core/types/gen_account_rlp.go
@@ -15,10 +15,7 @@ func (obj *StateAccount) EncodeRLP(_w io.Writer) error {
if obj.Balance == nil {
w.Write(rlp.EmptyString)
} else {
- if obj.Balance.Sign() == -1 {
- return rlp.ErrNegativeBigInt
- }
- w.WriteBigInt(obj.Balance)
+ w.WriteUint256(obj.Balance)
}
w.WriteBytes(obj.Root[:])
w.WriteBytes(obj.CodeHash)
diff --git a/core/types/hashing_test.go b/core/types/hashing_test.go
index 230ac97ebf..af2f72a8d5 100644
--- a/core/types/hashing_test.go
+++ b/core/types/hashing_test.go
@@ -37,6 +37,7 @@ import (
"github.com/ava-labs/subnet-evm/core/rawdb"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
@@ -49,7 +50,7 @@ func TestDeriveSha(t *testing.T) {
t.Fatal(err)
}
for len(txs) < 1000 {
- exp := types.DeriveSha(txs, trie.NewEmpty(trie.NewDatabase(rawdb.NewMemoryDatabase(), nil)))
+ exp := types.DeriveSha(txs, trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)))
got := types.DeriveSha(txs, trie.NewStackTrie(nil))
if !bytes.Equal(got[:], exp[:]) {
t.Fatalf("%d txs: got %x exp %x", len(txs), got, exp)
@@ -96,7 +97,7 @@ func BenchmarkDeriveSha200(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
- exp = types.DeriveSha(txs, trie.NewEmpty(trie.NewDatabase(rawdb.NewMemoryDatabase(), nil)))
+ exp = types.DeriveSha(txs, trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)))
}
})
@@ -117,7 +118,7 @@ func TestFuzzDeriveSha(t *testing.T) {
rndSeed := mrand.Int()
for i := 0; i < 10; i++ {
seed := rndSeed + i
- exp := types.DeriveSha(newDummy(i), trie.NewEmpty(trie.NewDatabase(rawdb.NewMemoryDatabase(), nil)))
+ exp := types.DeriveSha(newDummy(i), trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)))
got := types.DeriveSha(newDummy(i), trie.NewStackTrie(nil))
if !bytes.Equal(got[:], exp[:]) {
printList(newDummy(seed))
@@ -145,7 +146,7 @@ func TestDerivableList(t *testing.T) {
},
}
for i, tc := range tcs[1:] {
- exp := types.DeriveSha(flatList(tc), trie.NewEmpty(trie.NewDatabase(rawdb.NewMemoryDatabase(), nil)))
+ exp := types.DeriveSha(flatList(tc), trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)))
got := types.DeriveSha(flatList(tc), trie.NewStackTrie(nil))
if !bytes.Equal(got[:], exp[:]) {
t.Fatalf("case %d: got %x exp %x", i, got, exp)
diff --git a/core/types/state_account.go b/core/types/state_account.go
index 16dc9c2795..96c270a944 100644
--- a/core/types/state_account.go
+++ b/core/types/state_account.go
@@ -28,10 +28,10 @@ package types
import (
"bytes"
- "math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/holiman/uint256"
)
//go:generate go run github.com/ethereum/go-ethereum/rlp/rlpgen -type StateAccount -out gen_account_rlp.go
@@ -40,7 +40,7 @@ import (
// These objects are stored in the main account trie.
type StateAccount struct {
Nonce uint64
- Balance *big.Int
+ Balance *uint256.Int
Root common.Hash // merkle root of the storage trie
CodeHash []byte
}
@@ -48,7 +48,7 @@ type StateAccount struct {
// NewEmptyStateAccount constructs an empty state account.
func NewEmptyStateAccount() *StateAccount {
return &StateAccount{
- Balance: new(big.Int),
+ Balance: new(uint256.Int),
Root: EmptyRootHash,
CodeHash: EmptyCodeHash.Bytes(),
}
@@ -56,9 +56,9 @@ func NewEmptyStateAccount() *StateAccount {
// Copy returns a deep-copied state account object.
func (acct *StateAccount) Copy() *StateAccount {
- var balance *big.Int
+ var balance *uint256.Int
if acct.Balance != nil {
- balance = new(big.Int).Set(acct.Balance)
+ balance = new(uint256.Int).Set(acct.Balance)
}
return &StateAccount{
Nonce: acct.Nonce,
@@ -73,7 +73,7 @@ func (acct *StateAccount) Copy() *StateAccount {
// or slim format which replaces the empty root and code hash as nil byte slice.
type SlimAccount struct {
Nonce uint64
- Balance *big.Int
+ Balance *uint256.Int
Root []byte // Nil if root equals to types.EmptyRootHash
CodeHash []byte // Nil if hash equals to types.EmptyCodeHash
}
diff --git a/core/types/transaction.go b/core/types/transaction.go
index bf33165a77..35602becae 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -29,6 +29,7 @@ package types
import (
"bytes"
"errors"
+ "fmt"
"io"
"math/big"
"sync/atomic"
@@ -330,6 +331,7 @@ func (tx *Transaction) Cost() *big.Int {
// RawSignatureValues returns the V, R, S signature values of the transaction.
// The return values should not be modified by the caller.
+// The return values may be nil or zero, if the transaction is unsigned.
func (tx *Transaction) RawSignatureValues() (v, r, s *big.Int) {
return tx.inner.rawSignatureValues()
}
@@ -518,6 +520,9 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e
if err != nil {
return nil, err
}
+ if r == nil || s == nil || v == nil {
+ return nil, fmt.Errorf("%w: r: %s, s: %s, v: %s", ErrInvalidSig, r, s, v)
+ }
cpy := tx.inner.copy()
cpy.setSignatureValues(signer.ChainID(), v, r, s)
return &Transaction{inner: cpy, time: tx.time}, nil
diff --git a/core/types/transaction_marshalling.go b/core/types/transaction_marshalling.go
index c1ebc8b514..3839efdc63 100644
--- a/core/types/transaction_marshalling.go
+++ b/core/types/transaction_marshalling.go
@@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/holiman/uint256"
)
@@ -57,6 +58,11 @@ type txJSON struct {
S *hexutil.Big `json:"s"`
YParity *hexutil.Uint64 `json:"yParity,omitempty"`
+ // Blob transaction sidecar encoding:
+ Blobs []kzg4844.Blob `json:"blobs,omitempty"`
+ Commitments []kzg4844.Commitment `json:"commitments,omitempty"`
+ Proofs []kzg4844.Proof `json:"proofs,omitempty"`
+
// Only used for encoding:
Hash common.Hash `json:"hash"`
}
@@ -152,6 +158,11 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
enc.S = (*hexutil.Big)(itx.S.ToBig())
yparity := itx.V.Uint64()
enc.YParity = (*hexutil.Uint64)(&yparity)
+ if sidecar := itx.Sidecar; sidecar != nil {
+ enc.Blobs = itx.Sidecar.Blobs
+ enc.Commitments = itx.Sidecar.Commitments
+ enc.Proofs = itx.Sidecar.Proofs
+ }
}
return json.Marshal(&enc)
}
diff --git a/core/types/transaction_signing_test.go b/core/types/transaction_signing_test.go
index 0976f59751..d717e6f717 100644
--- a/core/types/transaction_signing_test.go
+++ b/core/types/transaction_signing_test.go
@@ -28,9 +28,11 @@ package types
import (
"errors"
+ "fmt"
"math/big"
"testing"
+ "github.com/ava-labs/subnet-evm/params"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
@@ -51,7 +53,7 @@ func TestEIP155Signing(t *testing.T) {
t.Fatal(err)
}
if from != addr {
- t.Errorf("exected from and address to be equal. Got %x want %x", from, addr)
+ t.Errorf("expected from and address to be equal. Got %x want %x", from, addr)
}
}
@@ -149,3 +151,53 @@ func TestChainId(t *testing.T) {
t.Error("expected no error")
}
}
+
+type nilSigner struct {
+ v, r, s *big.Int
+ Signer
+}
+
+func (ns *nilSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) {
+ return ns.v, ns.r, ns.s, nil
+}
+
+// TestNilSigner ensures a faulty Signer implementation does not result in nil signature values or panics.
+func TestNilSigner(t *testing.T) {
+ key, _ := crypto.GenerateKey()
+ innerSigner := LatestSignerForChainID(big.NewInt(1))
+ for i, signer := range []Signer{
+ &nilSigner{v: nil, r: nil, s: nil, Signer: innerSigner},
+ &nilSigner{v: big.NewInt(1), r: big.NewInt(1), s: nil, Signer: innerSigner},
+ &nilSigner{v: big.NewInt(1), r: nil, s: big.NewInt(1), Signer: innerSigner},
+ &nilSigner{v: nil, r: big.NewInt(1), s: big.NewInt(1), Signer: innerSigner},
+ } {
+ t.Run(fmt.Sprintf("signer_%d", i), func(t *testing.T) {
+ t.Run("legacy", func(t *testing.T) {
+ legacyTx := createTestLegacyTxInner()
+ _, err := SignNewTx(key, signer, legacyTx)
+ if !errors.Is(err, ErrInvalidSig) {
+ t.Fatal("expected signature values error, no nil result or panic")
+ }
+ })
+ // test Blob tx specifically, since the signature value types changed
+ t.Run("blobtx", func(t *testing.T) {
+ blobtx := createEmptyBlobTxInner(false)
+ _, err := SignNewTx(key, signer, blobtx)
+ if !errors.Is(err, ErrInvalidSig) {
+ t.Fatal("expected signature values error, no nil result or panic")
+ }
+ })
+ })
+ }
+}
+
+func createTestLegacyTxInner() *LegacyTx {
+ return &LegacyTx{
+ Nonce: uint64(0),
+ To: nil,
+ Value: big.NewInt(0),
+ Gas: params.TxGas,
+ GasPrice: big.NewInt(params.GWei),
+ Data: nil,
+ }
+}
diff --git a/core/types/tx_blob.go b/core/types/tx_blob.go
index d91845ff57..3e6872ae78 100644
--- a/core/types/tx_blob.go
+++ b/core/types/tx_blob.go
@@ -43,7 +43,7 @@ type BlobTx struct {
BlobHashes []common.Hash
// A blob transaction can optionally contain blobs. This field must be set when BlobTx
- // is used to create a transaction for sigining.
+ // is used to create a transaction for signing.
Sidecar *BlobTxSidecar `rlp:"-"`
// Signature values
@@ -61,9 +61,10 @@ type BlobTxSidecar struct {
// BlobHashes computes the blob hashes of the given blobs.
func (sc *BlobTxSidecar) BlobHashes() []common.Hash {
+ hasher := sha256.New()
h := make([]common.Hash, len(sc.Commitments))
for i := range sc.Blobs {
- h[i] = blobHash(&sc.Commitments[i])
+ h[i] = kzg4844.CalcBlobHashV1(hasher, &sc.Commitments[i])
}
return h
}
@@ -235,12 +236,3 @@ func (tx *BlobTx) decode(input []byte) error {
}
return nil
}
-
-func blobHash(commit *kzg4844.Commitment) common.Hash {
- hasher := sha256.New()
- hasher.Write(commit[:])
- var vhash common.Hash
- hasher.Sum(vhash[:0])
- vhash[0] = params.BlobTxHashVersion
- return vhash
-}
diff --git a/core/types/tx_blob_test.go b/core/types/tx_blob_test.go
index 44ac48cc6f..25d09e31ce 100644
--- a/core/types/tx_blob_test.go
+++ b/core/types/tx_blob_test.go
@@ -65,6 +65,12 @@ var (
)
func createEmptyBlobTx(key *ecdsa.PrivateKey, withSidecar bool) *Transaction {
+ blobtx := createEmptyBlobTxInner(withSidecar)
+ signer := NewCancunSigner(blobtx.ChainID.ToBig())
+ return MustSignNewTx(key, signer, blobtx)
+}
+
+func createEmptyBlobTxInner(withSidecar bool) *BlobTx {
sidecar := &BlobTxSidecar{
Blobs: []kzg4844.Blob{emptyBlob},
Commitments: []kzg4844.Commitment{emptyBlobCommit},
@@ -85,6 +91,5 @@ func createEmptyBlobTx(key *ecdsa.PrivateKey, withSidecar bool) *Transaction {
if withSidecar {
blobtx.Sidecar = sidecar
}
- signer := NewCancunSigner(blobtx.ChainID.ToBig())
- return MustSignNewTx(key, signer, blobtx)
+ return blobtx
}
diff --git a/core/vm/contract.go b/core/vm/contract.go
index ab541ccb0e..129b208eb7 100644
--- a/core/vm/contract.go
+++ b/core/vm/contract.go
@@ -27,8 +27,6 @@
package vm
import (
- "math/big"
-
"github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
)
@@ -69,11 +67,11 @@ type Contract struct {
Input []byte
Gas uint64
- value *big.Int
+ value *uint256.Int
}
// NewContract returns a new contract environment for the execution of EVM.
-func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract {
+func NewContract(caller ContractRef, object ContractRef, value *uint256.Int, gas uint64) *Contract {
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object}
if parent, ok := caller.(*Contract); ok {
@@ -183,7 +181,7 @@ func (c *Contract) Address() common.Address {
}
// Value returns the contract's value (sent to it from it's caller)
-func (c *Contract) Value() *big.Int {
+func (c *Contract) Value() *uint256.Int {
return c.value
}
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index 4eacdd2456..f4d35b5ab6 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -306,7 +306,6 @@ type bigModExp struct {
}
var (
- big0 = big.NewInt(0)
big1 = big.NewInt(1)
big3 = big.NewInt(3)
big4 = big.NewInt(4)
diff --git a/core/vm/contracts_test.go b/core/vm/contracts_test.go
index 8608fdf36d..80a2c33d18 100644
--- a/core/vm/contracts_test.go
+++ b/core/vm/contracts_test.go
@@ -233,7 +233,7 @@ func BenchmarkPrecompiledRipeMD(bench *testing.B) {
benchmarkPrecompiled("03", t, bench)
}
-// Benchmarks the sample inputs from the identiy precompile.
+// Benchmarks the sample inputs from the identity precompile.
func BenchmarkPrecompiledIdentity(bench *testing.B) {
t := precompiledTest{
Input: "38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e000000000000000000000000000000000000000000000000000000000000001b38d18acb67d25c8bb9942764b62f18e17054f66a817bd4295423adf9ed98873e789d1dd423d25f0772d2748d60f7e4b81bb14d086eba8e8e8efb6dcff8a4ae02",
diff --git a/core/vm/eips.go b/core/vm/eips.go
index 655bbb9fd3..80cd4e0ef0 100644
--- a/core/vm/eips.go
+++ b/core/vm/eips.go
@@ -95,7 +95,7 @@ func enable1884(jt *JumpTable) {
}
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(scope.Contract.Address()))
+ balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
scope.Stack.push(balance)
return nil, nil
}
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 7a95719c9e..90a9ff4c9f 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -63,9 +63,9 @@ func IsProhibited(addr common.Address) bool {
type (
// CanTransferFunc is the signature of a transfer guard function
- CanTransferFunc func(StateDB, common.Address, *big.Int) bool
+ CanTransferFunc func(StateDB, common.Address, *uint256.Int) bool
// TransferFunc is the signature of a transfer function
- TransferFunc func(StateDB, common.Address, common.Address, *big.Int)
+ TransferFunc func(StateDB, common.Address, common.Address, *uint256.Int)
// GetHashFunc returns the n'th block hash in the blockchain
// and is used by the BLOCKHASH EVM op code.
GetHashFunc func(uint64) common.Hash
@@ -254,16 +254,13 @@ func (evm *EVM) Interpreter() *EVMInterpreter {
// parameters. It also handles any necessary value transfer required and takes
// the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer.
-func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
+func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, vmerrs.ErrDepth
}
// Fail if we're trying to transfer more than the available balance
- // Note: it is not possible for a negative value to be passed in here due to the fact
- // that [value] will be popped from the stack and decoded to a *big.Int, which will
- // always yield a positive result.
- if value.Sign() != 0 && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
+ if !value.IsZero() && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, gas, vmerrs.ErrInsufficientBalance
}
snapshot := evm.StateDB.Snapshot()
@@ -271,14 +268,14 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
debug := evm.Config.Tracer != nil
if !evm.StateDB.Exist(addr) {
- if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
+ if !isPrecompile && evm.chainRules.IsEIP158 && value.IsZero() {
// Calling a non existing account, don't do anything, but ping the tracer
if debug {
if evm.depth == 0 {
- evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
+ evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value.ToBig())
evm.Config.Tracer.CaptureEnd(ret, 0, nil)
} else {
- evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
+ evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value.ToBig())
evm.Config.Tracer.CaptureExit(ret, 0, nil)
}
}
@@ -291,13 +288,13 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// Capture the tracer start/end events in debug mode
if debug {
if evm.depth == 0 {
- evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
+ evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value.ToBig())
defer func(startGas uint64) { // Lazy evaluation of the parameters
evm.Config.Tracer.CaptureEnd(ret, startGas-gas, err)
}(gas)
} else {
// Handle tracer events for entering and exiting a call frame
- evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
+ evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value.ToBig())
defer func(startGas uint64) {
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
@@ -344,7 +341,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
//
// CallCode differs from Call in the sense that it executes the given address'
// code with the caller as context.
-func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
+func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int) (ret []byte, leftOverGas uint64, err error) {
// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, vmerrs.ErrDepth
@@ -363,7 +360,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil {
- evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
+ evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value.ToBig())
defer func(startGas uint64) {
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
@@ -408,7 +405,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
// that caller is something other than a Contract.
parent := caller.(*Contract)
// DELEGATECALL inherits value from parent call
- evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value)
+ evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value.ToBig())
defer func(startGas uint64) {
evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
}(gas)
@@ -454,7 +451,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
// but is the correct thing to do and matters on other networks, in tests, and potential
// future scenarios
- evm.StateDB.AddBalance(addr, big0)
+ evm.StateDB.AddBalance(addr, new(uint256.Int))
// Invoke tracer hooks that signal entering/exiting a call frame
if evm.Config.Tracer != nil {
@@ -473,7 +470,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
addrCopy := addr
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
- contract := NewContract(caller, AccountRef(addrCopy), new(big.Int), gas)
+ contract := NewContract(caller, AccountRef(addrCopy), new(uint256.Int), gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
@@ -503,7 +500,7 @@ func (c *codeAndHash) Hash() common.Hash {
}
// create creates a new contract using code as deployment code.
-func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) {
+func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) {
// Depth check execution. Fail if we're trying to execute above the
// limit.
if evm.depth > int(params.CallCreateDepth) {
@@ -558,9 +555,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if evm.Config.Tracer != nil {
if evm.depth == 0 {
- evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
+ evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value.ToBig())
} else {
- evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
+ evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value.ToBig())
}
}
@@ -610,7 +607,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
// Create creates a new contract using code as deployment code.
-func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
+func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE)
}
@@ -619,7 +616,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
//
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
-func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
+func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
codeAndHash := &codeAndHash{code: code}
contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)
diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go
index 0102f71ec3..eaaaf5d36b 100644
--- a/core/vm/gas_table_test.go
+++ b/core/vm/gas_table_test.go
@@ -40,6 +40,7 @@ import (
"github.com/ava-labs/subnet-evm/vmerrs"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/holiman/uint256"
)
func TestMemoryGasCost(t *testing.T) {
@@ -102,12 +103,12 @@ func TestEIP2200(t *testing.T) {
statedb.Finalise(true) // Push the state into the "original" slot
vmctx := BlockContext{
- CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true },
- Transfer: func(StateDB, common.Address, common.Address, *big.Int) {},
+ CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true },
+ Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
}
vmenv := NewEVM(vmctx, TxContext{}, statedb, params.TestChainConfig, Config{ExtraEips: []int{2200}})
- _, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(big.Int))
+ _, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(uint256.Int))
if err != tt.failure {
t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure)
}
@@ -152,8 +153,8 @@ func TestCreateGas(t *testing.T) {
statedb.SetCode(address, hexutil.MustDecode(tt.code))
statedb.Finalise(true)
vmctx := BlockContext{
- CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true },
- Transfer: func(StateDB, common.Address, common.Address, *big.Int) {},
+ CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true },
+ Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
BlockNumber: big.NewInt(0),
}
config := Config{}
@@ -165,7 +166,7 @@ func TestCreateGas(t *testing.T) {
// because it is the last fork before the activation of EIP-3860
vmenv := NewEVM(vmctx, TxContext{}, statedb, params.TestSubnetEVMChainConfig, config)
var startGas = uint64(testGas)
- ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(big.Int))
+ ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(uint256.Int))
if err != nil {
return false
}
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 65f659aa32..eac495c0e9 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -27,6 +27,8 @@
package vm
import (
+ "math"
+
"github.com/ava-labs/subnet-evm/params"
"github.com/ava-labs/subnet-evm/vmerrs"
"github.com/ethereum/go-ethereum/common"
@@ -270,7 +272,7 @@ func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := scope.Stack.peek()
address := common.Address(slot.Bytes20())
- slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address))
+ slot.Set(interpreter.evm.StateDB.GetBalance(address))
return nil, nil
}
@@ -285,8 +287,7 @@ func opCaller(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
}
func opCallValue(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- v, _ := uint256.FromBig(scope.Contract.value)
- scope.Stack.push(v)
+ scope.Stack.push(scope.Contract.value)
return nil, nil
}
@@ -358,9 +359,7 @@ func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
}
func opCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
- l := new(uint256.Int)
- l.SetUint64(uint64(len(scope.Contract.Code)))
- scope.Stack.push(l)
+ scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Code))))
return nil, nil
}
@@ -372,7 +371,7 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
)
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
if overflow {
- uint64CodeOffset = 0xffffffffffffffff
+ uint64CodeOffset = math.MaxUint64
}
codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64())
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
@@ -390,7 +389,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
)
uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
if overflow {
- uint64CodeOffset = 0xffffffffffffffff
+ uint64CodeOffset = math.MaxUint64
}
addr := common.Address(a.Bytes20())
codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64())
@@ -596,13 +595,8 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
stackvalue := size
scope.Contract.UseGas(gas)
- //TODO: use uint256.Int instead of converting with toBig()
- var bigVal = big0
- if !value.IsZero() {
- bigVal = value.ToBig()
- }
- res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, bigVal)
+ res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, &value)
// Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must
@@ -641,13 +635,8 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
scope.Contract.UseGas(gas)
// reuse size int for stackvalue
stackvalue := size
- //TODO: use uint256.Int instead of converting with toBig()
- bigEndowment := big0
- if !endowment.IsZero() {
- bigEndowment = endowment.ToBig()
- }
res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas,
- bigEndowment, &salt)
+ &endowment, &salt)
// Push item on the stack based on the returned error.
if suberr != nil {
stackvalue.Clear()
@@ -680,16 +669,11 @@ func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
if interpreter.readOnly && !value.IsZero() {
return nil, vmerrs.ErrWriteProtection
}
- var bigVal = big0
- //TODO: use uint256.Int instead of converting with toBig()
- // By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls),
- // but it would make more sense to extend the usage of uint256.Int
if !value.IsZero() {
gas += params.CallStipend
- bigVal = value.ToBig()
}
+ ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, &value)
- ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, bigVal)
if err != nil {
temp.Clear()
} else {
@@ -717,14 +701,11 @@ func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
// Get arguments from the memory.
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
- //TODO: use uint256.Int instead of converting with toBig()
- var bigVal = big0
if !value.IsZero() {
gas += params.CallStipend
- bigVal = value.ToBig()
}
- ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, bigVal)
+ ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, &value)
if err != nil {
temp.Clear()
} else {
@@ -828,7 +809,7 @@ func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance)
interpreter.evm.StateDB.SelfDestruct(scope.Contract.Address())
if tracer := interpreter.evm.Config.Tracer; tracer != nil {
- tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
+ tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig())
tracer.CaptureExit([]byte{}, 0, nil)
}
return nil, errStopToken
@@ -844,7 +825,7 @@ func opSelfdestruct6780(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCon
interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance)
interpreter.evm.StateDB.Selfdestruct6780(scope.Contract.Address())
if tracer := interpreter.evm.Config.Tracer; tracer != nil {
- tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
+ tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance.ToBig())
tracer.CaptureExit([]byte{}, 0, nil)
}
return nil, errStopToken
diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go
index ce36b18bc8..ec7be55bd7 100644
--- a/core/vm/instructions_test.go
+++ b/core/vm/instructions_test.go
@@ -601,7 +601,7 @@ func TestOpTstore(t *testing.T) {
caller = common.Address{}
to = common.Address{1}
contractRef = contractRef{caller}
- contract = NewContract(contractRef, AccountRef(to), new(big.Int), 0)
+ contract = NewContract(contractRef, AccountRef(to), new(uint256.Int), 0)
scopeContext = ScopeContext{mem, stack, contract}
value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700")
)
diff --git a/core/vm/interface.go b/core/vm/interface.go
index 34b3e714da..e7355110b9 100644
--- a/core/vm/interface.go
+++ b/core/vm/interface.go
@@ -32,15 +32,16 @@ import (
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/params"
"github.com/ethereum/go-ethereum/common"
+ "github.com/holiman/uint256"
)
// StateDB is an EVM database for full state querying.
type StateDB interface {
CreateAccount(common.Address)
- SubBalance(common.Address, *big.Int)
- AddBalance(common.Address, *big.Int)
- GetBalance(common.Address) *big.Int
+ SubBalance(common.Address, *uint256.Int)
+ AddBalance(common.Address, *uint256.Int)
+ GetBalance(common.Address) *uint256.Int
GetNonce(common.Address) uint64
SetNonce(common.Address, uint64)
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index 34eb46425f..d79c25dcb5 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -157,7 +157,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
)
// Don't move this deferred function, it's placed before the capturestate-deferred method,
- // so that it get's executed _after_: the capturestate needs the stacks before
+ // so that it gets executed _after_: the capturestate needs the stacks before
// they are returned to the pools
defer func() {
returnStack(stack)
diff --git a/core/vm/interpreter_test.go b/core/vm/interpreter_test.go
index 8b40bfcc84..f1eb8f72e7 100644
--- a/core/vm/interpreter_test.go
+++ b/core/vm/interpreter_test.go
@@ -27,7 +27,6 @@
package vm
import (
- "math/big"
"testing"
"time"
@@ -37,6 +36,7 @@ import (
"github.com/ava-labs/subnet-evm/params"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
+ "github.com/holiman/uint256"
)
var loopInterruptTests = []string{
@@ -49,7 +49,7 @@ var loopInterruptTests = []string{
func TestLoopInterrupt(t *testing.T) {
address := common.BytesToAddress([]byte("contract"))
vmctx := BlockContext{
- Transfer: func(StateDB, common.Address, common.Address, *big.Int) {},
+ Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
}
for i, tt := range loopInterruptTests {
@@ -64,7 +64,7 @@ func TestLoopInterrupt(t *testing.T) {
timeout := make(chan bool)
go func(evm *EVM) {
- _, _, err := evm.Call(AccountRef(common.Address{}), address, nil, math.MaxUint64, new(big.Int))
+ _, _, err := evm.Call(AccountRef(common.Address{}), address, nil, math.MaxUint64, new(uint256.Int))
errChannel <- err
}(evm)
diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go
index a97e274ccd..caa5d721e7 100644
--- a/core/vm/jump_table.go
+++ b/core/vm/jump_table.go
@@ -112,7 +112,7 @@ func newDurangoInstructionSet() JumpTable {
// constantinople, istanbul, petersburg, subnet-evm instructions.
func newSubnetEVMInstructionSet() JumpTable {
instructionSet := newIstanbulInstructionSet()
- enable2929(&instructionSet)
+ enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929
enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
return validate(instructionSet)
}
diff --git a/core/vm/jump_table_test.go b/core/vm/jump_table_test.go
index 6e838337c1..5fa9a532cb 100644
--- a/core/vm/jump_table_test.go
+++ b/core/vm/jump_table_test.go
@@ -32,7 +32,7 @@ import (
"github.com/stretchr/testify/require"
)
-// TestJumpTableCopy tests that deep copy is necessery to prevent modify shared jump table
+// TestJumpTableCopy tests that deep copy is necessary to prevent modify shared jump table
func TestJumpTableCopy(t *testing.T) {
tbl := newDurangoInstructionSet()
require.Equal(t, uint64(0), tbl[SLOAD].constantGas)
diff --git a/core/vm/operations_acl.go b/core/vm/operations_acl.go
index 7d8aec3741..ae952d0ff9 100644
--- a/core/vm/operations_acl.go
+++ b/core/vm/operations_acl.go
@@ -175,7 +175,12 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
// outside of this function, as part of the dynamic gas, and that will make it
// also become correctly reported to tracers.
contract.Gas += coldCost
- return gas + coldCost, nil
+
+ var overflow bool
+ if gas, overflow = math.SafeAdd(gas, coldCost); overflow {
+ return 0, vmerrs.ErrGasUintOverflow
+ }
+ return gas, nil
}
}
@@ -185,7 +190,7 @@ var (
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall)
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode)
gasSelfdestructEIP2929 = makeSelfdestructGasFn(false) // Note: refunds were never enabled on Avalanche
- // gasSelfdestructEIP3529 implements the changes in EIP-2539 (no refunds)
+ // gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds)
gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929
//
@@ -202,7 +207,7 @@ var (
gasSStoreEIP2929 = makeGasSStoreFunc()
)
-// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-2539
+// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-3529
func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
index 52668a5c2c..52a3b04b01 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -37,6 +37,7 @@ import (
"github.com/ava-labs/subnet-evm/params"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/holiman/uint256"
)
// Config is a basic type specifying certain configuration flags for running
@@ -145,7 +146,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
common.BytesToAddress([]byte("contract")),
input,
cfg.GasLimit,
- cfg.Value,
+ uint256.MustFromBig(cfg.Value),
)
return ret, cfg.State, err
}
@@ -175,7 +176,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) {
sender,
input,
cfg.GasLimit,
- cfg.Value,
+ uint256.MustFromBig(cfg.Value),
)
return code, address, leftOverGas, err
}
@@ -190,7 +191,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
var (
vmenv = NewEnv(cfg)
- sender = cfg.State.GetOrNewStateObject(cfg.Origin)
+ sender = vm.AccountRef(cfg.Origin)
statedb = cfg.State
rules = cfg.ChainConfig.Rules(vmenv.Context.BlockNumber, vmenv.Context.Time)
)
@@ -205,7 +206,7 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, uint64, er
address,
input,
cfg.GasLimit,
- cfg.Value,
+ uint256.MustFromBig(cfg.Value),
)
return ret, leftOverGas, err
}
diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go
index 0ee9411f18..65127f24ef 100644
--- a/core/vm/runtime/runtime_test.go
+++ b/core/vm/runtime/runtime_test.go
@@ -48,6 +48,7 @@ import (
// force-load js tracers to trigger registration
_ "github.com/ava-labs/subnet-evm/eth/tracers/js"
+ "github.com/holiman/uint256"
)
func TestDefaults(t *testing.T) {
@@ -370,12 +371,12 @@ func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode
//cfg.State.CreateAccount(cfg.Origin)
// set the receiver's (the executing contract) code for execution.
cfg.State.SetCode(destination, code)
- vmenv.Call(sender, destination, nil, gas, cfg.Value)
+ vmenv.Call(sender, destination, nil, gas, uint256.MustFromBig(cfg.Value))
b.Run(name, func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
- vmenv.Call(sender, destination, nil, gas, cfg.Value)
+ vmenv.Call(sender, destination, nil, gas, uint256.MustFromBig(cfg.Value))
}
})
}
diff --git a/eth/api_backend.go b/eth/api_backend.go
index 92aab3fbb6..e3cba7bf92 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -38,8 +38,8 @@ import (
"github.com/ava-labs/subnet-evm/consensus/dummy"
"github.com/ava-labs/subnet-evm/core"
"github.com/ava-labs/subnet-evm/core/bloombits"
- "github.com/ava-labs/subnet-evm/core/rawdb"
"github.com/ava-labs/subnet-evm/core/state"
+ "github.com/ava-labs/subnet-evm/core/txpool"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/core/vm"
"github.com/ava-labs/subnet-evm/eth/gasprice"
@@ -299,7 +299,7 @@ func (b *EthAPIBackend) GetEVM(ctx context.Context, msg *core.Message, state *st
} else {
context = core.NewEVMBlockContext(header, b.eth.BlockChain(), nil)
}
- return vm.NewEVM(context, txContext, state, b.eth.blockchain.Config(), *vmConfig)
+ return vm.NewEVM(context, txContext, state, b.ChainConfig(), *vmConfig)
}
func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
@@ -353,7 +353,7 @@ func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction)
}
func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) {
- pending := b.eth.txPool.Pending(false)
+ pending := b.eth.txPool.Pending(txpool.PendingFilter{})
var txs types.Transactions
for _, batch := range pending {
for _, lazy := range batch {
@@ -369,10 +369,14 @@ func (b *EthAPIBackend) GetPoolTransaction(hash common.Hash) *types.Transaction
return b.eth.txPool.Get(hash)
}
-func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
- // Note: we only index transactions during Accept, so the below check against unfinalized queries is technically redundant, but
- // we keep it for defense in depth.
- tx, blockHash, blockNumber, index := rawdb.ReadTransaction(b.eth.ChainDb(), txHash)
+func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) {
+ lookup, tx, err := b.eth.blockchain.GetTransactionLookup(txHash)
+ if err != nil {
+ return false, nil, common.Hash{}, 0, 0, err
+ }
+ if lookup == nil || tx == nil {
+ return false, nil, common.Hash{}, 0, 0, nil
+ }
// Respond as if the transaction does not exist if it is not yet in an
// accepted block. We explicitly choose not to error here to avoid breaking
@@ -380,12 +384,12 @@ func (b *EthAPIBackend) GetTransaction(ctx context.Context, txHash common.Hash)
// does not exist).
acceptedBlock := b.eth.LastAcceptedBlock()
if !b.IsAllowUnfinalizedQueries() && acceptedBlock != nil && tx != nil {
- if blockNumber > acceptedBlock.NumberU64() {
- return nil, common.Hash{}, 0, 0, nil
+ if lookup.BlockIndex > acceptedBlock.NumberU64() {
+ return false, nil, common.Hash{}, 0, 0, nil
}
}
- return tx, blockHash, blockNumber, index, nil
+ return true, tx, lookup.BlockHash, lookup.BlockIndex, lookup.Index, nil
}
func (b *EthAPIBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
diff --git a/eth/api_debug_test.go b/eth/api_debug_test.go
index 4e9591e317..b4b626104c 100644
--- a/eth/api_debug_test.go
+++ b/eth/api_debug_test.go
@@ -29,7 +29,6 @@ package eth
import (
"bytes"
"fmt"
- "math/big"
"reflect"
"strings"
"testing"
@@ -37,7 +36,8 @@ import (
"github.com/ava-labs/subnet-evm/core/rawdb"
"github.com/ava-labs/subnet-evm/core/state"
"github.com/ava-labs/subnet-evm/core/types"
- "github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
+ "github.com/holiman/uint256"
"github.com/davecgh/go-spew/spew"
@@ -76,7 +76,7 @@ func TestAccountRange(t *testing.T) {
t.Parallel()
var (
- statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &trie.Config{Preimages: true})
+ statedb = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &triedb.Config{Preimages: true})
sdb, _ = state.New(types.EmptyRootHash, statedb, nil)
addrs = [AccountRangeMaxResults * 2]common.Address{}
m = map[common.Address]bool{}
@@ -86,7 +86,7 @@ func TestAccountRange(t *testing.T) {
hash := common.HexToHash(fmt.Sprintf("%x", i))
addr := common.BytesToAddress(crypto.Keccak256Hash(hash.Bytes()).Bytes())
addrs[i] = addr
- sdb.SetBalance(addrs[i], big.NewInt(1))
+ sdb.SetBalance(addrs[i], uint256.NewInt(1))
if _, ok := m[addr]; ok {
t.Fatalf("bad")
} else {
@@ -173,7 +173,7 @@ func TestStorageRangeAt(t *testing.T) {
// Create a state where account 0x010000... has a few storage entries.
var (
- db = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &trie.Config{Preimages: true})
+ db = state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), &triedb.Config{Preimages: true})
sdb, _ = state.New(types.EmptyRootHash, db, nil)
addr = common.Address{0x01}
keys = []common.Hash{ // hashes of Keys of storage
diff --git a/eth/backend.go b/eth/backend.go
index 85da1b318f..b92ad99f99 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -37,7 +37,6 @@ import (
"github.com/ava-labs/avalanchego/utils/timer/mockable"
"github.com/ava-labs/subnet-evm/accounts"
"github.com/ava-labs/subnet-evm/consensus"
- "github.com/ava-labs/subnet-evm/consensus/dummy"
"github.com/ava-labs/subnet-evm/core"
"github.com/ava-labs/subnet-evm/core/bloombits"
"github.com/ava-labs/subnet-evm/core/rawdb"
@@ -131,6 +130,7 @@ func New(
chainDb ethdb.Database,
settings Settings,
lastAcceptedHash common.Hash,
+ engine consensus.Engine,
clock *mockable.Clock,
) (*Ethereum, error) {
if chainDb == nil {
@@ -176,7 +176,7 @@ func New(
chainDb: chainDb,
eventMux: new(event.TypeMux),
accountManager: stack.AccountManager(),
- engine: dummy.NewFakerWithClock(clock),
+ engine: engine,
closeBloomHandler: make(chan struct{}),
networkID: networkID,
etherbase: config.Miner.Etherbase,
@@ -253,7 +253,7 @@ func New(
legacyPool := legacypool.New(config.TxPool, eth.blockchain)
- eth.txPool, err = txpool.New(new(big.Int).SetUint64(config.TxPool.PriceLimit), eth.blockchain, []txpool.SubPool{legacyPool}) //, blobPool})
+ eth.txPool, err = txpool.New(config.TxPool.PriceLimit, eth.blockchain, []txpool.SubPool{legacyPool}) //, blobPool})
if err != nil {
return nil, err
}
diff --git a/eth/filters/api.go b/eth/filters/api.go
index eae88bc20f..0748822b97 100644
--- a/eth/filters/api.go
+++ b/eth/filters/api.go
@@ -170,6 +170,8 @@ func (api *FilterAPI) NewPendingTransactions(ctx context.Context, fullTx *bool)
go func() {
txs := make(chan []*types.Transaction, 128)
pendingTxSub := api.events.SubscribePendingTxs(txs)
+ defer pendingTxSub.Unsubscribe()
+
chainConfig := api.sys.backend.ChainConfig()
for {
@@ -187,10 +189,8 @@ func (api *FilterAPI) NewPendingTransactions(ctx context.Context, fullTx *bool)
}
}
case <-rpcSub.Err():
- pendingTxSub.Unsubscribe()
return
case <-notifier.Closed():
- pendingTxSub.Unsubscribe()
return
}
}
@@ -301,16 +301,15 @@ func (api *FilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, error) {
} else {
headersSub = api.events.SubscribeAcceptedHeads(headers)
}
+ defer headersSub.Unsubscribe()
for {
select {
case h := <-headers:
notifier.Notify(rpcSub.ID, h)
case <-rpcSub.Err():
- headersSub.Unsubscribe()
return
case <-notifier.Closed():
- headersSub.Unsubscribe()
return
}
}
@@ -346,6 +345,7 @@ func (api *FilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc.Subsc
}
go func() {
+ defer logsSub.Unsubscribe()
for {
select {
case logs := <-matchedLogs:
@@ -354,10 +354,8 @@ func (api *FilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc.Subsc
notifier.Notify(rpcSub.ID, &log)
}
case <-rpcSub.Err(): // client send an unsubscribe request
- logsSub.Unsubscribe()
return
case <-notifier.Closed(): // connection dropped
- logsSub.Unsubscribe()
return
}
}
diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go
index 6a9c50675e..8965943bf5 100644
--- a/eth/filters/filter_test.go
+++ b/eth/filters/filter_test.go
@@ -42,7 +42,7 @@ import (
"github.com/ava-labs/subnet-evm/core/vm"
"github.com/ava-labs/subnet-evm/params"
"github.com/ava-labs/subnet-evm/rpc"
- "github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
@@ -68,7 +68,7 @@ func BenchmarkFilters(b *testing.B) {
addr4 = common.BytesToAddress([]byte("random addresses please"))
gspec = &core.Genesis{
- Alloc: core.GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}},
+ Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}},
BaseFee: big.NewInt(1),
Config: params.TestChainConfig,
}
@@ -98,7 +98,7 @@ func BenchmarkFilters(b *testing.B) {
// The test txs are not properly signed, can't simply create a chain
// and then import blocks. TODO(rjl493456442) try to get rid of the
// manual database writes.
- gspec.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults))
+ gspec.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults))
for i, block := range chain {
rawdb.WriteBlock(db, block)
@@ -176,7 +176,7 @@ func TestFilters(t *testing.T) {
gspec = &core.Genesis{
Config: params.TestChainConfig,
- Alloc: core.GenesisAlloc{
+ Alloc: types.GenesisAlloc{
addr: {Balance: big.NewInt(0).Mul(big.NewInt(100), big.NewInt(params.Ether))},
contract: {Balance: big.NewInt(0), Code: bytecode},
contract2: {Balance: big.NewInt(0), Code: bytecode},
@@ -192,7 +192,7 @@ func TestFilters(t *testing.T) {
// Hack: GenerateChainWithGenesis creates a new db.
// Commit the genesis manually and use GenerateChain.
- _, err = gspec.Commit(db, trie.NewDatabase(db, nil))
+ _, err = gspec.Commit(db, triedb.NewDatabase(db, nil))
if err != nil {
t.Fatal(err)
}
diff --git a/eth/gasestimator/gasestimator.go b/eth/gasestimator/gasestimator.go
index acc476ff9f..c150bad8cd 100644
--- a/eth/gasestimator/gasestimator.go
+++ b/eth/gasestimator/gasestimator.go
@@ -82,9 +82,9 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin
}
// Recap the highest gas limit with account's available balance.
if feeCap.BitLen() != 0 {
- balance := opts.State.GetBalance(call.From)
+ balance := opts.State.GetBalance(call.From).ToBig()
- available := new(big.Int).Set(balance)
+ available := balance
if call.Value != nil {
if call.Value.Cmp(available) >= 0 {
return 0, nil, core.ErrInsufficientFundsForTransfer
diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go
index 143adac2d4..c85e53308d 100644
--- a/eth/gasprice/feehistory.go
+++ b/eth/gasprice/feehistory.go
@@ -181,8 +181,8 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL
if p < 0 || p > 100 {
return common.Big0, nil, nil, nil, fmt.Errorf("%w: %f", errInvalidPercentile, p)
}
- if i > 0 && p < rewardPercentiles[i-1] {
- return common.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f > #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p)
+ if i > 0 && p <= rewardPercentiles[i-1] {
+ return common.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f >= #%d:%f", errInvalidPercentile, i-1, rewardPercentiles[i-1], i, p)
}
}
lastBlock, blocks, err := oracle.resolveBlockRange(ctx, unresolvedLastBlock, blocks)
diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go
index a41c9576a5..769c968a5a 100644
--- a/eth/gasprice/gasprice_test.go
+++ b/eth/gasprice/gasprice_test.go
@@ -101,7 +101,7 @@ func (b *testBackend) teardown() {
func newTestBackendFakerEngine(t *testing.T, config *params.ChainConfig, numBlocks int, genBlocks func(i int, b *core.BlockGen)) *testBackend {
var gspec = &core.Genesis{
Config: config,
- Alloc: core.GenesisAlloc{addr: core.GenesisAccount{Balance: bal}},
+ Alloc: types.GenesisAlloc{addr: {Balance: bal}},
}
engine := dummy.NewETHFaker()
@@ -128,7 +128,7 @@ func newTestBackendFakerEngine(t *testing.T, config *params.ChainConfig, numBloc
func newTestBackend(t *testing.T, config *params.ChainConfig, numBlocks int, genBlocks func(i int, b *core.BlockGen)) *testBackend {
var gspec = &core.Genesis{
Config: config,
- Alloc: core.GenesisAlloc{addr: core.GenesisAccount{Balance: bal}},
+ Alloc: types.GenesisAlloc{addr: {Balance: bal}},
}
engine := dummy.NewFaker()
diff --git a/eth/state_accessor.go b/eth/state_accessor.go
index aa03d5fdc7..0508e7dc56 100644
--- a/eth/state_accessor.go
+++ b/eth/state_accessor.go
@@ -39,6 +39,7 @@ import (
"github.com/ava-labs/subnet-evm/core/vm"
"github.com/ava-labs/subnet-evm/eth/tracers"
"github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
)
@@ -52,7 +53,7 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u
var (
current *types.Block
database state.Database
- triedb *trie.Database
+ tdb *triedb.Database
report = true
origin = block.NumberU64()
)
@@ -78,14 +79,14 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u
// the internal junks created by tracing will be persisted into the disk.
// TODO(rjl493456442), clean cache is disabled to prevent memory leak,
// please re-enable it for better performance.
- database = state.NewDatabaseWithConfig(eth.chainDb, trie.HashDefaults)
+ database = state.NewDatabaseWithConfig(eth.chainDb, triedb.HashDefaults)
if statedb, err = state.New(block.Root(), database, nil); err == nil {
log.Info("Found disk backend for state trie", "root", block.Root(), "number", block.Number())
return statedb, noopReleaser, nil
}
}
// The optional base statedb is given, mark the start point as parent block
- statedb, database, triedb, report = base, base.Database(), base.Database().TrieDB(), false
+ statedb, database, tdb, report = base, base.Database(), base.Database().TrieDB(), false
current = eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
} else {
// Otherwise, try to reexec blocks until we find a state or reach our limit
@@ -95,8 +96,8 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u
// the internal junks created by tracing will be persisted into the disk.
// TODO(rjl493456442), clean cache is disabled to prevent memory leak,
// please re-enable it for better performance.
- triedb = trie.NewDatabase(eth.chainDb, trie.HashDefaults)
- database = state.NewDatabaseWithNodeDB(eth.chainDb, triedb)
+ tdb = triedb.NewDatabase(eth.chainDb, triedb.HashDefaults)
+ database = state.NewDatabaseWithNodeDB(eth.chainDb, tdb)
// If we didn't check the live database, do check state over ephemeral database,
// otherwise we would rewind past a persisted block (specific corner case is
@@ -171,18 +172,18 @@ func (eth *Ethereum) hashState(ctx context.Context, block *types.Block, reexec u
if err != nil {
return nil, nil, fmt.Errorf("state reset after block %d failed: %v", current.NumberU64(), err)
}
- // Note: In coreth, the state reference is held by passing true to [statedb.Commit].
+ // Note: In subnet-evm, the state reference is held by passing true to [statedb.Commit].
// Drop the parent state to prevent accumulating too many nodes in memory.
if parent != (common.Hash{}) {
- triedb.Dereference(parent)
+ tdb.Dereference(parent)
}
parent = root
}
if report {
- _, nodes, imgs := triedb.Size() // all memory is contained within the nodes return in hashdb
+ _, nodes, imgs := tdb.Size() // all memory is contained within the nodes return in hashdb
log.Info("Historical state regenerated", "block", current.NumberU64(), "elapsed", time.Since(start), "nodes", nodes, "preimages", imgs)
}
- return statedb, func() { triedb.Dereference(block.Root()) }, nil
+ return statedb, func() { tdb.Dereference(block.Root()) }, nil
}
func (eth *Ethereum) pathState(block *types.Block) (*state.StateDB, func(), error) {
diff --git a/eth/tracers/api.go b/eth/tracers/api.go
index deb04c2902..1be62dacaa 100644
--- a/eth/tracers/api.go
+++ b/eth/tracers/api.go
@@ -90,7 +90,7 @@ type Backend interface {
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error)
BadBlocks() ([]*types.Block, []*core.BadBlockReason)
- GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
+ GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error)
RPCGasCap() uint64
ChainConfig() *params.ChainConfig
Engine() consensus.Engine
@@ -874,12 +874,12 @@ 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) {
- tx, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash)
+ found, _, blockHash, blockNumber, index, err := api.backend.GetTransaction(ctx, hash)
if err != nil {
- return nil, err
+ return nil, ethapi.NewTxIndexingError()
}
// Only mined txes are supported
- if tx == nil {
+ if !found {
return nil, errTxNotFound
}
// It shouldn't happen in practice.
@@ -975,7 +975,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
}
}
// Execute the trace
- msg, err := args.ToMessage(api.backend.RPCGasCap(), block.BaseFee())
+ msg, err := args.ToMessage(api.backend.RPCGasCap(), vmctx.BaseFee)
if err != nil {
return nil, err
}
diff --git a/eth/tracers/api_extra_test.go b/eth/tracers/api_extra_test.go
index f5e07bfa27..06bbfa5f77 100644
--- a/eth/tracers/api_extra_test.go
+++ b/eth/tracers/api_extra_test.go
@@ -33,7 +33,7 @@ func TestTraceBlockPrecompileActivation(t *testing.T) {
copyConfig := *params.TestChainConfig
genesis := &core.Genesis{
Config: ©Config,
- Alloc: core.GenesisAlloc{
+ Alloc: types.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
@@ -139,7 +139,7 @@ func TestTraceTransactionPrecompileActivation(t *testing.T) {
copyConfig := *params.TestChainConfig
genesis := &core.Genesis{
Config: ©Config,
- Alloc: core.GenesisAlloc{
+ Alloc: types.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
@@ -203,7 +203,7 @@ func TestTraceChainPrecompileActivation(t *testing.T) {
copyConfig := *params.TestChainConfig
genesis := &core.Genesis{
Config: ©Config,
- Alloc: core.GenesisAlloc{
+ Alloc: types.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(5 * params.Ether)},
accounts[1].addr: {Balance: big.NewInt(5 * params.Ether)},
accounts[2].addr: {Balance: big.NewInt(5 * params.Ether)},
@@ -300,7 +300,7 @@ func TestTraceCallWithOverridesStateUpgrade(t *testing.T) {
copyConfig := *params.TestChainConfig
genesis := &core.Genesis{
Config: ©Config,
- Alloc: core.GenesisAlloc{
+ Alloc: types.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(5 * params.Ether)},
accounts[1].addr: {Balance: big.NewInt(5 * params.Ether)},
accounts[2].addr: {Balance: big.NewInt(5 * params.Ether)},
diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go
index 7b99d8e250..1f1b32492e 100644
--- a/eth/tracers/api_test.go
+++ b/eth/tracers/api_test.go
@@ -133,9 +133,9 @@ func (b *testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber)
func (b *testBackend) BadBlocks() ([]*types.Block, []*core.BadBlockReason) { return nil, nil }
-func (b *testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
+func (b *testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) {
tx, hash, blockNumber, index := rawdb.ReadTransaction(b.chaindb, txHash)
- return tx, hash, blockNumber, index, nil
+ return tx != nil, tx, hash, blockNumber, index, nil
}
func (b *testBackend) RPCGasCap() uint64 {
@@ -227,7 +227,7 @@ func TestTraceCall(t *testing.T) {
accounts := newAccounts(3)
genesis := &core.Genesis{
Config: params.TestChainConfig,
- Alloc: core.GenesisAlloc{
+ Alloc: types.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
@@ -445,7 +445,7 @@ func TestTraceTransaction(t *testing.T) {
accounts := newAccounts(2)
genesis := &core.Genesis{
Config: params.TestChainConfig,
- Alloc: core.GenesisAlloc{
+ Alloc: types.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
},
@@ -501,7 +501,7 @@ func TestTraceBlock(t *testing.T) {
accounts := newAccounts(3)
genesis := &core.Genesis{
Config: params.TestChainConfig,
- Alloc: core.GenesisAlloc{
+ Alloc: types.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
@@ -591,7 +591,7 @@ func TestTracingWithOverrides(t *testing.T) {
storageAccount := common.Address{0x13, 37}
genesis := &core.Genesis{
Config: params.TestChainConfig,
- Alloc: core.GenesisAlloc{
+ Alloc: types.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
@@ -961,7 +961,7 @@ func TestTraceChain(t *testing.T) {
accounts := newAccounts(3)
genesis := &core.Genesis{
Config: params.TestChainConfig,
- Alloc: core.GenesisAlloc{
+ Alloc: types.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(5 * params.Ether)},
accounts[1].addr: {Balance: big.NewInt(5 * params.Ether)},
accounts[2].addr: {Balance: big.NewInt(5 * params.Ether)},
diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go
index 6c2aa1ab89..f2cbeed762 100644
--- a/eth/tracers/internal/tracetest/calltrace_test.go
+++ b/eth/tracers/internal/tracetest/calltrace_test.go
@@ -132,12 +132,7 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
}
// Configure a blockchain with the given prestate
var (
- signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
- origin, _ = signer.Sender(tx)
- txContext = vm.TxContext{
- Origin: origin,
- GasPrice: tx.GasPrice(),
- }
+ signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
context = vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
@@ -148,19 +143,19 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) {
GasLimit: uint64(test.Context.GasLimit),
BaseFee: test.Genesis.BaseFee,
}
- triedb, _, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme)
+ state = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme)
)
- triedb.Close()
+ state.Close()
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
- evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer})
- msg, err := core.TransactionToMessage(tx, signer, nil)
+ msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
+ evm := vm.NewEVM(context, core.NewEVMTxContext(msg), state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer})
vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil {
t.Fatalf("failed to execute transaction: %v", err)
@@ -232,10 +227,6 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
b.Fatalf("failed to parse testcase input: %v", err)
}
signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
- msg, err := core.TransactionToMessage(tx, signer, nil)
- if err != nil {
- b.Fatalf("failed to prepare transaction for tracing: %v", err)
- }
origin, _ := signer.Sender(tx)
txContext := vm.TxContext{
Origin: origin,
@@ -250,8 +241,12 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
Difficulty: (*big.Int)(test.Context.Difficulty),
GasLimit: uint64(test.Context.GasLimit),
}
- triedb, _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme)
- defer triedb.Close()
+ msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
+ if err != nil {
+ b.Fatalf("failed to prepare transaction for tracing: %v", err)
+ }
+ state := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme)
+ defer state.Close()
b.ReportAllocs()
b.ResetTimer()
@@ -260,8 +255,8 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
if err != nil {
b.Fatalf("failed to create call tracer: %v", err)
}
- evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer})
- snap := statedb.Snapshot()
+ evm := vm.NewEVM(context, txContext, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer})
+ snap := state.StateDB.Snapshot()
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if _, err = st.TransitionDb(); err != nil {
b.Fatalf("failed to execute transaction: %v", err)
@@ -269,7 +264,7 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
if _, err = tracer.GetResult(); err != nil {
b.Fatal(err)
}
- statedb.RevertToSnapshot(snap)
+ state.StateDB.RevertToSnapshot(snap)
}
}
@@ -377,18 +372,18 @@ func TestInternals(t *testing.T) {
},
} {
t.Run(tc.name, func(t *testing.T) {
- triedb, _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(),
- core.GenesisAlloc{
- to: core.GenesisAccount{
+ state := tests.MakePreState(rawdb.NewMemoryDatabase(),
+ types.GenesisAlloc{
+ to: types.Account{
Code: tc.code,
},
- origin: core.GenesisAccount{
+ origin: types.Account{
Balance: big.NewInt(500000000000000),
},
}, false, rawdb.HashScheme)
- defer triedb.Close()
+ defer state.Close()
- evm := vm.NewEVM(context, txContext, statedb, params.TestPreSubnetEVMChainConfig, vm.Config{Tracer: tc.tracer})
+ evm := vm.NewEVM(context, txContext, state.StateDB, params.TestPreSubnetEVMChainConfig, vm.Config{Tracer: tc.tracer})
msg := &core.Message{
To: &to,
From: origin,
diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go
index 3afa5d3080..93b85735e6 100644
--- a/eth/tracers/internal/tracetest/flat_calltrace_test.go
+++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go
@@ -96,11 +96,6 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
return fmt.Errorf("failed to parse testcase input: %v", err)
}
signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
- origin, _ := signer.Sender(tx)
- txContext := vm.TxContext{
- Origin: origin,
- GasPrice: tx.GasPrice(),
- }
context := vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
@@ -110,20 +105,19 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string
Difficulty: (*big.Int)(test.Context.Difficulty),
GasLimit: uint64(test.Context.GasLimit),
}
- triedb, _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme)
- defer triedb.Close()
+ state := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme)
+ defer state.Close()
// Create the tracer, the EVM environment and run it
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
if err != nil {
return fmt.Errorf("failed to create call tracer: %v", err)
}
- evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer})
-
- msg, err := core.TransactionToMessage(tx, signer, nil)
+ msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
if err != nil {
return fmt.Errorf("failed to prepare transaction for tracing: %v", err)
}
+ evm := vm.NewEVM(context, core.NewEVMTxContext(msg), state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer})
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if _, err = st.TransitionDb(); err != nil {
diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go
index 98a18aadba..ef7c5cfd20 100644
--- a/eth/tracers/internal/tracetest/prestate_test.go
+++ b/eth/tracers/internal/tracetest/prestate_test.go
@@ -102,12 +102,7 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
}
// Configure a blockchain with the given prestate
var (
- signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
- origin, _ = signer.Sender(tx)
- txContext = vm.TxContext{
- Origin: origin,
- GasPrice: tx.GasPrice(),
- }
+ signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time))
context = vm.BlockContext{
CanTransfer: core.CanTransfer,
Transfer: core.Transfer,
@@ -118,19 +113,19 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) {
GasLimit: uint64(test.Context.GasLimit),
BaseFee: test.Genesis.BaseFee,
}
- triedb, _, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme)
+ state = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme)
)
- defer triedb.Close()
+ defer state.Close()
tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), test.TracerConfig)
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
- evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Tracer: tracer})
- msg, err := core.TransactionToMessage(tx, signer, nil)
+ msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
if err != nil {
t.Fatalf("failed to prepare transaction for tracing: %v", err)
}
+ evm := vm.NewEVM(context, core.NewEVMTxContext(msg), state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer})
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
if _, err = st.TransitionDb(); err != nil {
t.Fatalf("failed to execute transaction: %v", err)
diff --git a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json
index 8a8786c37d..192a38c59a 100644
--- a/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json
+++ b/eth/tracers/internal/tracetest/testdata/prestate_tracer_with_diff_mode/create_failed.json
@@ -83,11 +83,11 @@
},
"post": {
"0x808b4da0be6c9512e948521452227efc619bea52": {
- "balance": "0x2cd72a36dd031f089",
+ "balance": "0x2cd987071ba2346b6",
"nonce": 1223933
},
"0x8f03f1a3f10c05e7cccf75c1fd10168e06659be7": {
- "balance": "0x3807e1f151a8123ec8"
+ "balance": "0x3807bc244dbe20e89b"
}
}
}
diff --git a/eth/tracers/js/internal/tracers/call_tracer_legacy.js b/eth/tracers/js/internal/tracers/call_tracer_legacy.js
index 451a644b91..0760bb1e3f 100644
--- a/eth/tracers/js/internal/tracers/call_tracer_legacy.js
+++ b/eth/tracers/js/internal/tracers/call_tracer_legacy.js
@@ -219,7 +219,7 @@
return this.finalize(result);
},
- // finalize recreates a call object using the final desired field oder for json
+ // finalize recreates a call object using the final desired field order for json
// serialization. This is a nicety feature to pass meaningfully ordered results
// to users who don't interpret it, just display it.
finalize: function(call) {
diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go
index a1eafddb14..d2fb8a4cf3 100644
--- a/eth/tracers/js/tracer_test.go
+++ b/eth/tracers/js/tracer_test.go
@@ -39,6 +39,7 @@ import (
"github.com/ava-labs/subnet-evm/eth/tracers"
"github.com/ava-labs/subnet-evm/params"
"github.com/ethereum/go-ethereum/common"
+ "github.com/holiman/uint256"
)
type account struct{}
@@ -47,9 +48,9 @@ func (account) SubBalance(amount *big.Int) {}
func (account) AddBalance(amount *big.Int) {}
func (account) SetAddress(common.Address) {}
func (account) Value() *big.Int { return nil }
-func (account) SetBalance(*big.Int) {}
+func (account) SetBalance(*uint256.Int) {}
func (account) SetNonce(uint64) {}
-func (account) Balance() *big.Int { return nil }
+func (account) Balance() *uint256.Int { return nil }
func (account) Address() common.Address { return common.Address{} }
func (account) SetCode(common.Hash, []byte) {}
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
@@ -58,8 +59,8 @@ type dummyStatedb struct {
state.StateDB
}
-func (*dummyStatedb) GetRefund() uint64 { return 1337 }
-func (*dummyStatedb) GetBalance(addr common.Address) *big.Int { return new(big.Int) }
+func (*dummyStatedb) GetRefund() uint64 { return 1337 }
+func (*dummyStatedb) GetBalance(addr common.Address) *uint256.Int { return new(uint256.Int) }
type vmContext struct {
blockCtx vm.BlockContext
@@ -75,7 +76,7 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon
env = vm.NewEVM(vmctx.blockCtx, vmctx.txCtx, &dummyStatedb{}, chaincfg, vm.Config{Tracer: tracer})
gasLimit uint64 = 31000
startGas uint64 = 10000
- value = big.NewInt(0)
+ value = uint256.NewInt(0)
contract = vm.NewContract(account{}, account{}, value, startGas)
)
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
@@ -84,7 +85,7 @@ func runTrace(tracer tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCon
}
tracer.CaptureTxStart(gasLimit)
- tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value)
+ tracer.CaptureStart(env, contract.Caller(), contract.Address(), false, []byte{}, startGas, value.ToBig())
ret, err := env.Interpreter().Run(contract, []byte{}, false)
tracer.CaptureEnd(ret, startGas-contract.Gas, err)
// Rest gas assumes no refund
@@ -192,7 +193,7 @@ func TestHaltBetweenSteps(t *testing.T) {
}
env := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: big.NewInt(1)}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: tracer})
scope := &vm.ScopeContext{
- Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
+ Contract: vm.NewContract(&account{}, &account{}, uint256.NewInt(0), 0),
}
tracer.CaptureStart(env, common.Address{}, common.Address{}, false, []byte{}, 0, big.NewInt(0))
tracer.CaptureState(0, 0, 0, 0, scope, nil, 0, nil)
@@ -282,7 +283,7 @@ func TestEnterExit(t *testing.T) {
t.Fatal(err)
}
scope := &vm.ScopeContext{
- Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
+ Contract: vm.NewContract(&account{}, &account{}, uint256.NewInt(0), 0),
}
tracer.CaptureEnter(vm.CALL, scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int))
tracer.CaptureExit([]byte{}, 400, nil)
diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go
index 7c675f3666..f859d35f9b 100644
--- a/eth/tracers/logger/logger_test.go
+++ b/eth/tracers/logger/logger_test.go
@@ -27,6 +27,7 @@ import (
"github.com/ava-labs/subnet-evm/core/vm"
"github.com/ava-labs/subnet-evm/params"
"github.com/ethereum/go-ethereum/common"
+ "github.com/holiman/uint256"
)
type dummyContractRef struct {
@@ -50,7 +51,7 @@ func TestStoreCapture(t *testing.T) {
logger = NewStructLogger(nil)
statedb, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
env = vm.NewEVM(vm.BlockContext{}, vm.TxContext{}, statedb, params.TestChainConfig, vm.Config{Tracer: logger})
- contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), 100000)
+ contract = vm.NewContract(&dummyContractRef{}, &dummyContractRef{}, new(uint256.Int), 100000)
)
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)}
var index common.Hash
diff --git a/eth/tracers/native/call.go b/eth/tracers/native/call.go
index 42ec4b74a3..d2e8a09b09 100644
--- a/eth/tracers/native/call.go
+++ b/eth/tracers/native/call.go
@@ -172,7 +172,7 @@ func (t *callTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, sco
return
}
// Avoid processing nested calls when only caring about top call
- if t.config.OnlyTopCall && depth > 0 {
+ if t.config.OnlyTopCall && depth > 1 {
return
}
// Skip if tracing was interrupted
diff --git a/eth/tracers/native/prestate.go b/eth/tracers/native/prestate.go
index b623797a60..602fbc4977 100644
--- a/eth/tracers/native/prestate.go
+++ b/eth/tracers/native/prestate.go
@@ -205,7 +205,7 @@ func (t *prestateTracer) CaptureTxEnd(restGas uint64) {
}
modified := false
postAccount := &account{Storage: make(map[common.Hash]common.Hash)}
- newBalance := t.env.StateDB.GetBalance(addr)
+ newBalance := t.env.StateDB.GetBalance(addr).ToBig()
newNonce := t.env.StateDB.GetNonce(addr)
newCode := t.env.StateDB.GetCode(addr)
@@ -289,7 +289,7 @@ func (t *prestateTracer) lookupAccount(addr common.Address) {
}
t.pre[addr] = &account{
- Balance: t.env.StateDB.GetBalance(addr),
+ Balance: t.env.StateDB.GetBalance(addr).ToBig(),
Nonce: t.env.StateDB.GetNonce(addr),
Code: t.env.StateDB.GetCode(addr),
Storage: make(map[common.Hash]common.Hash),
diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go
index aa0166d38e..b69faf00c7 100644
--- a/eth/tracers/tracers_test.go
+++ b/eth/tracers/tracers_test.go
@@ -71,7 +71,7 @@ func BenchmarkTransactionTrace(b *testing.B) {
GasLimit: gas,
BaseFee: big.NewInt(8),
}
- alloc := core.GenesisAlloc{}
+ alloc := types.GenesisAlloc{}
// The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns
// the address
loop := []byte{
@@ -79,18 +79,18 @@ func BenchmarkTransactionTrace(b *testing.B) {
byte(vm.PUSH1), 0, // jumpdestination
byte(vm.JUMP),
}
- alloc[common.HexToAddress("0x00000000000000000000000000000000deadbeef")] = core.GenesisAccount{
+ alloc[common.HexToAddress("0x00000000000000000000000000000000deadbeef")] = types.Account{
Nonce: 1,
Code: loop,
Balance: big.NewInt(1),
}
- alloc[from] = core.GenesisAccount{
+ alloc[from] = types.Account{
Nonce: 1,
Code: []byte{},
Balance: big.NewInt(500000000000000),
}
- triedb, _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false, rawdb.HashScheme)
- defer triedb.Close()
+ state := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false, rawdb.HashScheme)
+ defer state.Close()
// Create the tracer, the EVM environment and run it
tracer := logger.NewStructLogger(&logger.Config{
@@ -99,8 +99,8 @@ func BenchmarkTransactionTrace(b *testing.B) {
//EnableMemory: false,
//EnableReturnData: false,
})
- evm := vm.NewEVM(context, txContext, statedb, params.TestChainConfig, vm.Config{Tracer: tracer})
- msg, err := core.TransactionToMessage(tx, signer, nil)
+ evm := vm.NewEVM(context, txContext, state.StateDB, params.TestChainConfig, vm.Config{Tracer: tracer})
+ msg, err := core.TransactionToMessage(tx, signer, context.BaseFee)
if err != nil {
b.Fatalf("failed to prepare transaction for tracing: %v", err)
}
@@ -108,13 +108,13 @@ func BenchmarkTransactionTrace(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
- snap := statedb.Snapshot()
+ snap := state.StateDB.Snapshot()
st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
_, err = st.TransitionDb()
if err != nil {
b.Fatal(err)
}
- statedb.RevertToSnapshot(snap)
+ state.StateDB.RevertToSnapshot(snap)
if have, want := len(tracer.StructLogs()), 244752; have != want {
b.Fatalf("trace wrong, want %d steps, have %d", want, have)
}
@@ -134,9 +134,9 @@ func TestMemCopying(t *testing.T) {
{0, 100, 0, "", 0}, // No need to pad (0 size)
{100, 50, 100, "", 100}, // Should pad 100-150
{100, 50, 5, "", 5}, // Wanted range fully within memory
- {100, -50, 0, "offset or size must not be negative", 0}, // Errror
- {0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Errror
- {10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Errror
+ {100, -50, 0, "offset or size must not be negative", 0}, // Error
+ {0, 1, 1024*1024 + 1, "reached limit for padding memory slice: 1048578", 0}, // Error
+ {10, 0, 1024*1024 + 100, "reached limit for padding memory slice: 1048666", 0}, // Error
} {
mem := vm.NewMemory()
diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go
index c7f0593fc7..87d608f030 100644
--- a/ethclient/ethclient.go
+++ b/ethclient/ethclient.go
@@ -395,10 +395,8 @@ func (ec *client) TransactionInBlock(ctx context.Context, blockHash common.Hash,
func (ec *client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
var r *types.Receipt
err := ec.c.CallContext(ctx, &r, "eth_getTransactionReceipt", txHash)
- if err == nil {
- if r == nil {
- return nil, interfaces.NotFound
- }
+ if err == nil && r == nil {
+ return nil, interfaces.NotFound
}
return r, err
}
diff --git a/ethclient/simulated/backend.go b/ethclient/simulated/backend.go
new file mode 100644
index 0000000000..9d53ed79bd
--- /dev/null
+++ b/ethclient/simulated/backend.go
@@ -0,0 +1,279 @@
+// 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 .
+
+package simulated
+
+import (
+ "errors"
+ "math/big"
+ "time"
+
+ "github.com/ava-labs/avalanchego/utils/timer/mockable"
+ "github.com/ava-labs/subnet-evm/consensus/dummy"
+ "github.com/ava-labs/subnet-evm/constants"
+ "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/rawdb"
+ "github.com/ava-labs/subnet-evm/core/types"
+ "github.com/ava-labs/subnet-evm/eth"
+ "github.com/ava-labs/subnet-evm/eth/ethconfig"
+ "github.com/ava-labs/subnet-evm/ethclient"
+ "github.com/ava-labs/subnet-evm/interfaces"
+ "github.com/ava-labs/subnet-evm/node"
+ "github.com/ava-labs/subnet-evm/params"
+ "github.com/ava-labs/subnet-evm/rpc"
+ "github.com/ethereum/go-ethereum/common"
+)
+
+var _ eth.PushGossiper = (*fakePushGossiper)(nil)
+
+type fakePushGossiper struct{}
+
+func (*fakePushGossiper) Add(*types.Transaction) {}
+
+// Client exposes the methods provided by the Ethereum RPC client.
+type Client interface {
+ interfaces.BlockNumberReader
+ interfaces.ChainReader
+ interfaces.ChainStateReader
+ interfaces.ContractCaller
+ interfaces.GasEstimator
+ interfaces.GasPricer
+ interfaces.GasPricer1559
+ interfaces.FeeHistoryReader
+ interfaces.LogFilterer
+ interfaces.AcceptedStateReader
+ interfaces.AcceptedContractCaller
+ interfaces.TransactionReader
+ interfaces.TransactionSender
+ interfaces.ChainIDReader
+}
+
+// simClient wraps ethclient. This exists to prevent extracting ethclient.Client
+// from the Client interface returned by Backend.
+type simClient struct {
+ ethclient.Client
+}
+
+// Backend is a simulated blockchain. You can use it to test your contracts or
+// other code that interacts with the Ethereum chain.
+type Backend struct {
+ eth *eth.Ethereum
+ client simClient
+ clock *mockable.Clock
+ server *rpc.Server
+}
+
+// NewBackend creates a new simulated blockchain that can be used as a backend for
+// contract bindings in unit tests.
+//
+// A simulated backend always uses chainID 1337.
+func NewBackend(alloc types.GenesisAlloc, options ...func(nodeConf *node.Config, ethConf *ethconfig.Config)) *Backend {
+ chainConfig := *params.TestChainConfig
+ chainConfig.ChainID = big.NewInt(1337)
+
+ // Create the default configurations for the outer node shell and the Ethereum
+ // service to mutate with the options afterwards
+ nodeConf := node.DefaultConfig
+
+ ethConf := ethconfig.DefaultConfig
+ ethConf.Genesis = &core.Genesis{
+ Config: &chainConfig,
+ GasLimit: chainConfig.FeeConfig.GasLimit.Uint64(),
+ Alloc: alloc,
+ }
+ ethConf.AllowUnfinalizedQueries = true
+ ethConf.Miner.Etherbase = constants.BlackholeAddr
+ ethConf.Miner.TestOnlyAllowDuplicateBlocks = true
+ ethConf.TxPool.NoLocals = true
+
+ for _, option := range options {
+ option(&nodeConf, ðConf)
+ }
+ // Assemble the Ethereum stack to run the chain with
+ stack, err := node.New(&nodeConf)
+ if err != nil {
+ panic(err) // this should never happen
+ }
+ sim, err := newWithNode(stack, ðConf, 0)
+ if err != nil {
+ panic(err) // this should never happen
+ }
+ return sim
+}
+
+// newWithNode sets up a simulated backend on an existing node. The provided node
+// must not be started and will be started by this method.
+func newWithNode(stack *node.Node, conf *eth.Config, blockPeriod uint64) (*Backend, error) {
+ chaindb := rawdb.NewMemoryDatabase()
+ clock := &mockable.Clock{}
+ clock.Set(time.Unix(0, 0))
+
+ engine := dummy.NewFakerWithModeAndClock(
+ dummy.Mode{ModeSkipCoinbase: true}, clock,
+ )
+
+ backend, err := eth.New(
+ stack, conf, &fakePushGossiper{}, chaindb, eth.Settings{}, common.Hash{},
+ engine, clock,
+ )
+ if err != nil {
+ return nil, err
+ }
+ server := rpc.NewServer(0)
+ for _, api := range backend.APIs() {
+ if err := server.RegisterName(api.Namespace, api.Service); err != nil {
+ return nil, err
+ }
+ }
+ return &Backend{
+ eth: backend,
+ client: simClient{ethclient.NewClient(rpc.DialInProc(server))},
+ clock: clock,
+ server: server,
+ }, nil
+}
+
+// Close shuts down the simBackend.
+// The simulated backend can't be used afterwards.
+func (n *Backend) Close() error {
+ if n.client.Client != nil {
+ n.client.Close()
+ }
+ n.server.Stop()
+ return nil
+}
+
+// Commit seals a block and moves the chain forward to a new empty block.
+func (n *Backend) Commit(accept bool) common.Hash {
+ hash, err := n.buildBlock(accept, 10)
+ if err != nil {
+ panic(err)
+ }
+ return hash
+}
+
+func (n *Backend) buildBlock(accept bool, gap uint64) (common.Hash, error) {
+ chain := n.eth.BlockChain()
+ parent := chain.CurrentBlock()
+
+ if err := n.eth.TxPool().Sync(); err != nil {
+ return common.Hash{}, err
+ }
+
+ n.clock.Set(time.Unix(int64(parent.Time+gap), 0))
+ block, err := n.eth.Miner().GenerateBlock(nil)
+ if err != nil {
+ return common.Hash{}, err
+ }
+ if err := chain.InsertBlock(block); err != nil {
+ return common.Hash{}, err
+ }
+ if accept {
+ if err := n.acceptAncestors(block); err != nil {
+ return common.Hash{}, err
+ }
+ chain.DrainAcceptorQueue()
+ }
+ return block.Hash(), nil
+}
+
+func (n *Backend) acceptAncestors(block *types.Block) error {
+ chain := n.eth.BlockChain()
+ lastAccepted := chain.LastConsensusAcceptedBlock()
+
+ // Accept all ancestors of the block
+ toAccept := []*types.Block{block}
+ for block.ParentHash() != lastAccepted.Hash() {
+ block = chain.GetBlockByHash(block.ParentHash())
+ toAccept = append(toAccept, block)
+ if block.NumberU64() < lastAccepted.NumberU64() {
+ return errors.New("last accepted must be an ancestor of the block to accept")
+ }
+ }
+
+ for i := len(toAccept) - 1; i >= 0; i-- {
+ if err := chain.Accept(toAccept[i]); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// Rollback removes all pending transactions, reverting to the last committed state.
+func (n *Backend) Rollback() {
+ // Flush all transactions from the transaction pools
+ maxUint256 := new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 256), common.Big1)
+ original := n.eth.TxPool().GasTip()
+ n.eth.TxPool().SetGasTip(maxUint256)
+ n.eth.TxPool().SetGasTip(original)
+}
+
+// Fork creates a side-chain that can be used to simulate reorgs.
+//
+// This function should be called with the ancestor block where the new side
+// chain should be started. Transactions (old and new) can then be applied on
+// top and Commit-ed.
+//
+// Note, the side-chain will only become canonical (and trigger the events) when
+// it becomes longer. Until then CallContract will still operate on the current
+// canonical chain.
+//
+// There is a % chance that the side chain becomes canonical at the same length
+// to simulate live network behavior.
+func (n *Backend) Fork(parentHash common.Hash) error {
+ chain := n.eth.BlockChain()
+
+ if chain.CurrentBlock().Hash() == parentHash {
+ return nil
+ }
+
+ parent := chain.GetBlockByHash(parentHash)
+ if parent == nil {
+ return errors.New("parent block not found")
+ }
+
+ ch := make(chan core.NewTxPoolReorgEvent, 1)
+ sub := n.eth.TxPool().SubscribeNewReorgEvent(ch)
+ defer sub.Unsubscribe()
+
+ if err := n.eth.BlockChain().SetPreference(parent); err != nil {
+ return err
+ }
+ for {
+ select {
+ case reorg := <-ch:
+ // Wait for tx pool to reorg, then flush the tx pool
+ if reorg.Head.Hash() == parent.Hash() {
+ n.Rollback()
+ return nil
+ }
+ case <-time.After(2 * time.Second):
+ return errors.New("fork not accepted")
+ }
+ }
+}
+
+// AdjustTime changes the block timestamp and creates a new block.
+// It can only be called on empty blocks.
+func (n *Backend) AdjustTime(adjustment time.Duration) error {
+ _, err := n.buildBlock(false, uint64(adjustment))
+ return err
+}
+
+// Client returns a client that accesses the simulated chain.
+func (n *Backend) Client() Client {
+ return n.client
+}
diff --git a/ethclient/simulated/backend_test.go b/ethclient/simulated/backend_test.go
new file mode 100644
index 0000000000..080bbb6960
--- /dev/null
+++ b/ethclient/simulated/backend_test.go
@@ -0,0 +1,318 @@
+// Copyright 2019 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 .
+
+package simulated
+
+import (
+ "context"
+ "crypto/ecdsa"
+ "math/big"
+ "math/rand"
+ "testing"
+ "time"
+
+ "github.com/ava-labs/subnet-evm/accounts/abi/bind"
+ "github.com/ava-labs/subnet-evm/core/types"
+ "github.com/ava-labs/subnet-evm/params"
+ "github.com/ava-labs/subnet-evm/rpc"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/stretchr/testify/require"
+)
+
+var _ bind.ContractBackend = (Client)(nil)
+
+var (
+ testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
+)
+
+func simTestBackend(testAddr common.Address) *Backend {
+ return NewBackend(
+ types.GenesisAlloc{
+ testAddr: {Balance: big.NewInt(10000000000000000)},
+ },
+ )
+}
+
+func newTx(sim *Backend, key *ecdsa.PrivateKey) (*types.Transaction, error) {
+ client := sim.Client()
+
+ // create a signed transaction to send
+ head, _ := client.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
+ gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(params.GWei))
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ chainid, _ := client.ChainID(context.Background())
+ nonce, err := client.NonceAt(context.Background(), addr, nil)
+ if err != nil {
+ return nil, err
+ }
+ tx := types.NewTx(&types.DynamicFeeTx{
+ ChainID: chainid,
+ Nonce: nonce,
+ GasTipCap: big.NewInt(params.GWei),
+ GasFeeCap: gasPrice,
+ Gas: 21000,
+ To: &addr,
+ })
+ return types.SignTx(tx, types.LatestSignerForChainID(chainid), key)
+}
+
+func TestNewBackend(t *testing.T) {
+ sim := NewBackend(types.GenesisAlloc{})
+ defer sim.Close()
+
+ client := sim.Client()
+ num, err := client.BlockNumber(context.Background())
+ if err != nil {
+ t.Fatal(err)
+ }
+ if num != 0 {
+ t.Fatalf("expected 0 got %v", num)
+ }
+ // Create a block
+ sim.Commit(true)
+ num, err = client.BlockNumber(context.Background())
+ if err != nil {
+ t.Fatal(err)
+ }
+ if num != 1 {
+ t.Fatalf("expected 1 got %v", num)
+ }
+}
+
+func TestAdjustTime(t *testing.T) {
+ sim := NewBackend(types.GenesisAlloc{})
+ defer sim.Close()
+
+ client := sim.Client()
+ block1, _ := client.BlockByNumber(context.Background(), nil)
+
+ // Create a block
+ if err := sim.AdjustTime(time.Minute); err != nil {
+ t.Fatal(err)
+ }
+ block2, _ := client.BlockByNumber(context.Background(), nil)
+ prevTime := block1.Time()
+ newTime := block2.Time()
+ if newTime-prevTime != uint64(time.Minute) {
+ t.Errorf("adjusted time not equal to 60 seconds. prev: %v, new: %v", prevTime, newTime)
+ }
+}
+
+func TestSendTransaction(t *testing.T) {
+ sim := simTestBackend(testAddr)
+ defer sim.Close()
+
+ client := sim.Client()
+ ctx := context.Background()
+
+ signedTx, err := newTx(sim, testKey)
+ if err != nil {
+ t.Errorf("could not create transaction: %v", err)
+ }
+ // send tx to simulated backend
+ err = client.SendTransaction(ctx, signedTx)
+ if err != nil {
+ t.Errorf("could not add tx to pending block: %v", err)
+ }
+ sim.Commit(false)
+ block, err := client.BlockByNumber(ctx, big.NewInt(1))
+ if err != nil {
+ t.Errorf("could not get block at height 1: %v", err)
+ }
+
+ if signedTx.Hash() != block.Transactions()[0].Hash() {
+ t.Errorf("did not commit sent transaction. expected hash %v got hash %v", block.Transactions()[0].Hash(), signedTx.Hash())
+ }
+}
+
+// TestFork check that the chain length after a reorg is correct.
+// Steps:
+// 1. Save the current block which will serve as parent for the fork.
+// 2. Mine n blocks with n ∈ [0, 20].
+// 3. Assert that the chain length is n.
+// 4. Fork by using the parent block as ancestor.
+// 5. Mine n+1 blocks which should trigger a reorg.
+// 6. Assert that the chain length is n+1.
+// Since Commit() was called 2n+1 times in total,
+// having a chain length of just n+1 means that a reorg occurred.
+func TestFork(t *testing.T) {
+ t.Parallel()
+ testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
+ sim := simTestBackend(testAddr)
+ defer sim.Close()
+
+ client := sim.Client()
+ ctx := context.Background()
+
+ // 1.
+ parent, _ := client.HeaderByNumber(ctx, nil)
+
+ // 2.
+ n := int(rand.Int31n(21))
+ for i := 0; i < n; i++ {
+ sim.Commit(false)
+ }
+
+ // 3.
+ b, _ := client.BlockNumber(ctx)
+ if b != uint64(n) {
+ t.Error("wrong chain length")
+ }
+
+ // 4.
+ sim.Fork(parent.Hash())
+
+ // 5.
+ for i := 0; i < n+1; i++ {
+ sim.Commit(false)
+ }
+
+ // 6.
+ b, _ = client.BlockNumber(ctx)
+ if b != uint64(n+1) {
+ t.Error("wrong chain length")
+ }
+}
+
+// TestForkResendTx checks that re-sending a TX after a fork
+// is possible and does not cause a "nonce mismatch" panic.
+// Steps:
+// 1. Save the current block which will serve as parent for the fork.
+// 2. Send a transaction.
+// 3. Check that the TX is included in block 1.
+// 4. Fork by using the parent block as ancestor.
+// 5. Mine a block, Re-send the transaction and mine another one.
+// 6. Check that the TX is now included in block 2.
+func TestForkResendTx(t *testing.T) {
+ t.Parallel()
+ testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
+ sim := simTestBackend(testAddr)
+ defer sim.Close()
+
+ client := sim.Client()
+ ctx := context.Background()
+
+ // 1.
+ parent, _ := client.HeaderByNumber(ctx, nil)
+
+ // 2.
+ tx, err := newTx(sim, testKey)
+ if err != nil {
+ t.Fatalf("could not create transaction: %v", err)
+ }
+ client.SendTransaction(ctx, tx)
+ sim.Commit(false)
+
+ // 3.
+ // Note this test is revised from upstream since we cannot get the receipt
+ // for a pending transaction. (Receipts only written for accepted blocks).
+ require := require.New(t)
+ pendingBlockNum := big.NewInt(int64(rpc.PendingBlockNumber))
+ b1, err := client.BlockByNumber(ctx, pendingBlockNum)
+ require.NoError(err)
+ require.Len(b1.Transactions(), 1)
+ require.Equal(tx.Hash(), b1.Transactions()[0].Hash())
+ require.Equal(uint64(1), b1.NumberU64())
+
+ // 4.
+ if err := sim.Fork(parent.Hash()); err != nil {
+ t.Errorf("forking: %v", err)
+ }
+
+ // 5.
+ sim.Commit(false)
+ if err := client.SendTransaction(ctx, tx); err != nil {
+ t.Fatalf("sending transaction: %v", err)
+ }
+ sim.Commit(true)
+ receipt, _ := client.TransactionReceipt(ctx, tx.Hash())
+ if h := receipt.BlockNumber.Uint64(); h != 2 {
+ t.Errorf("TX included in wrong block: %d", h)
+ }
+}
+
+func TestCommitReturnValue(t *testing.T) {
+ t.Parallel()
+ testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
+ sim := simTestBackend(testAddr)
+ defer sim.Close()
+
+ client := sim.Client()
+ ctx := context.Background()
+ chainid, _ := client.ChainID(context.Background())
+
+ // Test if Commit returns the correct block hash
+ h1 := sim.Commit(false)
+ cur, _ := client.HeaderByNumber(ctx, nil)
+ if h1 != cur.Hash() {
+ t.Error("Commit did not return the hash of the last block.")
+ }
+
+ // Create a block in the original chain (containing a transaction to force different block hashes)
+ head, _ := client.HeaderByNumber(ctx, nil) // Should be child's, good enough
+ gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
+ _tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
+ tx, _ := types.SignTx(_tx, types.LatestSignerForChainID(chainid), testKey)
+ if err := client.SendTransaction(ctx, tx); err != nil {
+ t.Fatalf("sending transaction: %v", err)
+ }
+
+ h2 := sim.Commit(false)
+
+ // Create another block in the original chain
+ sim.Commit(false)
+
+ // Fork at the first bock
+ if err := sim.Fork(h1); err != nil {
+ t.Errorf("forking: %v", err)
+ }
+
+ // Test if Commit returns the correct block hash after the reorg
+ h2fork := sim.Commit(false)
+ if h2 == h2fork {
+ t.Error("The block in the fork and the original block are the same block!")
+ }
+ if header, err := client.HeaderByHash(ctx, h2fork); err != nil || header == nil {
+ t.Error("Could not retrieve the just created block (side-chain)")
+ }
+}
+
+// TestAdjustTimeAfterFork ensures that after a fork, AdjustTime uses the pending fork
+// block's parent rather than the canonical head's parent.
+func TestAdjustTimeAfterFork(t *testing.T) {
+ t.Parallel()
+ testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
+ sim := simTestBackend(testAddr)
+ defer sim.Close()
+
+ client := sim.Client()
+ ctx := context.Background()
+
+ sim.Commit(false) // h1
+ h1, _ := client.HeaderByNumber(ctx, nil)
+
+ sim.Commit(false) // h2
+ sim.Fork(h1.Hash())
+ sim.AdjustTime(1 * time.Second)
+ sim.Commit(false)
+
+ head, _ := client.HeaderByNumber(ctx, nil)
+ if head.Number.Uint64() == 2 && head.ParentHash != h1.Hash() {
+ t.Errorf("failed to build block on fork")
+ }
+}
diff --git a/ethclient/simulated/options.go b/ethclient/simulated/options.go
new file mode 100644
index 0000000000..8d5723c8d7
--- /dev/null
+++ b/ethclient/simulated/options.go
@@ -0,0 +1,41 @@
+// Copyright 2024 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 .
+
+package simulated
+
+import (
+ "math/big"
+
+ "github.com/ava-labs/subnet-evm/eth/ethconfig"
+ "github.com/ava-labs/subnet-evm/node"
+)
+
+// WithBlockGasLimit configures the simulated backend to target a specific gas limit
+// when producing blocks.
+func WithBlockGasLimit(gaslimit uint64) func(nodeConf *node.Config, ethConf *ethconfig.Config) {
+ return func(nodeConf *node.Config, ethConf *ethconfig.Config) {
+ ethConf.Genesis.GasLimit = gaslimit
+ ethConf.Genesis.Config.FeeConfig.GasLimit = new(big.Int).SetUint64(gaslimit)
+ }
+}
+
+// WithCallGasLimit configures the simulated backend to cap eth_calls to a specific
+// gas limit when running client operations.
+func WithCallGasLimit(gaslimit uint64) func(nodeConf *node.Config, ethConf *ethconfig.Config) {
+ return func(nodeConf *node.Config, ethConf *ethconfig.Config) {
+ ethConf.RPCGasCap = gaslimit
+ }
+}
diff --git a/ethclient/simulated/options_test.go b/ethclient/simulated/options_test.go
new file mode 100644
index 0000000000..f57f215496
--- /dev/null
+++ b/ethclient/simulated/options_test.go
@@ -0,0 +1,74 @@
+// Copyright 2024 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 .
+
+package simulated
+
+import (
+ "context"
+ "math/big"
+ "strings"
+ "testing"
+
+ "github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
+ "github.com/ava-labs/subnet-evm/interfaces"
+ "github.com/ava-labs/subnet-evm/params"
+)
+
+// Tests that the simulator starts with the initial gas limit in the genesis block,
+// and that it keeps the same target value.
+func TestWithBlockGasLimitOption(t *testing.T) {
+ // Construct a simulator, targeting a different gas limit
+ sim := NewBackend(types.GenesisAlloc{}, WithBlockGasLimit(12_345_678))
+ defer sim.Close()
+
+ client := sim.Client()
+ genesis, err := client.BlockByNumber(context.Background(), big.NewInt(0))
+ if err != nil {
+ t.Fatalf("failed to retrieve genesis block: %v", err)
+ }
+ if genesis.GasLimit() != 12_345_678 {
+ t.Errorf("genesis gas limit mismatch: have %v, want %v", genesis.GasLimit(), 12_345_678)
+ }
+ // Produce a number of blocks and verify the locked in gas target
+ sim.Commit(false)
+ head, err := client.BlockByNumber(context.Background(), big.NewInt(1))
+ if err != nil {
+ t.Fatalf("failed to retrieve head block: %v", err)
+ }
+ if head.GasLimit() != 12_345_678 {
+ t.Errorf("head gas limit mismatch: have %v, want %v", head.GasLimit(), 12_345_678)
+ }
+}
+
+// Tests that the simulator honors the RPC call caps set by the options.
+func TestWithCallGasLimitOption(t *testing.T) {
+ // Construct a simulator, targeting a different gas limit
+ sim := NewBackend(types.GenesisAlloc{
+ testAddr: {Balance: big.NewInt(10000000000000000)},
+ }, WithCallGasLimit(params.TxGas-1))
+ defer sim.Close()
+
+ client := sim.Client()
+ _, err := client.CallContract(context.Background(), interfaces.CallMsg{
+ From: testAddr,
+ To: &testAddr,
+ Gas: 21000,
+ }, nil)
+ if !strings.Contains(err.Error(), core.ErrIntrinsicGas.Error()) {
+ t.Fatalf("error mismatch: have %v, want %v", err, core.ErrIntrinsicGas)
+ }
+}
diff --git a/ethclient/subnetevmclient/subnet_evm_client.go b/ethclient/subnetevmclient/subnet_evm_client.go
index 08cce85959..d5794c8164 100644
--- a/ethclient/subnetevmclient/subnet_evm_client.go
+++ b/ethclient/subnetevmclient/subnet_evm_client.go
@@ -197,6 +197,12 @@ func toCallArg(msg interfaces.CallMsg) interface{} {
if msg.GasPrice != nil {
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
}
+ if msg.GasFeeCap != nil {
+ arg["maxFeePerGas"] = (*hexutil.Big)(msg.GasFeeCap)
+ }
+ if msg.GasTipCap != nil {
+ arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap)
+ }
if msg.AccessList != nil {
arg["accessList"] = msg.AccessList
}
diff --git a/go.mod b/go.mod
index 2e56608d7d..51fda3902c 100644
--- a/go.mod
+++ b/go.mod
@@ -11,8 +11,8 @@ require (
github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set/v2 v2.1.0
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127
- github.com/ethereum/go-ethereum v1.13.8
- github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
+ github.com/ethereum/go-ethereum v1.13.14
+ github.com/fjl/memsize v0.0.2
github.com/fsnotify/fsnotify v1.6.0
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08
github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46
@@ -22,7 +22,7 @@ require (
github.com/gorilla/websocket v1.4.2
github.com/hashicorp/go-bexpr v0.1.10
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
- github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7
+ github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4
github.com/holiman/bloomfilter/v2 v2.0.3
github.com/holiman/uint256 v1.2.4
github.com/kylelemons/godebug v1.1.0
@@ -42,10 +42,10 @@ require (
github.com/urfave/cli/v2 v2.25.7
go.uber.org/goleak v1.3.0
go.uber.org/mock v0.4.0
- golang.org/x/crypto v0.21.0
+ golang.org/x/crypto v0.22.0
golang.org/x/exp v0.0.0-20231127185646-65229373498e
- golang.org/x/sync v0.6.0
- golang.org/x/sys v0.18.0
+ golang.org/x/sync v0.7.0
+ golang.org/x/sys v0.19.0
golang.org/x/text v0.14.0
golang.org/x/time v0.3.0
google.golang.org/protobuf v1.34.2
@@ -81,7 +81,7 @@ require (
github.com/getsentry/sentry-go v0.18.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
- github.com/go-ole/go-ole v1.2.6 // indirect
+ github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
@@ -141,9 +141,9 @@ require (
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0 // indirect
- golang.org/x/net v0.23.0 // indirect
- golang.org/x/term v0.18.0 // indirect
- golang.org/x/tools v0.17.0 // indirect
+ golang.org/x/net v0.24.0 // indirect
+ golang.org/x/term v0.19.0 // indirect
+ golang.org/x/tools v0.20.0 // indirect
gonum.org/v1/gonum v0.11.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
diff --git a/go.sum b/go.sum
index e4768cae01..2e295ef79a 100644
--- a/go.sum
+++ b/go.sum
@@ -180,12 +180,12 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY=
github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
-github.com/ethereum/go-ethereum v1.13.8 h1:1od+thJel3tM52ZUNQwvpYOeRHlbkVFZ5S8fhi0Lgsg=
-github.com/ethereum/go-ethereum v1.13.8/go.mod h1:sc48XYQxCzH3fG9BcrXCOOgQk2JfZzNAmIKnceogzsA=
+github.com/ethereum/go-ethereum v1.13.14 h1:EwiY3FZP94derMCIam1iW4HFVrSgIcpsu0HwTQtm6CQ=
+github.com/ethereum/go-ethereum v1.13.14/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
-github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c=
-github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
+github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA=
+github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
@@ -218,8 +218,9 @@ github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
-github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
+github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
@@ -339,8 +340,8 @@ github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuW
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw=
-github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc=
+github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4=
+github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc=
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
@@ -645,8 +646,8 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
-golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
+golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
+golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -729,8 +730,8 @@ golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
-golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
+golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -752,8 +753,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
-golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -817,15 +818,16 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
+golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
-golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
+golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
+golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -899,8 +901,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
-golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
+golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
+golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/interfaces/interfaces.go b/interfaces/interfaces.go
index 1db99f4952..b234312baf 100644
--- a/interfaces/interfaces.go
+++ b/interfaces/interfaces.go
@@ -178,6 +178,16 @@ type GasPricer interface {
SuggestGasPrice(ctx context.Context) (*big.Int, error)
}
+// GasPricer1559 provides access to the EIP-1559 gas price oracle.
+type GasPricer1559 interface {
+ SuggestGasTipCap(ctx context.Context) (*big.Int, error)
+}
+
+// FeeHistoryReader provides access to the fee history oracle.
+type FeeHistoryReader interface {
+ FeeHistory(ctx context.Context, blockCount uint64, lastBlock *big.Int, rewardPercentiles []float64) (*FeeHistory, error)
+}
+
// FeeHistory provides recent fee market data that consumers can use to determine
// a reasonable maxPriorityFeePerGas value.
type FeeHistory struct {
@@ -212,3 +222,13 @@ type GasEstimator interface {
type PendingStateEventer interface {
SubscribePendingTransactions(ctx context.Context, ch chan<- *types.Transaction) (Subscription, error)
}
+
+// BlockNumberReader provides access to the current block number.
+type BlockNumberReader interface {
+ BlockNumber(ctx context.Context) (uint64, error)
+}
+
+// ChainIDReader provides access to the chain ID.
+type ChainIDReader interface {
+ ChainID(ctx context.Context) (*big.Int, error)
+}
diff --git a/internal/debug/flags.go b/internal/debug/flags.go
index bf893293e1..34289ab921 100644
--- a/internal/debug/flags.go
+++ b/internal/debug/flags.go
@@ -176,22 +176,12 @@ var Flags = []cli.Flag{
}
var (
- glogger *log.GlogHandler
- logOutputFile io.WriteCloser
- defaultTerminalHandler *log.TerminalHandler
+ glogger *log.GlogHandler
+ logOutputFile io.WriteCloser
)
func init() {
- defaultTerminalHandler = log.NewTerminalHandler(os.Stderr, false)
- glogger = log.NewGlogHandler(defaultTerminalHandler)
- glogger.Verbosity(log.LvlInfo)
- log.SetDefault(log.NewLogger(glogger))
-}
-
-func ResetLogging() {
- if defaultTerminalHandler != nil {
- defaultTerminalHandler.ResetFieldPadding()
- }
+ glogger = log.NewGlogHandler(log.NewTerminalHandler(os.Stderr, false))
}
// Setup initializes profiling and logging based on the CLI flags.
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 00ad49e908..acd837ad33 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -55,6 +55,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/holiman/uint256"
"github.com/tyler-smith/go-bip39"
)
@@ -62,6 +63,8 @@ import (
// allowed to produce in order to speed up calculations.
const estimateGasErrorRatio = 0.015
+var errBlobTxNotSupported = errors.New("signing blob transactions not supported")
+
// EthereumAPI provides an API to access Ethereum related information.
type EthereumAPI struct {
b Backend
@@ -275,7 +278,7 @@ type PersonalAccountAPI struct {
b Backend
}
-// NewPersonalAccountAPI create a new PersonalAccountAPI.
+// NewPersonalAccountAPI creates a new PersonalAccountAPI.
func NewPersonalAccountAPI(b Backend, nonceLock *AddrLocker) *PersonalAccountAPI {
return &PersonalAccountAPI{
am: b.AccountManager(),
@@ -440,7 +443,7 @@ func (s *PersonalAccountAPI) signTransaction(ctx context.Context, args *Transact
return nil, err
}
// Set some sanity defaults and terminate on failure
- if err := args.setDefaults(ctx, s.b); err != nil {
+ if err := args.setDefaults(ctx, s.b, false); err != nil {
return nil, err
}
// Assemble the transaction and sign with the wallet
@@ -459,6 +462,9 @@ func (s *PersonalAccountAPI) SendTransaction(ctx context.Context, args Transacti
s.nonceLock.LockAddr(args.from())
defer s.nonceLock.UnlockAddr(args.from())
}
+ if args.IsEIP4844() {
+ return common.Hash{}, errBlobTxNotSupported
+ }
signed, err := s.signTransaction(ctx, &args, passwd)
if err != nil {
log.Warn("Failed transaction send attempt", "from", args.from(), "to", args.To, "value", args.Value.ToInt(), "err", err)
@@ -483,6 +489,9 @@ func (s *PersonalAccountAPI) SignTransaction(ctx context.Context, args Transacti
if args.GasPrice == nil && (args.MaxFeePerGas == nil || args.MaxPriorityFeePerGas == nil) {
return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas")
}
+ if args.IsEIP4844() {
+ return nil, errBlobTxNotSupported
+ }
if args.Nonce == nil {
return nil, errors.New("nonce not specified")
}
@@ -511,7 +520,7 @@ func (s *PersonalAccountAPI) SignTransaction(ctx context.Context, args Transacti
//
// The key used to calculate the signature is decrypted with the given password.
//
-// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign
+// https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-personal#personal-sign
func (s *PersonalAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) {
// Look up the wallet containing the requested signer
account := accounts.Account{Address: addr}
@@ -539,7 +548,7 @@ func (s *PersonalAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr
// Note, the signature must conform to the secp256k1 curve R, S and V values, where
// the V value must be 27 or 28 for legacy reasons.
//
-// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover
+// https://geth.ethereum.org/docs/interacting-with-geth/rpc/ns-personal#personal-ecrecover
func (s *PersonalAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) {
if len(sig) != crypto.SignatureLength {
return common.Address{}, fmt.Errorf("signature must be %d bytes long", crypto.SignatureLength)
@@ -632,10 +641,11 @@ func (s *BlockChainAPI) GetBalance(ctx context.Context, address common.Address,
if state == nil || err != nil {
return nil, err
}
- return (*hexutil.Big)(state.GetBalance(address)), state.Error()
+ b := state.GetBalance(address).ToBig()
+ return (*hexutil.Big)(b), state.Error()
}
-// Result structs for GetProof
+// AccountResult structs for GetProof
type AccountResult struct {
Address common.Address `json:"address"`
AccountProof []string `json:"accountProof"`
@@ -730,10 +740,11 @@ func (s *BlockChainAPI) GetProof(ctx context.Context, address common.Address, st
if err := tr.Prove(crypto.Keccak256(address.Bytes()), &accountProof); err != nil {
return nil, err
}
+ balance := statedb.GetBalance(address).ToBig()
return &AccountResult{
Address: address,
AccountProof: accountProof,
- Balance: (*hexutil.Big)(statedb.GetBalance(address)),
+ Balance: (*hexutil.Big)(balance),
CodeHash: codeHash,
Nonce: hexutil.Uint64(statedb.GetNonce(address)),
StorageHash: storageRoot,
@@ -958,7 +969,8 @@ func (diff *StateOverride) Apply(state *state.StateDB) error {
}
// Override account balance.
if account.Balance != nil {
- state.SetBalance(addr, (*big.Int)(*account.Balance))
+ u256Balance, _ := uint256.FromBig((*big.Int)(*account.Balance))
+ state.SetBalance(addr, u256Balance)
}
if account.State != nil && account.StateDiff != nil {
return fmt.Errorf("account %s has both 'state' and 'stateDiff'", addr.Hex())
@@ -1070,14 +1082,14 @@ func doCall(ctx context.Context, b Backend, args TransactionArgs, state *state.S
defer cancel()
// Get a new instance of the EVM.
- msg, err := args.ToMessage(globalGasCap, header.BaseFee)
- if err != nil {
- return nil, err
- }
blockCtx := core.NewEVMBlockContext(header, NewChainContext(ctx, b), nil)
if blockOverrides != nil {
blockOverrides.Apply(&blockCtx)
}
+ msg, err := args.ToMessage(globalGasCap, blockCtx.BaseFee)
+ if err != nil {
+ return nil, err
+ }
evm := b.GetEVM(ctx, msg, state, header, &vm.Config{NoBaseFee: true}, &blockCtx)
// Wait for the context to be done and cancel the evm. Even if the
@@ -1202,6 +1214,7 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
// returns error if the transaction would revert or if there are unexpected failures. The returned
// value is capped by both `args.Gas` (if non-nil & non-zero) and the backend's RPCGasCap
// configuration (if non-zero).
+// Note: Required blob gas is not computed in this method.
func (s *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Uint64, error) {
bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
if blockNrOrHash != nil {
@@ -1462,7 +1475,7 @@ type accessListResult struct {
// CreateAccessList creates an EIP-2930 type AccessList for the given transaction.
// Reexec and BlockNrOrHash can be specified to create the accessList on top of a certain state.
func (s *BlockChainAPI) CreateAccessList(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash) (*accessListResult, error) {
- bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
+ bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
if blockNrOrHash != nil {
bNrOrHash = *blockNrOrHash
}
@@ -1486,14 +1499,9 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
if db == nil || err != nil {
return nil, 0, nil, err
}
- // If the gas amount is not set, default to RPC gas cap.
- if args.Gas == nil {
- tmp := hexutil.Uint64(b.RPCGasCap())
- args.Gas = &tmp
- }
// Ensure any missing fields are filled, extract the recipient and input data
- if err := args.setDefaults(ctx, b); err != nil {
+ if err := args.setDefaults(ctx, b, true); err != nil {
return nil, 0, nil, err
}
var to common.Address
@@ -1626,51 +1634,49 @@ func (s *TransactionAPI) GetTransactionCount(ctx context.Context, address common
// GetTransactionByHash returns the transaction for the given hash
func (s *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) {
// Try to return an already finalized transaction
- tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash)
- if err != nil {
- return nil, err
- }
- if tx != nil {
- header, err := s.b.HeaderByHash(ctx, blockHash)
- if err != nil {
- return nil, err
+ found, tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash)
+ if !found {
+ // No finalized transaction, try to retrieve it from the pool
+ if tx := s.b.GetPoolTransaction(hash); tx != nil {
+ estimatedBaseFee, _ := s.b.EstimateBaseFee(ctx)
+ return NewRPCTransaction(tx, s.b.CurrentHeader(), estimatedBaseFee, s.b.ChainConfig()), nil
+ }
+ if err == nil {
+ return nil, nil
}
- return newRPCTransaction(tx, blockHash, blockNumber, header.Time, index, header.BaseFee, s.b.ChainConfig()), nil
+ return nil, NewTxIndexingError()
}
- // No finalized transaction, try to retrieve it from the pool
- if tx := s.b.GetPoolTransaction(hash); tx != nil {
- estimatedBaseFee, _ := s.b.EstimateBaseFee(ctx)
- return NewRPCTransaction(tx, s.b.CurrentHeader(), estimatedBaseFee, s.b.ChainConfig()), nil
+ header, err := s.b.HeaderByHash(ctx, blockHash)
+ if err != nil {
+ return nil, err
}
-
- // Transaction unknown, return as such
- return nil, nil
+ return newRPCTransaction(tx, blockHash, blockNumber, header.Time, index, header.BaseFee, s.b.ChainConfig()), nil
}
// GetRawTransactionByHash returns the bytes of the transaction for the given hash.
func (s *TransactionAPI) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
// Retrieve a finalized transaction, or a pooled otherwise
- tx, _, _, _, err := s.b.GetTransaction(ctx, hash)
- if err != nil {
- return nil, err
- }
- if tx == nil {
- if tx = s.b.GetPoolTransaction(hash); tx == nil {
- // Transaction not found anywhere, abort
+ found, tx, _, _, _, err := s.b.GetTransaction(ctx, hash)
+ if !found {
+ if tx = s.b.GetPoolTransaction(hash); tx != nil {
+ return tx.MarshalBinary()
+ }
+ if err == nil {
return nil, nil
}
+ return nil, NewTxIndexingError()
}
- // Serialize to RLP and return
return tx.MarshalBinary()
}
// GetTransactionReceipt returns the transaction receipt for the given transaction hash.
func (s *TransactionAPI) GetTransactionReceipt(ctx context.Context, hash common.Hash) (map[string]interface{}, error) {
- tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash)
- if tx == nil || err != nil {
- // When the transaction doesn't exist, the RPC method should return JSON null
- // as per specification.
- return nil, nil
+ found, tx, blockHash, blockNumber, index, err := s.b.GetTransaction(ctx, hash)
+ if err != nil {
+ return nil, NewTxIndexingError() // transaction is not fully indexed
+ }
+ if !found {
+ return nil, nil // transaction is not existent or reachable
}
header, err := s.b.HeaderByHash(ctx, blockHash)
if err != nil {
@@ -1793,9 +1799,12 @@ func (s *TransactionAPI) SendTransaction(ctx context.Context, args TransactionAr
s.nonceLock.LockAddr(args.from())
defer s.nonceLock.UnlockAddr(args.from())
}
+ if args.IsEIP4844() {
+ return common.Hash{}, errBlobTxNotSupported
+ }
// Set some sanity defaults and terminate on failure
- if err := args.setDefaults(ctx, s.b); err != nil {
+ if err := args.setDefaults(ctx, s.b, false); err != nil {
return common.Hash{}, err
}
// Assemble the transaction and sign with the wallet
@@ -1812,8 +1821,10 @@ func (s *TransactionAPI) SendTransaction(ctx context.Context, args TransactionAr
// on a given unsigned transaction, and returns it to the caller for further
// processing (signing + broadcast).
func (s *TransactionAPI) FillTransaction(ctx context.Context, args TransactionArgs) (*SignTransactionResult, error) {
+ args.blobSidecarAllowed = true
+
// Set some sanity defaults and terminate on failure
- if err := args.setDefaults(ctx, s.b); err != nil {
+ if err := args.setDefaults(ctx, s.b, false); err != nil {
return nil, err
}
// Assemble the transaction and obtain rlp
@@ -1876,10 +1887,13 @@ func (s *TransactionAPI) SignTransaction(ctx context.Context, args TransactionAr
if args.GasPrice == nil && (args.MaxPriorityFeePerGas == nil || args.MaxFeePerGas == nil) {
return nil, errors.New("missing gasPrice or maxFeePerGas/maxPriorityFeePerGas")
}
+ if args.IsEIP4844() {
+ return nil, errBlobTxNotSupported
+ }
if args.Nonce == nil {
return nil, errors.New("nonce not specified")
}
- if err := args.setDefaults(ctx, s.b); err != nil {
+ if err := args.setDefaults(ctx, s.b, false); err != nil {
return nil, err
}
// Before actually sign the transaction, ensure the transaction fee is reasonable.
@@ -1929,7 +1943,7 @@ func (s *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, g
if sendArgs.Nonce == nil {
return common.Hash{}, errors.New("missing transaction nonce in transaction spec")
}
- if err := sendArgs.setDefaults(ctx, s.b); err != nil {
+ if err := sendArgs.setDefaults(ctx, s.b, false); err != nil {
return common.Hash{}, err
}
matchTx := sendArgs.toTransaction()
@@ -2054,15 +2068,15 @@ func (api *DebugAPI) GetRawReceipts(ctx context.Context, blockNrOrHash rpc.Block
// GetRawTransaction returns the bytes of the transaction for the given hash.
func (s *DebugAPI) GetRawTransaction(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) {
// Retrieve a finalized transaction, or a pooled otherwise
- tx, _, _, _, err := s.b.GetTransaction(ctx, hash)
- if err != nil {
- return nil, err
- }
- if tx == nil {
- if tx = s.b.GetPoolTransaction(hash); tx == nil {
- // Transaction not found anywhere, abort
+ found, tx, _, _, _, err := s.b.GetTransaction(ctx, hash)
+ if !found {
+ if tx = s.b.GetPoolTransaction(hash); tx != nil {
+ return tx.MarshalBinary()
+ }
+ if err == nil {
return nil, nil
}
+ return nil, NewTxIndexingError()
}
return tx.MarshalBinary()
}
diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go
index 1fd0b0da75..72b2ba8568 100644
--- a/internal/ethapi/api_test.go
+++ b/internal/ethapi/api_test.go
@@ -27,8 +27,10 @@
package ethapi
import (
+ "bytes"
"context"
"crypto/ecdsa"
+ "crypto/sha256"
"encoding/json"
"errors"
"fmt"
@@ -41,6 +43,7 @@ import (
"github.com/ava-labs/avalanchego/upgrade"
"github.com/ava-labs/subnet-evm/accounts"
+ "github.com/ava-labs/subnet-evm/accounts/keystore"
"github.com/ava-labs/subnet-evm/commontype"
"github.com/ava-labs/subnet-evm/consensus"
"github.com/ava-labs/subnet-evm/consensus/dummy"
@@ -53,9 +56,11 @@ import (
"github.com/ava-labs/subnet-evm/internal/blocktest"
"github.com/ava-labs/subnet-evm/params"
"github.com/ava-labs/subnet-evm/rpc"
+ "github.com/ava-labs/subnet-evm/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/holiman/uint256"
@@ -413,9 +418,29 @@ func allBlobTxs(addr common.Address, config *params.ChainConfig) []txData {
}
}
+func newTestAccountManager(t *testing.T) (*accounts.Manager, accounts.Account) {
+ var (
+ dir = t.TempDir()
+ am = accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: true})
+ b = keystore.NewKeyStore(dir, 2, 1)
+ testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ )
+ acc, err := b.ImportECDSA(testKey, "")
+ if err != nil {
+ t.Fatalf("failed to create test account: %v", err)
+ }
+ if err := b.Unlock(acc, ""); err != nil {
+ t.Fatalf("failed to unlock account: %v\n", err)
+ }
+ am.AddBackend(b)
+ return am, acc
+}
+
type testBackend struct {
- db ethdb.Database
- chain *core.BlockChain
+ db ethdb.Database
+ chain *core.BlockChain
+ accman *accounts.Manager
+ acc accounts.Account
}
func newTestBackend(t *testing.T, n int, gspec *core.Genesis, engine consensus.Engine, generator func(i int, b *core.BlockGen)) *testBackend {
@@ -427,6 +452,8 @@ func newTestBackend(t *testing.T, n int, gspec *core.Genesis, engine consensus.E
Pruning: false, // Archive mode
}
)
+ accman, acc := newTestAccountManager(t)
+ gspec.Alloc[acc.Address] = types.Account{Balance: big.NewInt(params.Ether)}
// Generate blocks for testing
db, blocks, _, _ := core.GenerateChainWithGenesis(gspec, engine, n, 10, generator)
chain, err := core.NewBlockChain(db, cacheConfig, gspec, engine, vm.Config{}, gspec.ToBlock().Hash(), false)
@@ -443,7 +470,7 @@ func newTestBackend(t *testing.T, n int, gspec *core.Genesis, engine consensus.E
}
chain.DrainAcceptorQueue()
- backend := &testBackend{db: db, chain: chain}
+ backend := &testBackend{db: db, chain: chain, accman: accman, acc: acc}
return backend
}
@@ -454,7 +481,7 @@ func (b testBackend) FeeHistory(ctx context.Context, blockCount uint64, lastBloc
return nil, nil, nil, nil, nil
}
func (b testBackend) ChainDb() ethdb.Database { return b.db }
-func (b testBackend) AccountManager() *accounts.Manager { return nil }
+func (b testBackend) AccountManager() *accounts.Manager { return b.accman }
func (b testBackend) ExtRPCEnabled() bool { return false }
func (b testBackend) RPCGasCap() uint64 { return 10000000 }
func (b testBackend) RPCEVMTimeout() time.Duration { return time.Second }
@@ -558,14 +585,14 @@ func (b testBackend) GetFeeConfigAt(parent *types.Header) (commontype.FeeConfig,
func (b testBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
panic("implement me")
}
-func (b testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error) {
+func (b testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error) {
tx, blockHash, blockNumber, index := rawdb.ReadTransaction(b.db, txHash)
- return tx, blockHash, blockNumber, index, nil
+ return true, tx, blockHash, blockNumber, index, nil
}
func (b testBackend) GetPoolTransactions() (types.Transactions, error) { panic("implement me") }
func (b testBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { panic("implement me") }
func (b testBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) {
- panic("implement me")
+ return 0, nil
}
func (b testBackend) Stats() (pending int, queued int) { panic("implement me") }
func (b testBackend) TxPoolContent() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction) {
@@ -611,7 +638,7 @@ func TestEstimateGas(t *testing.T) {
accounts = newAccounts(2)
genesis = &core.Genesis{
Config: params.TestChainConfig,
- Alloc: core.GenesisAlloc{
+ Alloc: types.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
},
@@ -626,6 +653,7 @@ func TestEstimateGas(t *testing.T) {
// fee: 0 wei
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &accounts[1].addr, Value: big.NewInt(1000), Gas: params.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, accounts[0].key)
b.AddTx(tx)
+ // b.SetPoS()
}))
var testSuite = []struct {
blockNumber rpc.BlockNumber
@@ -725,6 +753,18 @@ func TestEstimateGas(t *testing.T) {
expectErr: nil,
want: 67595,
},
+ // Blobs should have no effect on gas estimate
+ {
+ blockNumber: rpc.LatestBlockNumber,
+ call: TransactionArgs{
+ From: &accounts[0].addr,
+ To: &accounts[1].addr,
+ Value: (*hexutil.Big)(big.NewInt(1)),
+ BlobHashes: []common.Hash{common.Hash{0x01, 0x22}},
+ BlobFeeCap: (*hexutil.Big)(big.NewInt(1)),
+ },
+ want: 21000,
+ },
}
for i, tc := range testSuite {
result, err := api.EstimateGas(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides)
@@ -749,13 +789,16 @@ func TestEstimateGas(t *testing.T) {
}
func TestCall(t *testing.T) {
+ // Enable BLOBHASH opcode in Cancun
+ cfg := *params.TestChainConfig
+ cfg.CancunTime = utils.NewUint64(0)
t.Parallel()
// Initialize test accounts
var (
accounts = newAccounts(3)
genesis = &core.Genesis{
- Config: params.TestChainConfig,
- Alloc: core.GenesisAlloc{
+ Config: &cfg,
+ Alloc: types.GenesisAlloc{
accounts[0].addr: {Balance: big.NewInt(params.Ether)},
accounts[1].addr: {Balance: big.NewInt(params.Ether)},
accounts[2].addr: {Balance: big.NewInt(params.Ether)},
@@ -770,6 +813,7 @@ func TestCall(t *testing.T) {
// fee: 0 wei
tx, _ := types.SignTx(types.NewTx(&types.LegacyTx{Nonce: uint64(i), To: &accounts[1].addr, Value: big.NewInt(1000), Gas: params.TxGas, GasPrice: b.BaseFee(), Data: nil}), signer, accounts[0].key)
b.AddTx(tx)
+ // b.SetPoS()
}))
randomAccounts := newAccounts(3)
var testSuite = []struct {
@@ -891,6 +935,32 @@ func TestCall(t *testing.T) {
blockOverrides: BlockOverrides{Number: (*hexutil.Big)(big.NewInt(11))},
want: "0x000000000000000000000000000000000000000000000000000000000000000b",
},
+ // Invalid blob tx
+ {
+ blockNumber: rpc.LatestBlockNumber,
+ call: TransactionArgs{
+ From: &accounts[1].addr,
+ Input: &hexutil.Bytes{0x00},
+ BlobHashes: []common.Hash{},
+ },
+ expectErr: core.ErrBlobTxCreate,
+ },
+ // BLOBHASH opcode
+ {
+ blockNumber: rpc.LatestBlockNumber,
+ call: TransactionArgs{
+ From: &accounts[1].addr,
+ To: &randomAccounts[2].addr,
+ BlobHashes: []common.Hash{common.Hash{0x01, 0x22}},
+ BlobFeeCap: (*hexutil.Big)(big.NewInt(1)),
+ },
+ overrides: StateOverride{
+ randomAccounts[2].addr: {
+ Code: hex2Bytes("60004960005260206000f3"),
+ },
+ },
+ want: "0x0122000000000000000000000000000000000000000000000000000000000000",
+ },
}
for i, tc := range testSuite {
result, err := api.Call(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides)
@@ -917,6 +987,323 @@ func TestCall(t *testing.T) {
}
}
+func TestSignTransaction(t *testing.T) {
+ t.Parallel()
+ // Initialize test accounts
+ var (
+ key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
+ to = crypto.PubkeyToAddress(key.PublicKey)
+ genesis = &core.Genesis{
+ Config: params.TestChainConfig,
+ Alloc: types.GenesisAlloc{},
+ }
+ )
+ b := newTestBackend(t, 1, genesis, dummy.NewCoinbaseFaker(), func(i int, b *core.BlockGen) {
+ // b.SetPoS()
+ })
+ api := NewTransactionAPI(b, nil)
+ res, err := api.FillTransaction(context.Background(), TransactionArgs{
+ From: &b.acc.Address,
+ To: &to,
+ Value: (*hexutil.Big)(big.NewInt(1)),
+ })
+ if err != nil {
+ t.Fatalf("failed to fill tx defaults: %v\n", err)
+ }
+
+ res, err = api.SignTransaction(context.Background(), argsFromTransaction(res.Tx, b.acc.Address))
+ if err != nil {
+ t.Fatalf("failed to sign tx: %v\n", err)
+ }
+ tx, err := json.Marshal(res.Tx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expect := `{"type":"0x2","chainId":"0x1","nonce":"0x0","to":"0x703c4b2bd70c169f5717101caee543299fc946c7","gas":"0x5208","gasPrice":null,"maxPriorityFeePerGas":"0x0","maxFeePerGas":"0xba43b7400","value":"0x1","input":"0x","accessList":[],"v":"0x0","r":"0xa7bbf5672b6f78e934bd380aad0b2626d5337e96c12f1e755fa5522ba7a314bd","s":"0x4d661f8c7b850b7dc3ce1c8c7b443a4434a22fe3ad14cc463205e0259546f0c8","yParity":"0x0","hash":"0x0333d97cbdababb6af7cc55a6f64d47711b8e18a93d7343657508a454407a82c"}`
+ if !bytes.Equal(tx, []byte(expect)) {
+ t.Errorf("result mismatch. Have:\n%s\nWant:\n%s\n", tx, expect)
+ }
+}
+
+func TestSignBlobTransaction(t *testing.T) {
+ t.Parallel()
+ // Initialize test accounts
+ var (
+ key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
+ to = crypto.PubkeyToAddress(key.PublicKey)
+ genesis = &core.Genesis{
+ Config: params.TestChainConfig,
+ Alloc: types.GenesisAlloc{},
+ }
+ )
+ b := newTestBackend(t, 1, genesis, dummy.NewCoinbaseFaker(), func(i int, b *core.BlockGen) {
+ // b.SetPoS()
+ })
+ api := NewTransactionAPI(b, nil)
+ res, err := api.FillTransaction(context.Background(), TransactionArgs{
+ From: &b.acc.Address,
+ To: &to,
+ Value: (*hexutil.Big)(big.NewInt(1)),
+ BlobHashes: []common.Hash{{0x01, 0x22}},
+ })
+ if err != nil {
+ t.Fatalf("failed to fill tx defaults: %v\n", err)
+ }
+
+ _, err = api.SignTransaction(context.Background(), argsFromTransaction(res.Tx, b.acc.Address))
+ if err == nil {
+ t.Fatalf("should fail on blob transaction")
+ }
+ if !errors.Is(err, errBlobTxNotSupported) {
+ t.Errorf("error mismatch. Have: %v, want: %v", err, errBlobTxNotSupported)
+ }
+}
+
+func TestSendBlobTransaction(t *testing.T) {
+ t.Parallel()
+ // Initialize test accounts
+ var (
+ key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
+ to = crypto.PubkeyToAddress(key.PublicKey)
+ genesis = &core.Genesis{
+ Config: params.TestChainConfig,
+ Alloc: types.GenesisAlloc{},
+ }
+ )
+ b := newTestBackend(t, 1, genesis, dummy.NewCoinbaseFaker(), func(i int, b *core.BlockGen) {
+ // b.SetPoS()
+ })
+ api := NewTransactionAPI(b, nil)
+ res, err := api.FillTransaction(context.Background(), TransactionArgs{
+ From: &b.acc.Address,
+ To: &to,
+ Value: (*hexutil.Big)(big.NewInt(1)),
+ BlobHashes: []common.Hash{common.Hash{0x01, 0x22}},
+ })
+ if err != nil {
+ t.Fatalf("failed to fill tx defaults: %v\n", err)
+ }
+
+ _, err = api.SendTransaction(context.Background(), argsFromTransaction(res.Tx, b.acc.Address))
+ if err == nil {
+ t.Errorf("sending tx should have failed")
+ } else if !errors.Is(err, errBlobTxNotSupported) {
+ t.Errorf("unexpected error. Have %v, want %v\n", err, errBlobTxNotSupported)
+ }
+}
+
+func TestFillBlobTransaction(t *testing.T) {
+ t.Parallel()
+ // Initialize test accounts
+ var (
+ key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
+ to = crypto.PubkeyToAddress(key.PublicKey)
+ genesis = &core.Genesis{
+ Config: params.TestChainConfig,
+ Alloc: types.GenesisAlloc{},
+ }
+ emptyBlob = kzg4844.Blob{}
+ emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob)
+ emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit)
+ emptyBlobHash common.Hash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit)
+ )
+ b := newTestBackend(t, 1, genesis, dummy.NewCoinbaseFaker(), func(i int, b *core.BlockGen) {
+ // b.SetPoS()
+ })
+ api := NewTransactionAPI(b, nil)
+ type result struct {
+ Hashes []common.Hash
+ Sidecar *types.BlobTxSidecar
+ }
+ suite := []struct {
+ name string
+ args TransactionArgs
+ err string
+ want *result
+ }{
+ {
+ name: "TestInvalidParamsCombination1",
+ args: TransactionArgs{
+ From: &b.acc.Address,
+ To: &to,
+ Value: (*hexutil.Big)(big.NewInt(1)),
+ Blobs: []kzg4844.Blob{{}},
+ Proofs: []kzg4844.Proof{{}},
+ },
+ err: `blob proofs provided while commitments were not`,
+ },
+ {
+ name: "TestInvalidParamsCombination2",
+ args: TransactionArgs{
+ From: &b.acc.Address,
+ To: &to,
+ Value: (*hexutil.Big)(big.NewInt(1)),
+ Blobs: []kzg4844.Blob{{}},
+ Commitments: []kzg4844.Commitment{{}},
+ },
+ err: `blob commitments provided while proofs were not`,
+ },
+ {
+ name: "TestInvalidParamsCount1",
+ args: TransactionArgs{
+ From: &b.acc.Address,
+ To: &to,
+ Value: (*hexutil.Big)(big.NewInt(1)),
+ Blobs: []kzg4844.Blob{{}},
+ Commitments: []kzg4844.Commitment{{}, {}},
+ Proofs: []kzg4844.Proof{{}, {}},
+ },
+ err: `number of blobs and commitments mismatch (have=2, want=1)`,
+ },
+ {
+ name: "TestInvalidParamsCount2",
+ args: TransactionArgs{
+ From: &b.acc.Address,
+ To: &to,
+ Value: (*hexutil.Big)(big.NewInt(1)),
+ Blobs: []kzg4844.Blob{{}, {}},
+ Commitments: []kzg4844.Commitment{{}, {}},
+ Proofs: []kzg4844.Proof{{}},
+ },
+ err: `number of blobs and proofs mismatch (have=1, want=2)`,
+ },
+ {
+ name: "TestInvalidProofVerification",
+ args: TransactionArgs{
+ From: &b.acc.Address,
+ To: &to,
+ Value: (*hexutil.Big)(big.NewInt(1)),
+ Blobs: []kzg4844.Blob{{}, {}},
+ Commitments: []kzg4844.Commitment{{}, {}},
+ Proofs: []kzg4844.Proof{{}, {}},
+ },
+ err: `failed to verify blob proof: short buffer`,
+ },
+ {
+ name: "TestGenerateBlobHashes",
+ args: TransactionArgs{
+ From: &b.acc.Address,
+ To: &to,
+ Value: (*hexutil.Big)(big.NewInt(1)),
+ Blobs: []kzg4844.Blob{emptyBlob},
+ Commitments: []kzg4844.Commitment{emptyBlobCommit},
+ Proofs: []kzg4844.Proof{emptyBlobProof},
+ },
+ want: &result{
+ Hashes: []common.Hash{emptyBlobHash},
+ Sidecar: &types.BlobTxSidecar{
+ Blobs: []kzg4844.Blob{emptyBlob},
+ Commitments: []kzg4844.Commitment{emptyBlobCommit},
+ Proofs: []kzg4844.Proof{emptyBlobProof},
+ },
+ },
+ },
+ {
+ name: "TestValidBlobHashes",
+ args: TransactionArgs{
+ From: &b.acc.Address,
+ To: &to,
+ Value: (*hexutil.Big)(big.NewInt(1)),
+ BlobHashes: []common.Hash{emptyBlobHash},
+ Blobs: []kzg4844.Blob{emptyBlob},
+ Commitments: []kzg4844.Commitment{emptyBlobCommit},
+ Proofs: []kzg4844.Proof{emptyBlobProof},
+ },
+ want: &result{
+ Hashes: []common.Hash{emptyBlobHash},
+ Sidecar: &types.BlobTxSidecar{
+ Blobs: []kzg4844.Blob{emptyBlob},
+ Commitments: []kzg4844.Commitment{emptyBlobCommit},
+ Proofs: []kzg4844.Proof{emptyBlobProof},
+ },
+ },
+ },
+ {
+ name: "TestInvalidBlobHashes",
+ args: TransactionArgs{
+ From: &b.acc.Address,
+ To: &to,
+ Value: (*hexutil.Big)(big.NewInt(1)),
+ BlobHashes: []common.Hash{{0x01, 0x22}},
+ Blobs: []kzg4844.Blob{emptyBlob},
+ Commitments: []kzg4844.Commitment{emptyBlobCommit},
+ Proofs: []kzg4844.Proof{emptyBlobProof},
+ },
+ err: fmt.Sprintf("blob hash verification failed (have=%s, want=%s)", common.Hash{0x01, 0x22}, emptyBlobHash),
+ },
+ {
+ name: "TestGenerateBlobProofs",
+ args: TransactionArgs{
+ From: &b.acc.Address,
+ To: &to,
+ Value: (*hexutil.Big)(big.NewInt(1)),
+ Blobs: []kzg4844.Blob{emptyBlob},
+ },
+ want: &result{
+ Hashes: []common.Hash{emptyBlobHash},
+ Sidecar: &types.BlobTxSidecar{
+ Blobs: []kzg4844.Blob{emptyBlob},
+ Commitments: []kzg4844.Commitment{emptyBlobCommit},
+ Proofs: []kzg4844.Proof{emptyBlobProof},
+ },
+ },
+ },
+ }
+ for _, tc := range suite {
+ t.Run(tc.name, func(t *testing.T) {
+ res, err := api.FillTransaction(context.Background(), tc.args)
+ if len(tc.err) > 0 {
+ if err == nil {
+ t.Fatalf("missing error. want: %s", tc.err)
+ } else if err != nil && err.Error() != tc.err {
+ t.Fatalf("error mismatch. want: %s, have: %s", tc.err, err.Error())
+ }
+ return
+ }
+ if err != nil && len(tc.err) == 0 {
+ t.Fatalf("expected no error. have: %s", err)
+ }
+ if res == nil {
+ t.Fatal("result missing")
+ }
+ want, err := json.Marshal(tc.want)
+ if err != nil {
+ t.Fatalf("failed to encode expected: %v", err)
+ }
+ have, err := json.Marshal(result{Hashes: res.Tx.BlobHashes(), Sidecar: res.Tx.BlobTxSidecar()})
+ if err != nil {
+ t.Fatalf("failed to encode computed sidecar: %v", err)
+ }
+ if !bytes.Equal(have, want) {
+ t.Errorf("blob sidecar mismatch. Have: %s, want: %s", have, want)
+ }
+ })
+ }
+}
+
+func argsFromTransaction(tx *types.Transaction, from common.Address) TransactionArgs {
+ var (
+ gas = tx.Gas()
+ nonce = tx.Nonce()
+ input = tx.Data()
+ )
+ return TransactionArgs{
+ From: &from,
+ To: tx.To(),
+ Gas: (*hexutil.Uint64)(&gas),
+ MaxFeePerGas: (*hexutil.Big)(tx.GasFeeCap()),
+ MaxPriorityFeePerGas: (*hexutil.Big)(tx.GasTipCap()),
+ Value: (*hexutil.Big)(tx.Value()),
+ Nonce: (*hexutil.Uint64)(&nonce),
+ Input: (*hexutil.Bytes)(&input),
+ ChainID: (*hexutil.Big)(tx.ChainId()),
+ // TODO: impl accessList conversion
+ //AccessList: tx.AccessList(),
+ BlobFeeCap: (*hexutil.Big)(tx.BlobGasFeeCap()),
+ BlobHashes: tx.BlobHashes(),
+ }
+}
+
type account struct {
key *ecdsa.PrivateKey
addr common.Address
@@ -1162,7 +1549,7 @@ func TestRPCGetBlockOrHeader(t *testing.T) {
acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey)
genesis = &core.Genesis{
Config: params.TestChainConfig,
- Alloc: core.GenesisAlloc{
+ Alloc: types.GenesisAlloc{
acc1Addr: {Balance: big.NewInt(params.Ether)},
acc2Addr: {Balance: big.NewInt(params.Ether)},
},
@@ -1400,6 +1787,7 @@ func TestRPCGetBlockOrHeader(t *testing.T) {
func setupReceiptBackend(t *testing.T, genBlocks int) (*testBackend, []common.Hash) {
config := *params.TestChainConfig
+ config.CancunTime = new(uint64)
var (
acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
@@ -1411,7 +1799,7 @@ func setupReceiptBackend(t *testing.T, genBlocks int) (*testBackend, []common.Ha
ExcessBlobGas: new(uint64),
BlobGasUsed: new(uint64),
Timestamp: uint64(upgrade.InitiallyActiveTime.Unix()),
- Alloc: core.GenesisAlloc{
+ Alloc: types.GenesisAlloc{
acc1Addr: {Balance: big.NewInt(params.Ether)},
acc2Addr: {Balance: big.NewInt(params.Ether)},
// // SPDX-License-Identifier: GPL-3.0
@@ -1431,14 +1819,12 @@ func setupReceiptBackend(t *testing.T, genBlocks int) (*testBackend, []common.Ha
txHashes = make([]common.Hash, genBlocks)
)
- // Set the terminal total difficulty in the config
- // genesis.Config.TerminalTotalDifficulty = big.NewInt(0)
- // genesis.Config.TerminalTotalDifficultyPassed = true
backend := newTestBackend(t, genBlocks, genesis, dummy.NewCoinbaseFaker(), func(i int, b *core.BlockGen) {
var (
tx *types.Transaction
err error
)
+ // b.SetPoS()
switch i {
case 0:
// transfer 1000wei
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index e80e4cad51..a5dab73a80 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -85,7 +85,7 @@ type Backend interface {
// Transaction pool API
SendTx(ctx context.Context, signedTx *types.Transaction) error
- GetTransaction(ctx context.Context, txHash common.Hash) (*types.Transaction, common.Hash, uint64, uint64, error)
+ GetTransaction(ctx context.Context, txHash common.Hash) (bool, *types.Transaction, common.Hash, uint64, uint64, error)
GetPoolTransactions() (types.Transactions, error)
GetPoolTransaction(txHash common.Hash) *types.Transaction
GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error)
diff --git a/internal/ethapi/errors.go b/internal/ethapi/errors.go
index aedf110624..68327efb2c 100644
--- a/internal/ethapi/errors.go
+++ b/internal/ethapi/errors.go
@@ -65,3 +65,24 @@ func newRevertError(revert []byte) *revertError {
reason: hexutil.Encode(revert),
}
}
+
+// TxIndexingError is an API error that indicates the transaction indexing is not
+// fully finished yet with JSON error code and a binary data blob.
+type TxIndexingError struct{}
+
+// NewTxIndexingError creates a TxIndexingError instance.
+func NewTxIndexingError() *TxIndexingError { return &TxIndexingError{} }
+
+// Error implement error interface, returning the error message.
+func (e *TxIndexingError) Error() string {
+ return "transaction indexing is in progress"
+}
+
+// ErrorCode returns the JSON error code for a revert.
+// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal
+func (e *TxIndexingError) ErrorCode() int {
+ return -32000 // to be decided
+}
+
+// ErrorData returns the hex encoded revert reason.
+func (e *TxIndexingError) ErrorData() interface{} { return "transaction indexing is in progress" }
diff --git a/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json b/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json
index a21af855b8..3835567741 100644
--- a/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json
+++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json
@@ -5,17 +5,17 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x7a1200",
"gasUsed": "0x5208",
- "hash": "0x0f67ad1fc8052afad4c24551748600c164091cf37e068adef76315025d3c78e7",
+ "hash": "0xb0a6fc2230444f86583fec57e9fa0aef3ebf36e094b5f2abeb231089697c50e0",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x1",
- "parentHash": "0x3ead7923676a44500c46ad2192a0fc084aa42063b1703e6866f138a47fb1a9ca",
+ "parentHash": "0xa779a1e82fd903ae98affe3a06a0a353906130052c24969b24237adf350bd03b",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x2bb",
- "stateRoot": "0x6b830601767ac4968163193facbe20123435180e325910b2c50efa21f778c697",
+ "stateRoot": "0xa17c770ca1e18e544a4a6d14b611cd03b505d465cc967ba5162d59ce459b553d",
"timestamp": "0xa",
"totalDifficulty": "0x1",
"transactions": [
diff --git a/internal/ethapi/testdata/eth_getBlockByHash-hash-genesis.json b/internal/ethapi/testdata/eth_getBlockByHash-hash-genesis.json
index 446f5db6ab..e8f06e2d94 100644
--- a/internal/ethapi/testdata/eth_getBlockByHash-hash-genesis.json
+++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-genesis.json
@@ -4,7 +4,7 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x0",
- "hash": "0x3ead7923676a44500c46ad2192a0fc084aa42063b1703e6866f138a47fb1a9ca",
+ "hash": "0xa779a1e82fd903ae98affe3a06a0a353906130052c24969b24237adf350bd03b",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
@@ -14,7 +14,7 @@
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x201",
- "stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2",
+ "stateRoot": "0xd883f48b83cc9c1e8389453beb4ad4e572462eec049ca4fffbe16ecefb3fe937",
"timestamp": "0x0",
"totalDifficulty": "0x0",
"transactions": [],
diff --git a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json
index 4d54e3f10b..8f55c154c0 100644
--- a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json
+++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json
@@ -5,22 +5,22 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x7a1200",
"gasUsed": "0x5208",
- "hash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365",
+ "hash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x9",
- "parentHash": "0x2fab5c6892c66668842683ced6b384c2ee83bfd6096a58f451290cabaf57a63e",
+ "parentHash": "0x4f8c1e68991fdb03a668c72e42c454dc911f34416035ef59fdeb83218fe4ae84",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x2bb",
- "stateRoot": "0x3703d70c6443e809ce035c2a8212dbf9813f6b7d1b0f597766e9023867a852f5",
+ "stateRoot": "0x67e974f6435c557282cd778ae1ae16dded6e84db121d3ab27b931726bd48e2ac",
"timestamp": "0x5a",
"totalDifficulty": "0x9",
"transactions": [
{
- "blockHash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365",
+ "blockHash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c",
"blockNumber": "0x9",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gas": "0x5208",
diff --git a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json
index 7917a2acc7..02d29dd90b 100644
--- a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json
+++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json
@@ -5,17 +5,17 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x7a1200",
"gasUsed": "0x5208",
- "hash": "0x1ec39e7ec46f8df1fb31cfca53fbf71a01869af8bd8f9a1bccbffc16ffa1461d",
+ "hash": "0xb64852ab9008988697eb2bc3aec63b97d79c77ff81df2de60535ef31453a5979",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0xa",
- "parentHash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365",
+ "parentHash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x2bb",
- "stateRoot": "0x7e06187d15d50badf60930290fb292ebe43e79553ad8b7d8f1b614316631def7",
+ "stateRoot": "0x2b8307a066fd4575aca1fa6b42ef2c3a860a9c40337ea0d647c9f25e22062e34",
"timestamp": "0x64",
"totalDifficulty": "0xa",
"transactions": [
diff --git a/internal/ethapi/testdata/eth_getBlockByNumber-number-0.json b/internal/ethapi/testdata/eth_getBlockByNumber-number-0.json
index 446f5db6ab..e8f06e2d94 100644
--- a/internal/ethapi/testdata/eth_getBlockByNumber-number-0.json
+++ b/internal/ethapi/testdata/eth_getBlockByNumber-number-0.json
@@ -4,7 +4,7 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x0",
- "hash": "0x3ead7923676a44500c46ad2192a0fc084aa42063b1703e6866f138a47fb1a9ca",
+ "hash": "0xa779a1e82fd903ae98affe3a06a0a353906130052c24969b24237adf350bd03b",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
@@ -14,7 +14,7 @@
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x201",
- "stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2",
+ "stateRoot": "0xd883f48b83cc9c1e8389453beb4ad4e572462eec049ca4fffbe16ecefb3fe937",
"timestamp": "0x0",
"totalDifficulty": "0x0",
"transactions": [],
diff --git a/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json b/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json
index a21af855b8..3835567741 100644
--- a/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json
+++ b/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json
@@ -5,17 +5,17 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x7a1200",
"gasUsed": "0x5208",
- "hash": "0x0f67ad1fc8052afad4c24551748600c164091cf37e068adef76315025d3c78e7",
+ "hash": "0xb0a6fc2230444f86583fec57e9fa0aef3ebf36e094b5f2abeb231089697c50e0",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x1",
- "parentHash": "0x3ead7923676a44500c46ad2192a0fc084aa42063b1703e6866f138a47fb1a9ca",
+ "parentHash": "0xa779a1e82fd903ae98affe3a06a0a353906130052c24969b24237adf350bd03b",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x2bb",
- "stateRoot": "0x6b830601767ac4968163193facbe20123435180e325910b2c50efa21f778c697",
+ "stateRoot": "0xa17c770ca1e18e544a4a6d14b611cd03b505d465cc967ba5162d59ce459b553d",
"timestamp": "0xa",
"totalDifficulty": "0x1",
"transactions": [
diff --git a/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json b/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json
index 4d54e3f10b..8f55c154c0 100644
--- a/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json
+++ b/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json
@@ -5,22 +5,22 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x7a1200",
"gasUsed": "0x5208",
- "hash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365",
+ "hash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x9",
- "parentHash": "0x2fab5c6892c66668842683ced6b384c2ee83bfd6096a58f451290cabaf57a63e",
+ "parentHash": "0x4f8c1e68991fdb03a668c72e42c454dc911f34416035ef59fdeb83218fe4ae84",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x2bb",
- "stateRoot": "0x3703d70c6443e809ce035c2a8212dbf9813f6b7d1b0f597766e9023867a852f5",
+ "stateRoot": "0x67e974f6435c557282cd778ae1ae16dded6e84db121d3ab27b931726bd48e2ac",
"timestamp": "0x5a",
"totalDifficulty": "0x9",
"transactions": [
{
- "blockHash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365",
+ "blockHash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c",
"blockNumber": "0x9",
"from": "0x703c4b2bd70c169f5717101caee543299fc946c7",
"gas": "0x5208",
diff --git a/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json b/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json
index 7917a2acc7..02d29dd90b 100644
--- a/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json
+++ b/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json
@@ -5,17 +5,17 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x7a1200",
"gasUsed": "0x5208",
- "hash": "0x1ec39e7ec46f8df1fb31cfca53fbf71a01869af8bd8f9a1bccbffc16ffa1461d",
+ "hash": "0xb64852ab9008988697eb2bc3aec63b97d79c77ff81df2de60535ef31453a5979",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0xa",
- "parentHash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365",
+ "parentHash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x2bb",
- "stateRoot": "0x7e06187d15d50badf60930290fb292ebe43e79553ad8b7d8f1b614316631def7",
+ "stateRoot": "0x2b8307a066fd4575aca1fa6b42ef2c3a860a9c40337ea0d647c9f25e22062e34",
"timestamp": "0x64",
"totalDifficulty": "0xa",
"transactions": [
diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json
index 5d888be080..24afbebc14 100644
--- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json
+++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-blob-tx.json
@@ -2,7 +2,7 @@
{
"blobGasPrice": "0x1",
"blobGasUsed": "0x20000",
- "blockHash": "0x264b5f62e2900dd39a6c68af3ba656cffa3fe209614ca857af1f5702c6e2ba7e",
+ "blockHash": "0xff73bf27fcb39258f9c4a2f7f6eb018f7029f2d5bd960a63ebb4d7ecd8044545",
"blockNumber": "0x6",
"contractAddress": null,
"cumulativeGasUsed": "0x5208",
@@ -17,4 +17,4 @@
"transactionIndex": "0x0",
"type": "0x3"
}
-]
+]
\ No newline at end of file
diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json
index 1ef955ab92..84f48f0ea5 100644
--- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json
+++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-contract-create-tx.json
@@ -1,6 +1,6 @@
[
{
- "blockHash": "0x0524420ca4d3974c72883de6ccdeca2e8be7eafeac88ff03d144ed16fe78063a",
+ "blockHash": "0xdcba2f7c99ad0f58002737f1393578f1b72aca3270c1722d9d0fbdc2439b0484",
"blockNumber": "0x2",
"contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592",
"cumulativeGasUsed": "0xcf50",
@@ -15,4 +15,4 @@
"transactionIndex": "0x0",
"type": "0x0"
}
-]
+]
\ No newline at end of file
diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json
index 96ca04b629..977cbe81df 100644
--- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json
+++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-dynamic-fee-tx.json
@@ -1,6 +1,6 @@
[
{
- "blockHash": "0x0b7437b9229f72a563918ee8c73e9a8f5e294f9d0e17db6bf8408cdf6fbc84b7",
+ "blockHash": "0xfa29fee1c5195fda47b23d3ce5259e314eb7578d18b76b36068d0e321db024e1",
"blockNumber": "0x4",
"contractAddress": null,
"cumulativeGasUsed": "0x538d",
@@ -15,4 +15,4 @@
"transactionIndex": "0x0",
"type": "0x2"
}
-]
+]
\ No newline at end of file
diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json
index f63bffdf0a..78bec365a3 100644
--- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json
+++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-contract-call-tx.json
@@ -1,6 +1,6 @@
[
{
- "blockHash": "0x587918ec6a2187a54418dc5334d02a53b61c39578d095f4845be8d4f25dea5b5",
+ "blockHash": "0xd82a2ecb764df020971408362eed2f6521c9a7ae38ab991aba7c6ddd60ba7e64",
"blockNumber": "0x3",
"contractAddress": null,
"cumulativeGasUsed": "0x5e28",
@@ -19,7 +19,7 @@
"blockNumber": "0x3",
"transactionHash": "0x7366a7738f47e32f5b6d292ca064b6b66f295d3931533a3745975be1191fccdf",
"transactionIndex": "0x0",
- "blockHash": "0x587918ec6a2187a54418dc5334d02a53b61c39578d095f4845be8d4f25dea5b5",
+ "blockHash": "0xd82a2ecb764df020971408362eed2f6521c9a7ae38ab991aba7c6ddd60ba7e64",
"logIndex": "0x0",
"removed": false
}
@@ -31,4 +31,4 @@
"transactionIndex": "0x0",
"type": "0x0"
}
-]
+]
\ No newline at end of file
diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json
index 6a7af4ce3c..e4c599bf22 100644
--- a/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json
+++ b/internal/ethapi/testdata/eth_getBlockReceipts-block-with-legacy-transfer-tx.json
@@ -1,6 +1,6 @@
[
{
- "blockHash": "0x2918b59e4b455614c1b83c0281e2b8462af47ca9726fff31789cb168793015d7",
+ "blockHash": "0xf9081fe79fcdfd6a743577cc42fa17bec5e6cc1ebf5807b771724bf88b454b71",
"blockNumber": "0x1",
"contractAddress": null,
"cumulativeGasUsed": "0x5208",
@@ -15,4 +15,4 @@
"transactionIndex": "0x0",
"type": "0x0"
}
-]
+]
\ No newline at end of file
diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json b/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json
index 5d888be080..24afbebc14 100644
--- a/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json
+++ b/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json
@@ -2,7 +2,7 @@
{
"blobGasPrice": "0x1",
"blobGasUsed": "0x20000",
- "blockHash": "0x264b5f62e2900dd39a6c68af3ba656cffa3fe209614ca857af1f5702c6e2ba7e",
+ "blockHash": "0xff73bf27fcb39258f9c4a2f7f6eb018f7029f2d5bd960a63ebb4d7ecd8044545",
"blockNumber": "0x6",
"contractAddress": null,
"cumulativeGasUsed": "0x5208",
@@ -17,4 +17,4 @@
"transactionIndex": "0x0",
"type": "0x3"
}
-]
+]
\ No newline at end of file
diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-0.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-0.json
index eced05ccfc..2d905eb44b 100644
--- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-0.json
+++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-0.json
@@ -4,7 +4,7 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x0",
- "hash": "0x3ead7923676a44500c46ad2192a0fc084aa42063b1703e6866f138a47fb1a9ca",
+ "hash": "0xa779a1e82fd903ae98affe3a06a0a353906130052c24969b24237adf350bd03b",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
@@ -13,7 +13,7 @@
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2",
+ "stateRoot": "0xd883f48b83cc9c1e8389453beb4ad4e572462eec049ca4fffbe16ecefb3fe937",
"timestamp": "0x0",
"totalDifficulty": "0x0",
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json
index d14b952ff6..b400b4ef33 100644
--- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json
+++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json
@@ -5,16 +5,16 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x7a1200",
"gasUsed": "0x5208",
- "hash": "0x0f67ad1fc8052afad4c24551748600c164091cf37e068adef76315025d3c78e7",
+ "hash": "0xb0a6fc2230444f86583fec57e9fa0aef3ebf36e094b5f2abeb231089697c50e0",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x1",
- "parentHash": "0x3ead7923676a44500c46ad2192a0fc084aa42063b1703e6866f138a47fb1a9ca",
+ "parentHash": "0xa779a1e82fd903ae98affe3a06a0a353906130052c24969b24237adf350bd03b",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0x6b830601767ac4968163193facbe20123435180e325910b2c50efa21f778c697",
+ "stateRoot": "0xa17c770ca1e18e544a4a6d14b611cd03b505d465cc967ba5162d59ce459b553d",
"timestamp": "0xa",
"totalDifficulty": "0x1",
"transactionsRoot": "0x87c65a3f1a98dafe282ace11eaf88b8f31bf41fe6794d401d2f986c1af84bcd5"
diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json
index c90f186501..ed8758d9a0 100644
--- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json
+++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json
@@ -5,16 +5,16 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x7a1200",
"gasUsed": "0x5208",
- "hash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365",
+ "hash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x9",
- "parentHash": "0x2fab5c6892c66668842683ced6b384c2ee83bfd6096a58f451290cabaf57a63e",
+ "parentHash": "0x4f8c1e68991fdb03a668c72e42c454dc911f34416035ef59fdeb83218fe4ae84",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0x3703d70c6443e809ce035c2a8212dbf9813f6b7d1b0f597766e9023867a852f5",
+ "stateRoot": "0x67e974f6435c557282cd778ae1ae16dded6e84db121d3ab27b931726bd48e2ac",
"timestamp": "0x5a",
"totalDifficulty": "0x9",
"transactionsRoot": "0xe16929d9c7efab0f962c1ed8c1295ddff42d3026779ed1318ea079ca580ee4cb"
diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json
index ce691fa435..1ac22e8b43 100644
--- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json
+++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json
@@ -5,16 +5,16 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x7a1200",
"gasUsed": "0x5208",
- "hash": "0x1ec39e7ec46f8df1fb31cfca53fbf71a01869af8bd8f9a1bccbffc16ffa1461d",
+ "hash": "0xb64852ab9008988697eb2bc3aec63b97d79c77ff81df2de60535ef31453a5979",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0xa",
- "parentHash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365",
+ "parentHash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0x7e06187d15d50badf60930290fb292ebe43e79553ad8b7d8f1b614316631def7",
+ "stateRoot": "0x2b8307a066fd4575aca1fa6b42ef2c3a860a9c40337ea0d647c9f25e22062e34",
"timestamp": "0x64",
"totalDifficulty": "0xa",
"transactionsRoot": "0x69ff8003291e1cd08f75d174f070618f7291e4540b2e33f60b3375743e3fda01"
diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-number-0.json b/internal/ethapi/testdata/eth_getHeaderByNumber-number-0.json
index eced05ccfc..2d905eb44b 100644
--- a/internal/ethapi/testdata/eth_getHeaderByNumber-number-0.json
+++ b/internal/ethapi/testdata/eth_getHeaderByNumber-number-0.json
@@ -4,7 +4,7 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x0",
- "hash": "0x3ead7923676a44500c46ad2192a0fc084aa42063b1703e6866f138a47fb1a9ca",
+ "hash": "0xa779a1e82fd903ae98affe3a06a0a353906130052c24969b24237adf350bd03b",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
@@ -13,7 +13,7 @@
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0xfe168c5e9584a85927212e5bea5304bb7d0d8a893453b4b2c52176a72f585ae2",
+ "stateRoot": "0xd883f48b83cc9c1e8389453beb4ad4e572462eec049ca4fffbe16ecefb3fe937",
"timestamp": "0x0",
"totalDifficulty": "0x0",
"transactionsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json b/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json
index d14b952ff6..b400b4ef33 100644
--- a/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json
+++ b/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json
@@ -5,16 +5,16 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x7a1200",
"gasUsed": "0x5208",
- "hash": "0x0f67ad1fc8052afad4c24551748600c164091cf37e068adef76315025d3c78e7",
+ "hash": "0xb0a6fc2230444f86583fec57e9fa0aef3ebf36e094b5f2abeb231089697c50e0",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x1",
- "parentHash": "0x3ead7923676a44500c46ad2192a0fc084aa42063b1703e6866f138a47fb1a9ca",
+ "parentHash": "0xa779a1e82fd903ae98affe3a06a0a353906130052c24969b24237adf350bd03b",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0x6b830601767ac4968163193facbe20123435180e325910b2c50efa21f778c697",
+ "stateRoot": "0xa17c770ca1e18e544a4a6d14b611cd03b505d465cc967ba5162d59ce459b553d",
"timestamp": "0xa",
"totalDifficulty": "0x1",
"transactionsRoot": "0x87c65a3f1a98dafe282ace11eaf88b8f31bf41fe6794d401d2f986c1af84bcd5"
diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json b/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json
index c90f186501..ed8758d9a0 100644
--- a/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json
+++ b/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json
@@ -5,16 +5,16 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x7a1200",
"gasUsed": "0x5208",
- "hash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365",
+ "hash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x9",
- "parentHash": "0x2fab5c6892c66668842683ced6b384c2ee83bfd6096a58f451290cabaf57a63e",
+ "parentHash": "0x4f8c1e68991fdb03a668c72e42c454dc911f34416035ef59fdeb83218fe4ae84",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0x3703d70c6443e809ce035c2a8212dbf9813f6b7d1b0f597766e9023867a852f5",
+ "stateRoot": "0x67e974f6435c557282cd778ae1ae16dded6e84db121d3ab27b931726bd48e2ac",
"timestamp": "0x5a",
"totalDifficulty": "0x9",
"transactionsRoot": "0xe16929d9c7efab0f962c1ed8c1295ddff42d3026779ed1318ea079ca580ee4cb"
diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json b/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json
index ce691fa435..1ac22e8b43 100644
--- a/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json
+++ b/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json
@@ -5,16 +5,16 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0x7a1200",
"gasUsed": "0x5208",
- "hash": "0x1ec39e7ec46f8df1fb31cfca53fbf71a01869af8bd8f9a1bccbffc16ffa1461d",
+ "hash": "0xb64852ab9008988697eb2bc3aec63b97d79c77ff81df2de60535ef31453a5979",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0xa",
- "parentHash": "0x0583a9d630632001771b4ecc7d62574aec3825aff47e2a680b0ea4ddb79e7365",
+ "parentHash": "0x71f4093e0536817246679d6e183d836710027189eed7d0e09f1fde11a26f499c",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0x7e06187d15d50badf60930290fb292ebe43e79553ad8b7d8f1b614316631def7",
+ "stateRoot": "0x2b8307a066fd4575aca1fa6b42ef2c3a860a9c40337ea0d647c9f25e22062e34",
"timestamp": "0x64",
"totalDifficulty": "0xa",
"transactionsRoot": "0x69ff8003291e1cd08f75d174f070618f7291e4540b2e33f60b3375743e3fda01"
diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json
index e0db84ad00..674b7e4f28 100644
--- a/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json
+++ b/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json
@@ -1,7 +1,7 @@
{
"blobGasPrice": "0x1",
"blobGasUsed": "0x20000",
- "blockHash": "0x264b5f62e2900dd39a6c68af3ba656cffa3fe209614ca857af1f5702c6e2ba7e",
+ "blockHash": "0xff73bf27fcb39258f9c4a2f7f6eb018f7029f2d5bd960a63ebb4d7ecd8044545",
"blockNumber": "0x6",
"contractAddress": null,
"cumulativeGasUsed": "0x5208",
@@ -15,4 +15,4 @@
"transactionHash": "0x7e71344129674f4bbfdaa86313d005a96581993d93ae3a30d81b13fa25579eb2",
"transactionIndex": "0x0",
"type": "0x3"
-}
+}
\ No newline at end of file
diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json
index 41206a6c7a..0345b77cef 100644
--- a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json
+++ b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json
@@ -1,5 +1,5 @@
{
- "blockHash": "0x0524420ca4d3974c72883de6ccdeca2e8be7eafeac88ff03d144ed16fe78063a",
+ "blockHash": "0xdcba2f7c99ad0f58002737f1393578f1b72aca3270c1722d9d0fbdc2439b0484",
"blockNumber": "0x2",
"contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592",
"cumulativeGasUsed": "0xcf50",
@@ -13,4 +13,4 @@
"transactionHash": "0x22aa617165f83a9f8c191c2b7724ae43eeb1249bee06c98c03c7624c21d27dc8",
"transactionIndex": "0x0",
"type": "0x0"
-}
+}
\ No newline at end of file
diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json
index 0fb927479e..2d68a68bd9 100644
--- a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json
+++ b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-with-access-list.json
@@ -1,5 +1,5 @@
{
- "blockHash": "0x2bf952c71ce24c68f887c12ecba6f1a36f97f528259fe0574890969e1113187a",
+ "blockHash": "0xea28a367715debefc3b6d9f5ed5ab5b8d3a13b956e87f05f148852d3e1e522b5",
"blockNumber": "0x5",
"contractAddress": "0xfdaa97661a584d977b4d3abb5370766ff5b86a18",
"cumulativeGasUsed": "0xe01c",
@@ -13,4 +13,4 @@
"transactionHash": "0x8afe030574f663fe5096371d6f58a6287bfb3e0c73a5050220f5775a08e7abc9",
"transactionIndex": "0x0",
"type": "0x1"
-}
+}
\ No newline at end of file
diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json b/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json
index c322944f2b..6b558601b2 100644
--- a/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json
+++ b/internal/ethapi/testdata/eth_getTransactionReceipt-dynamic-tx-with-logs.json
@@ -1,5 +1,5 @@
{
- "blockHash": "0x0b7437b9229f72a563918ee8c73e9a8f5e294f9d0e17db6bf8408cdf6fbc84b7",
+ "blockHash": "0xfa29fee1c5195fda47b23d3ce5259e314eb7578d18b76b36068d0e321db024e1",
"blockNumber": "0x4",
"contractAddress": null,
"cumulativeGasUsed": "0x538d",
@@ -13,4 +13,4 @@
"transactionHash": "0x4e1e9194ca6f9d4e1736e9e441f66104f273548ed6d91b236a5f9c2ea10fa06d",
"transactionIndex": "0x0",
"type": "0x2"
-}
+}
\ No newline at end of file
diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json
index be72a02737..8411f0b255 100644
--- a/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json
+++ b/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json
@@ -1,5 +1,5 @@
{
- "blockHash": "0x2918b59e4b455614c1b83c0281e2b8462af47ca9726fff31789cb168793015d7",
+ "blockHash": "0xf9081fe79fcdfd6a743577cc42fa17bec5e6cc1ebf5807b771724bf88b454b71",
"blockNumber": "0x1",
"contractAddress": null,
"cumulativeGasUsed": "0x5208",
@@ -13,4 +13,4 @@
"transactionHash": "0xdf92bc7c4c0341ecbdcd2a3ca7011fe9e21df4b8553bf0c8caabe6cb4a1aee26",
"transactionIndex": "0x0",
"type": "0x0"
-}
+}
\ No newline at end of file
diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json b/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json
index 7ced85c2a2..6aebc6190b 100644
--- a/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json
+++ b/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json
@@ -1,5 +1,5 @@
{
- "blockHash": "0x587918ec6a2187a54418dc5334d02a53b61c39578d095f4845be8d4f25dea5b5",
+ "blockHash": "0xd82a2ecb764df020971408362eed2f6521c9a7ae38ab991aba7c6ddd60ba7e64",
"blockNumber": "0x3",
"contractAddress": null,
"cumulativeGasUsed": "0x5e28",
@@ -18,7 +18,7 @@
"blockNumber": "0x3",
"transactionHash": "0x7366a7738f47e32f5b6d292ca064b6b66f295d3931533a3745975be1191fccdf",
"transactionIndex": "0x0",
- "blockHash": "0x587918ec6a2187a54418dc5334d02a53b61c39578d095f4845be8d4f25dea5b5",
+ "blockHash": "0xd82a2ecb764df020971408362eed2f6521c9a7ae38ab991aba7c6ddd60ba7e64",
"logIndex": "0x0",
"removed": false
}
@@ -29,4 +29,4 @@
"transactionHash": "0x7366a7738f47e32f5b6d292ca064b6b66f295d3931533a3745975be1191fccdf",
"transactionIndex": "0x0",
"type": "0x0"
-}
+}
\ No newline at end of file
diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go
index 9dd9ede18d..3be1289947 100644
--- a/internal/ethapi/transaction_args.go
+++ b/internal/ethapi/transaction_args.go
@@ -29,10 +29,12 @@ package ethapi
import (
"bytes"
"context"
+ "crypto/sha256"
"errors"
"fmt"
"math/big"
+ "github.com/ava-labs/subnet-evm/consensus/misc/eip4844"
"github.com/ava-labs/subnet-evm/core"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/params"
@@ -40,7 +42,13 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/ethereum/go-ethereum/log"
+ "github.com/holiman/uint256"
+)
+
+var (
+ maxBlobsPerTransaction = params.MaxBlobGasPerBlock / params.BlobTxBlobGasPerBlob
)
// TransactionArgs represents the arguments to construct a new transaction
@@ -64,6 +72,18 @@ type TransactionArgs struct {
// Introduced by AccessListTxType transaction.
AccessList *types.AccessList `json:"accessList,omitempty"`
ChainID *hexutil.Big `json:"chainId,omitempty"`
+
+ // For BlobTxType
+ BlobFeeCap *hexutil.Big `json:"maxFeePerBlobGas"`
+ BlobHashes []common.Hash `json:"blobVersionedHashes,omitempty"`
+
+ // For BlobTxType transactions with blob sidecar
+ Blobs []kzg4844.Blob `json:"blobs"`
+ Commitments []kzg4844.Commitment `json:"commitments"`
+ Proofs []kzg4844.Proof `json:"proofs"`
+
+ // This configures whether blobs are allowed to be passed.
+ blobSidecarAllowed bool
}
// from retrieves the transaction sender address.
@@ -86,10 +106,14 @@ func (args *TransactionArgs) data() []byte {
}
// setDefaults fills in default values for unspecified tx fields.
-func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error {
+func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGasEstimation bool) error {
+ if err := args.setBlobTxSidecar(ctx, b); err != nil {
+ return err
+ }
if err := args.setFeeDefaults(ctx, b); err != nil {
return err
}
+
if args.Value == nil {
args.Value = new(hexutil.Big)
}
@@ -103,32 +127,58 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error {
if args.Data != nil && args.Input != nil && !bytes.Equal(*args.Data, *args.Input) {
return errors.New(`both "data" and "input" are set and not equal. Please use "input" to pass transaction call data`)
}
- if args.To == nil && len(args.data()) == 0 {
- return errors.New(`contract creation without any data provided`)
+
+ // BlobTx fields
+ if args.BlobHashes != nil && len(args.BlobHashes) == 0 {
+ return errors.New(`need at least 1 blob for a blob transaction`)
}
- // Estimate the gas usage if necessary.
- if args.Gas == nil {
- // These fields are immutable during the estimation, safe to
- // pass the pointer directly.
- data := args.data()
- callArgs := TransactionArgs{
- From: args.From,
- To: args.To,
- GasPrice: args.GasPrice,
- MaxFeePerGas: args.MaxFeePerGas,
- MaxPriorityFeePerGas: args.MaxPriorityFeePerGas,
- Value: args.Value,
- Data: (*hexutil.Bytes)(&data),
- AccessList: args.AccessList,
+ if args.BlobHashes != nil && len(args.BlobHashes) > maxBlobsPerTransaction {
+ return fmt.Errorf(`too many blobs in transaction (have=%d, max=%d)`, len(args.BlobHashes), maxBlobsPerTransaction)
+ }
+
+ // create check
+ if args.To == nil {
+ if args.BlobHashes != nil {
+ return errors.New(`missing "to" in blob transaction`)
}
- pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
- estimated, err := DoEstimateGas(ctx, b, callArgs, pendingBlockNr, nil, b.RPCGasCap())
- if err != nil {
- return err
+ if len(args.data()) == 0 {
+ return errors.New(`contract creation without any data provided`)
}
- args.Gas = &estimated
- log.Trace("Estimate gas usage automatically", "gas", args.Gas)
}
+
+ if args.Gas == nil {
+ if skipGasEstimation { // Skip gas usage estimation if a precise gas limit is not critical, e.g., in non-transaction calls.
+ gas := hexutil.Uint64(b.RPCGasCap())
+ if gas == 0 {
+ gas = hexutil.Uint64(math.MaxUint64 / 2)
+ }
+ args.Gas = &gas
+ } else { // Estimate the gas usage otherwise.
+ // These fields are immutable during the estimation, safe to
+ // pass the pointer directly.
+ data := args.data()
+ callArgs := TransactionArgs{
+ From: args.From,
+ To: args.To,
+ GasPrice: args.GasPrice,
+ MaxFeePerGas: args.MaxFeePerGas,
+ MaxPriorityFeePerGas: args.MaxPriorityFeePerGas,
+ Value: args.Value,
+ Data: (*hexutil.Bytes)(&data),
+ AccessList: args.AccessList,
+ BlobFeeCap: args.BlobFeeCap,
+ BlobHashes: args.BlobHashes,
+ }
+ latestBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
+ estimated, err := DoEstimateGas(ctx, b, callArgs, latestBlockNr, nil, b.RPCGasCap())
+ if err != nil {
+ return err
+ }
+ args.Gas = &estimated
+ log.Trace("Estimate gas usage automatically", "gas", args.Gas)
+ }
+ }
+
// If chain id is provided, ensure it matches the local chain id. Otherwise, set the local
// chain id as the default.
want := b.ChainConfig().ChainID
@@ -150,6 +200,14 @@ type feeBackend interface {
// setFeeDefaults fills in default fee values for unspecified tx fields.
func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b feeBackend) error {
+ head := b.CurrentHeader()
+ // Sanity check the EIP-4844 fee parameters.
+ if args.BlobFeeCap != nil && args.BlobFeeCap.ToInt().Sign() == 0 {
+ return errors.New("maxFeePerBlobGas, if specified, must be non-zero")
+ }
+ if err := args.setCancunFeeDefaults(ctx, head, b); err != nil {
+ return err
+ }
// If both gasPrice and at least one of the EIP-1559 fee parameters are specified, error.
if args.GasPrice != nil && (args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil) {
return errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
@@ -159,7 +217,6 @@ func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b feeBackend) e
// other tx values. See https://github.com/ethereum/go-ethereum/pull/23274
// for more information.
eip1559ParamsSet := args.MaxFeePerGas != nil && args.MaxPriorityFeePerGas != nil
-
// Sanity check the EIP-1559 fee parameters if present.
if args.GasPrice == nil && eip1559ParamsSet {
if args.MaxFeePerGas.ToInt().Sign() == 0 {
@@ -170,8 +227,8 @@ func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b feeBackend) e
}
return nil // No need to set anything, user already set MaxFeePerGas and MaxPriorityFeePerGas
}
+
// Sanity check the non-EIP-1559 fee parameters.
- head := b.CurrentHeader()
isLondon := b.ChainConfig().IsSubnetEVM(head.Time)
if args.GasPrice != nil && !eip1559ParamsSet {
// Zero gas-price is not allowed after London fork
@@ -201,7 +258,26 @@ func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b feeBackend) e
return nil
}
-// setSubnetEVMFeeDefault fills in reasonable default fee values for unspecified fields.
+// setCancunFeeDefaults fills in reasonable default fee values for unspecified fields.
+func (args *TransactionArgs) setCancunFeeDefaults(ctx context.Context, head *types.Header, b feeBackend) error {
+ // Set maxFeePerBlobGas if it is missing.
+ if args.BlobHashes != nil && args.BlobFeeCap == nil {
+ var excessBlobGas uint64
+ if head.ExcessBlobGas != nil {
+ excessBlobGas = *head.ExcessBlobGas
+ }
+ // ExcessBlobGas must be set for a Cancun block.
+ blobBaseFee := eip4844.CalcBlobFee(excessBlobGas)
+ // Set the max fee to be 2 times larger than the previous block's blob base fee.
+ // The additional slack allows the tx to not become invalidated if the base
+ // fee is rising.
+ val := new(big.Int).Mul(blobBaseFee, big.NewInt(2))
+ args.BlobFeeCap = (*hexutil.Big)(val)
+ }
+ return nil
+}
+
+// setSubnetEVMFeeDefaults fills in reasonable default fee values for unspecified fields.
func (args *TransactionArgs) setSubnetEVMFeeDefault(ctx context.Context, head *types.Header, b feeBackend) error {
// Set maxPriorityFeePerGas if it is missing.
if args.MaxPriorityFeePerGas == nil {
@@ -229,6 +305,81 @@ func (args *TransactionArgs) setSubnetEVMFeeDefault(ctx context.Context, head *t
return nil
}
+// setBlobTxSidecar adds the blob tx
+func (args *TransactionArgs) setBlobTxSidecar(ctx context.Context, b Backend) error {
+ // No blobs, we're done.
+ if args.Blobs == nil {
+ return nil
+ }
+
+ // Passing blobs is not allowed in all contexts, only in specific methods.
+ if !args.blobSidecarAllowed {
+ return errors.New(`"blobs" is not supported for this RPC method`)
+ }
+
+ n := len(args.Blobs)
+ // Assume user provides either only blobs (w/o hashes), or
+ // blobs together with commitments and proofs.
+ if args.Commitments == nil && args.Proofs != nil {
+ return errors.New(`blob proofs provided while commitments were not`)
+ } else if args.Commitments != nil && args.Proofs == nil {
+ return errors.New(`blob commitments provided while proofs were not`)
+ }
+
+ // len(blobs) == len(commitments) == len(proofs) == len(hashes)
+ if args.Commitments != nil && len(args.Commitments) != n {
+ return fmt.Errorf("number of blobs and commitments mismatch (have=%d, want=%d)", len(args.Commitments), n)
+ }
+ if args.Proofs != nil && len(args.Proofs) != n {
+ return fmt.Errorf("number of blobs and proofs mismatch (have=%d, want=%d)", len(args.Proofs), n)
+ }
+ if args.BlobHashes != nil && len(args.BlobHashes) != n {
+ return fmt.Errorf("number of blobs and hashes mismatch (have=%d, want=%d)", len(args.BlobHashes), n)
+ }
+
+ if args.Commitments == nil {
+ // Generate commitment and proof.
+ commitments := make([]kzg4844.Commitment, n)
+ proofs := make([]kzg4844.Proof, n)
+ for i, b := range args.Blobs {
+ c, err := kzg4844.BlobToCommitment(b)
+ if err != nil {
+ return fmt.Errorf("blobs[%d]: error computing commitment: %v", i, err)
+ }
+ commitments[i] = c
+ p, err := kzg4844.ComputeBlobProof(b, c)
+ if err != nil {
+ return fmt.Errorf("blobs[%d]: error computing proof: %v", i, err)
+ }
+ proofs[i] = p
+ }
+ args.Commitments = commitments
+ args.Proofs = proofs
+ } else {
+ for i, b := range args.Blobs {
+ if err := kzg4844.VerifyBlobProof(b, args.Commitments[i], args.Proofs[i]); err != nil {
+ return fmt.Errorf("failed to verify blob proof: %v", err)
+ }
+ }
+ }
+
+ hashes := make([]common.Hash, n)
+ hasher := sha256.New()
+ for i, c := range args.Commitments {
+ hashes[i] = kzg4844.CalcBlobHashV1(hasher, &c)
+ }
+ if args.BlobHashes != nil {
+ for i, h := range hashes {
+ if h != args.BlobHashes[i] {
+ return fmt.Errorf("blob hash verification failed (have=%s, want=%s)", args.BlobHashes[i], h)
+ }
+ }
+ } else {
+ args.BlobHashes = hashes
+ }
+ return nil
+}
+
// ToMessage converts the transaction arguments to the Message type used by the
// core evm. This method is used in calls and traces that do not require a real
// live transaction.
@@ -253,9 +404,10 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (*
gas = globalGasCap
}
var (
- gasPrice *big.Int
- gasFeeCap *big.Int
- gasTipCap *big.Int
+ gasPrice *big.Int
+ gasFeeCap *big.Int
+ gasTipCap *big.Int
+ blobFeeCap *big.Int
)
if baseFee == nil {
// If there's no basefee, then it must be a non-1559 execution
@@ -287,6 +439,11 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (*
}
}
}
+ if args.BlobFeeCap != nil {
+ blobFeeCap = args.BlobFeeCap.ToInt()
+ } else if args.BlobHashes != nil {
+ blobFeeCap = new(big.Int)
+ }
value := new(big.Int)
if args.Value != nil {
value = args.Value.ToInt()
@@ -306,6 +463,8 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (*
GasTipCap: gasTipCap,
Data: data,
AccessList: accessList,
+ BlobGasFeeCap: blobFeeCap,
+ BlobHashes: args.BlobHashes,
SkipAccountChecks: true,
}
return msg, nil
@@ -316,6 +475,32 @@ func (args *TransactionArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (*
func (args *TransactionArgs) toTransaction() *types.Transaction {
var data types.TxData
switch {
+ case args.BlobHashes != nil:
+ al := types.AccessList{}
+ if args.AccessList != nil {
+ al = *args.AccessList
+ }
+ data = &types.BlobTx{
+ To: *args.To,
+ ChainID: uint256.MustFromBig((*big.Int)(args.ChainID)),
+ Nonce: uint64(*args.Nonce),
+ Gas: uint64(*args.Gas),
+ GasFeeCap: uint256.MustFromBig((*big.Int)(args.MaxFeePerGas)),
+ GasTipCap: uint256.MustFromBig((*big.Int)(args.MaxPriorityFeePerGas)),
+ Value: uint256.MustFromBig((*big.Int)(args.Value)),
+ Data: args.data(),
+ AccessList: al,
+ BlobHashes: args.BlobHashes,
+ BlobFeeCap: uint256.MustFromBig((*big.Int)(args.BlobFeeCap)),
+ }
+ if args.Blobs != nil {
+ data.(*types.BlobTx).Sidecar = &types.BlobTxSidecar{
+ Blobs: args.Blobs,
+ Commitments: args.Commitments,
+ Proofs: args.Proofs,
+ }
+ }
+
case args.MaxFeePerGas != nil:
al := types.AccessList{}
if args.AccessList != nil {
@@ -332,6 +517,7 @@ func (args *TransactionArgs) toTransaction() *types.Transaction {
Data: args.data(),
AccessList: al,
}
+
case args.AccessList != nil:
data = &types.AccessListTx{
To: args.To,
@@ -343,6 +529,7 @@ func (args *TransactionArgs) toTransaction() *types.Transaction {
Data: args.data(),
AccessList: *args.AccessList,
}
+
default:
data = &types.LegacyTx{
To: args.To,
@@ -356,8 +543,7 @@ func (args *TransactionArgs) toTransaction() *types.Transaction {
return types.NewTx(data)
}
-// ToTransaction converts the arguments to a transaction.
-// This assumes that setDefaults has been called.
-func (args *TransactionArgs) ToTransaction() *types.Transaction {
- return args.toTransaction()
+// IsEIP4844 returns an indicator if the args contains EIP4844 fields.
+func (args *TransactionArgs) IsEIP4844() bool {
+ return args.BlobHashes != nil || args.BlobFeeCap != nil
}
diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go
index 98bdde2edf..a439553909 100644
--- a/internal/ethapi/transaction_args_test.go
+++ b/internal/ethapi/transaction_args_test.go
@@ -45,11 +45,11 @@ var _ feeBackend = &backendMock{}
// TestSetFeeDefaults tests the logic for filling in default fee values works as expected.
func TestSetFeeDefaults(t *testing.T) {
type test struct {
- name string
- isLondon bool
- in *TransactionArgs
- want *TransactionArgs
- err error
+ name string
+ fork string // options: legacy, london, cancun
+ in *TransactionArgs
+ want *TransactionArgs
+ err error
}
var (
@@ -64,28 +64,28 @@ func TestSetFeeDefaults(t *testing.T) {
// Legacy txs
{
"legacy tx pre-London",
- false,
+ "legacy",
&TransactionArgs{},
&TransactionArgs{GasPrice: fortytwo},
nil,
},
{
"legacy tx pre-London with zero price",
- false,
+ "legacy",
&TransactionArgs{GasPrice: zero},
&TransactionArgs{GasPrice: zero},
nil,
},
{
"legacy tx post-London, explicit gas price",
- true,
+ "london",
&TransactionArgs{GasPrice: fortytwo},
&TransactionArgs{GasPrice: fortytwo},
nil,
},
{
"legacy tx post-London with zero price",
- true,
+ "london",
&TransactionArgs{GasPrice: zero},
nil,
errors.New("gasPrice must be non-zero after london fork"),
@@ -94,35 +94,35 @@ func TestSetFeeDefaults(t *testing.T) {
// Access list txs
{
"access list tx pre-London",
- false,
+ "legacy",
&TransactionArgs{AccessList: al},
&TransactionArgs{AccessList: al, GasPrice: fortytwo},
nil,
},
{
"access list tx post-London, explicit gas price",
- false,
+ "legacy",
&TransactionArgs{AccessList: al, GasPrice: fortytwo},
&TransactionArgs{AccessList: al, GasPrice: fortytwo},
nil,
},
{
"access list tx post-London",
- true,
+ "london",
&TransactionArgs{AccessList: al},
&TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
nil,
},
{
"access list tx post-London, only max fee",
- true,
+ "london",
&TransactionArgs{AccessList: al, MaxFeePerGas: maxFee},
&TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
nil,
},
{
"access list tx post-London, only priority fee",
- true,
+ "london",
&TransactionArgs{AccessList: al, MaxFeePerGas: maxFee},
&TransactionArgs{AccessList: al, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
nil,
@@ -131,56 +131,56 @@ func TestSetFeeDefaults(t *testing.T) {
// Dynamic fee txs
{
"dynamic tx post-London",
- true,
+ "london",
&TransactionArgs{},
&TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
nil,
},
{
"dynamic tx post-London, only max fee",
- true,
+ "london",
&TransactionArgs{MaxFeePerGas: maxFee},
&TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
nil,
},
{
"dynamic tx post-London, only priority fee",
- true,
+ "london",
&TransactionArgs{MaxFeePerGas: maxFee},
&TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
nil,
},
{
"dynamic fee tx pre-London, maxFee set",
- false,
+ "legacy",
&TransactionArgs{MaxFeePerGas: maxFee},
nil,
errors.New("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active"),
},
{
"dynamic fee tx pre-London, priorityFee set",
- false,
+ "legacy",
&TransactionArgs{MaxPriorityFeePerGas: fortytwo},
nil,
errors.New("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active"),
},
{
"dynamic fee tx, maxFee < priorityFee",
- true,
+ "london",
&TransactionArgs{MaxFeePerGas: maxFee, MaxPriorityFeePerGas: (*hexutil.Big)(big.NewInt(1000))},
nil,
errors.New("maxFeePerGas (0x3e) < maxPriorityFeePerGas (0x3e8)"),
},
{
"dynamic fee tx, maxFee < priorityFee while setting default",
- true,
+ "london",
&TransactionArgs{MaxFeePerGas: (*hexutil.Big)(big.NewInt(7))},
nil,
errors.New("maxFeePerGas (0x7) < maxPriorityFeePerGas (0x2a)"),
},
{
"dynamic fee tx post-London, explicit gas price",
- true,
+ "london",
&TransactionArgs{MaxFeePerGas: zero, MaxPriorityFeePerGas: zero},
nil,
errors.New("maxFeePerGas must be non-zero"),
@@ -189,41 +189,66 @@ func TestSetFeeDefaults(t *testing.T) {
// Misc
{
"set all fee parameters",
- false,
+ "legacy",
&TransactionArgs{GasPrice: fortytwo, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
nil,
errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"),
},
{
"set gas price and maxPriorityFee",
- false,
+ "legacy",
&TransactionArgs{GasPrice: fortytwo, MaxPriorityFeePerGas: fortytwo},
nil,
errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"),
},
{
"set gas price and maxFee",
- true,
+ "london",
&TransactionArgs{GasPrice: fortytwo, MaxFeePerGas: maxFee},
nil,
errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"),
},
+ // EIP-4844
+ {
+ "set gas price and maxFee for blob transaction",
+ "cancun",
+ &TransactionArgs{GasPrice: fortytwo, MaxFeePerGas: maxFee, BlobHashes: []common.Hash{}},
+ nil,
+ errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"),
+ },
+ {
+ "fill maxFeePerBlobGas",
+ "cancun",
+ &TransactionArgs{BlobHashes: []common.Hash{}},
+ &TransactionArgs{BlobHashes: []common.Hash{}, BlobFeeCap: (*hexutil.Big)(big.NewInt(4)), MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
+ nil,
+ },
+ {
+ "fill maxFeePerBlobGas when dynamic fees are set",
+ "cancun",
+ &TransactionArgs{BlobHashes: []common.Hash{}, MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
+ &TransactionArgs{BlobHashes: []common.Hash{}, BlobFeeCap: (*hexutil.Big)(big.NewInt(4)), MaxFeePerGas: maxFee, MaxPriorityFeePerGas: fortytwo},
+ nil,
+ },
}
ctx := context.Background()
for i, test := range tests {
- if test.isLondon {
- b.activateLondon()
- } else {
- b.deactivateLondon()
+ if err := b.setFork(test.fork); err != nil {
+ t.Fatalf("failed to set fork: %v", err)
}
got := test.in
err := got.setFeeDefaults(ctx, b)
- if err != nil && err.Error() == test.err.Error() {
- // Test threw expected error.
+ if err != nil {
+ if test.err == nil {
+ t.Fatalf("test %d (%s): unexpected error: %s", i, test.name, err)
+ } else if err.Error() != test.err.Error() {
+ t.Fatalf("test %d (%s): unexpected error: (got: %s, want: %s)", i, test.name, err, test.err)
+ }
+ // Matching error.
continue
- } else if err != nil {
- t.Fatalf("test %d (%s): unexpected error: %s", i, test.name, err)
+ } else if test.err != nil {
+ t.Fatalf("test %d (%s): expected error: %s", i, test.name, test.err)
}
if !reflect.DeepEqual(got, test.want) {
t.Fatalf("test %d (%s): did not fill defaults as expected: (got: %v, want: %v)", i, test.name, got, test.want)
@@ -237,6 +262,7 @@ type backendMock struct {
}
func newBackendMock() *backendMock {
+ var cancunTime uint64 = 600
config := ¶ms.ChainConfig{
ChainID: big.NewInt(42),
HomesteadBlock: big.NewInt(0),
@@ -249,8 +275,9 @@ func newBackendMock() *backendMock {
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
NetworkUpgrades: params.NetworkUpgrades{
- SubnetEVMTimestamp: utils.NewUint64(1000),
+ SubnetEVMTimestamp: utils.NewUint64(100),
},
+ CancunTime: &cancunTime,
}
return &backendMock{
current: &types.Header{
@@ -266,13 +293,23 @@ func newBackendMock() *backendMock {
}
}
-func (b *backendMock) activateLondon() {
- b.current.Time = uint64(1100)
+func (b *backendMock) setFork(fork string) error {
+ if fork == "legacy" {
+ b.current.Time = uint64(90) // Before SubnetEVMTimestamp
+ } else if fork == "london" {
+ b.current.Time = uint64(110) // After SubnetEVMTimestamp
+ } else if fork == "cancun" {
+ b.current.Number = big.NewInt(1100)
+ b.current.Time = 700
+ // Blob base fee will be 2
+ excess := uint64(2314058)
+ b.current.ExcessBlobGas = &excess
+ } else {
+ return errors.New("invalid fork")
+ }
+ return nil
}
-func (b *backendMock) deactivateLondon() {
- b.current.Time = uint64(900)
-}
func (b *backendMock) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
return big.NewInt(42), nil
}
diff --git a/internal/flags/flags.go b/internal/flags/flags.go
index ed3a14bcf4..167a485ab7 100644
--- a/internal/flags/flags.go
+++ b/internal/flags/flags.go
@@ -266,7 +266,8 @@ type BigFlag struct {
Hidden bool
HasBeenSet bool
- Value *big.Int
+ Value *big.Int
+ defaultValue *big.Int
Aliases []string
EnvVars []string
@@ -279,6 +280,10 @@ func (f *BigFlag) IsSet() bool { return f.HasBeenSet }
func (f *BigFlag) String() string { return cli.FlagStringer(f) }
func (f *BigFlag) Apply(set *flag.FlagSet) error {
+ // Set default value so that environment wont be able to overwrite it
+ if f.Value != nil {
+ f.defaultValue = new(big.Int).Set(f.Value)
+ }
for _, envVar := range f.EnvVars {
envVar = strings.TrimSpace(envVar)
if value, found := syscall.Getenv(envVar); found {
@@ -293,7 +298,6 @@ func (f *BigFlag) Apply(set *flag.FlagSet) error {
f.Value = new(big.Int)
set.Var((*bigValue)(f.Value), f.Name, f.Usage)
})
-
return nil
}
@@ -320,7 +324,7 @@ func (f *BigFlag) GetDefaultText() string {
if f.DefaultText != "" {
return f.DefaultText
}
- return f.GetValue()
+ return f.defaultValue.String()
}
// bigValue turns *big.Int into a flag.Value
diff --git a/internal/flags/helpers.go b/internal/flags/helpers.go
index 0724e4ccee..dc54193ec9 100644
--- a/internal/flags/helpers.go
+++ b/internal/flags/helpers.go
@@ -51,7 +51,7 @@ func NewApp(usage string) *cli.App {
app.EnableBashCompletion = true
app.Version = params.VersionWithCommit(git.Commit, git.Date)
app.Usage = usage
- app.Copyright = "Copyright 2013-2023 The go-ethereum Authors"
+ app.Copyright = "Copyright 2013-2024 The go-ethereum Authors"
app.Before = func(ctx *cli.Context) error {
MigrateGlobalFlags(ctx)
return nil
@@ -125,7 +125,7 @@ func doMigrateFlags(ctx *cli.Context) {
for _, parent := range ctx.Lineage()[1:] {
if parent.IsSet(name) {
// When iterating across the lineage, we will be served both
- // the 'canon' and alias formats of all commmands. In most cases,
+ // the 'canon' and alias formats of all commands. In most cases,
// it's fine to set it in the ctx multiple times (one for each
// name), however, the Slice-flags are not fine.
// The slice-flags accumulate, so if we set it once as
diff --git a/log/logger.go b/log/logger.go
index b3e60d6451..a3e78748a9 100644
--- a/log/logger.go
+++ b/log/logger.go
@@ -106,7 +106,7 @@ func LevelAlignedString(l slog.Level) string {
}
}
-// LevelString returns a 5-character string containing the name of a Lvl.
+// LevelString returns a string containing the name of a Lvl.
func LevelString(l slog.Level) string {
switch l {
case LevelTrace:
@@ -118,7 +118,7 @@ func LevelString(l slog.Level) string {
case slog.LevelWarn:
return "warn"
case slog.LevelError:
- return "eror"
+ return "error"
case LevelCrit:
return "crit"
default:
diff --git a/metrics/counter.go b/metrics/counter.go
index cb81599c21..dbe8e16a90 100644
--- a/metrics/counter.go
+++ b/metrics/counter.go
@@ -8,7 +8,7 @@ type CounterSnapshot interface {
Count() int64
}
-// Counters hold an int64 value that can be incremented and decremented.
+// Counter hold an int64 value that can be incremented and decremented.
type Counter interface {
Clear()
Dec(int64)
diff --git a/metrics/gauge.go b/metrics/gauge.go
index 68f8f11abc..5933df3107 100644
--- a/metrics/gauge.go
+++ b/metrics/gauge.go
@@ -2,12 +2,12 @@ package metrics
import "sync/atomic"
-// gaugeSnapshot contains a readonly int64.
+// GaugeSnapshot contains a readonly int64.
type GaugeSnapshot interface {
Value() int64
}
-// Gauges hold an int64 value that can be set arbitrarily.
+// Gauge holds an int64 value that can be set arbitrarily.
type Gauge interface {
Snapshot() GaugeSnapshot
Update(int64)
@@ -74,7 +74,7 @@ func (g *StandardGauge) Update(v int64) {
g.value.Store(v)
}
-// Update updates the gauge's value if v is larger then the current valie.
+// Update updates the gauge's value if v is larger then the current value.
func (g *StandardGauge) UpdateIfGt(v int64) {
for {
exist := g.value.Load()
diff --git a/metrics/gauge_float64.go b/metrics/gauge_float64.go
index 967f2bc60e..c1c3c6b6e6 100644
--- a/metrics/gauge_float64.go
+++ b/metrics/gauge_float64.go
@@ -48,7 +48,7 @@ type gaugeFloat64Snapshot float64
// Value returns the value at the time the snapshot was taken.
func (g gaugeFloat64Snapshot) Value() float64 { return float64(g) }
-// NilGauge is a no-op Gauge.
+// NilGaugeFloat64 is a no-op Gauge.
type NilGaugeFloat64 struct{}
func (NilGaugeFloat64) Snapshot() GaugeFloat64Snapshot { return NilGaugeFloat64{} }
diff --git a/metrics/gauge_info.go b/metrics/gauge_info.go
index c44b2d85f3..0010edc324 100644
--- a/metrics/gauge_info.go
+++ b/metrics/gauge_info.go
@@ -9,7 +9,7 @@ type GaugeInfoSnapshot interface {
Value() GaugeInfoValue
}
-// GaugeInfos hold a GaugeInfoValue value that can be set arbitrarily.
+// GaugeInfo holds a GaugeInfoValue value that can be set arbitrarily.
type GaugeInfo interface {
Update(GaugeInfoValue)
Snapshot() GaugeInfoSnapshot
diff --git a/metrics/healthcheck.go b/metrics/healthcheck.go
index f1ae31e34a..adcd15ab58 100644
--- a/metrics/healthcheck.go
+++ b/metrics/healthcheck.go
@@ -1,6 +1,6 @@
package metrics
-// Healthchecks hold an error value describing an arbitrary up/down status.
+// Healthcheck holds an error value describing an arbitrary up/down status.
type Healthcheck interface {
Check()
Error() error
diff --git a/metrics/histogram.go b/metrics/histogram.go
index 44de588bc1..10259a2463 100644
--- a/metrics/histogram.go
+++ b/metrics/histogram.go
@@ -4,7 +4,7 @@ type HistogramSnapshot interface {
SampleSnapshot
}
-// Histograms calculate distribution statistics from a series of int64 values.
+// Histogram calculates distribution statistics from a series of int64 values.
type Histogram interface {
Clear()
Update(int64)
diff --git a/miner/miner.go b/miner/miner.go
index 14e5ba8d75..a325bff0d2 100644
--- a/miner/miner.go
+++ b/miner/miner.go
@@ -47,7 +47,8 @@ type Backend interface {
// Config is the configuration parameters of mining.
type Config struct {
- Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards
+ Etherbase common.Address `toml:",omitempty"` // Public address for block mining rewards
+ TestOnlyAllowDuplicateBlocks bool // Allow mining of duplicate blocks (used in tests only)
}
type Miner struct {
diff --git a/miner/ordering.go b/miner/ordering.go
index 70a2a42eb6..40bc68d032 100644
--- a/miner/ordering.go
+++ b/miner/ordering.go
@@ -33,26 +33,29 @@ import (
"github.com/ava-labs/subnet-evm/core/txpool"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/common/math"
+ "github.com/holiman/uint256"
)
// txWithMinerFee wraps a transaction with its gas price or effective miner gasTipCap
type txWithMinerFee struct {
tx *txpool.LazyTransaction
from common.Address
- fees *big.Int
+ fees *uint256.Int
}
// newTxWithMinerFee creates a wrapped transaction, calculating the effective
// miner gasTipCap if a base fee is provided.
// Returns error in case of a negative effective miner gasTipCap.
-func newTxWithMinerFee(tx *txpool.LazyTransaction, from common.Address, baseFee *big.Int) (*txWithMinerFee, error) {
- tip := new(big.Int).Set(tx.GasTipCap)
+func newTxWithMinerFee(tx *txpool.LazyTransaction, from common.Address, baseFee *uint256.Int) (*txWithMinerFee, error) {
+ tip := new(uint256.Int).Set(tx.GasTipCap)
if baseFee != nil {
if tx.GasFeeCap.Cmp(baseFee) < 0 {
return nil, types.ErrGasFeeCapTooLow
}
- tip = math.BigMin(tx.GasTipCap, new(big.Int).Sub(tx.GasFeeCap, baseFee))
+ tip = new(uint256.Int).Sub(tx.GasFeeCap, baseFee)
+ if tip.Gt(tx.GasTipCap) {
+ tip = tx.GasTipCap
+ }
}
return &txWithMinerFee{
tx: tx,
@@ -97,7 +100,7 @@ type transactionsByPriceAndNonce struct {
txs map[common.Address][]*txpool.LazyTransaction // Per account nonce-sorted list of transactions
heads txByPriceAndTime // Next transaction for each unique account (price heap)
signer types.Signer // Signer for the set of transactions
- baseFee *big.Int // Current base fee
+ baseFee *uint256.Int // Current base fee
}
// newTransactionsByPriceAndNonce creates a transaction set that can retrieve
@@ -106,10 +109,15 @@ type transactionsByPriceAndNonce struct {
// Note, the input map is reowned so the caller should not interact any more with
// if after providing it to the constructor.
func newTransactionsByPriceAndNonce(signer types.Signer, txs map[common.Address][]*txpool.LazyTransaction, baseFee *big.Int) *transactionsByPriceAndNonce {
+ // Convert the basefee from header format to uint256 format
+ var baseFeeUint *uint256.Int
+ if baseFee != nil {
+ baseFeeUint = uint256.MustFromBig(baseFee)
+ }
// Initialize a price and received time based heap with the head transactions
heads := make(txByPriceAndTime, 0, len(txs))
for from, accTxs := range txs {
- wrapped, err := newTxWithMinerFee(accTxs[0], from, baseFee)
+ wrapped, err := newTxWithMinerFee(accTxs[0], from, baseFeeUint)
if err != nil {
delete(txs, from)
continue
@@ -124,16 +132,16 @@ func newTransactionsByPriceAndNonce(signer types.Signer, txs map[common.Address]
txs: txs,
heads: heads,
signer: signer,
- baseFee: baseFee,
+ baseFee: baseFeeUint,
}
}
// Peek returns the next transaction by price.
-func (t *transactionsByPriceAndNonce) Peek() *txpool.LazyTransaction {
+func (t *transactionsByPriceAndNonce) Peek() (*txpool.LazyTransaction, *uint256.Int) {
if len(t.heads) == 0 {
- return nil
+ return nil, nil
}
- return t.heads[0].tx
+ return t.heads[0].tx, t.heads[0].fees
}
// Shift replaces the current best head with the next one from the same account.
@@ -155,3 +163,14 @@ func (t *transactionsByPriceAndNonce) Shift() {
func (t *transactionsByPriceAndNonce) Pop() {
heap.Pop(&t.heads)
}
+
+// Empty returns if the price heap is empty. It can be used to check it simpler
+// than calling peek and checking for nil return.
+func (t *transactionsByPriceAndNonce) Empty() bool {
+ return len(t.heads) == 0
+}
+
+// Clear removes the entire content of the heap.
+func (t *transactionsByPriceAndNonce) Clear() {
+ t.heads, t.txs = nil, nil
+}
diff --git a/miner/ordering_test.go b/miner/ordering_test.go
index 9bee7cf700..2773affe22 100644
--- a/miner/ordering_test.go
+++ b/miner/ordering_test.go
@@ -37,6 +37,7 @@ import (
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/holiman/uint256"
)
func TestTransactionPriceNonceSortLegacy(t *testing.T) {
@@ -102,8 +103,8 @@ func testTransactionPriceNonceSort(t *testing.T, baseFee *big.Int) {
Hash: tx.Hash(),
Tx: tx,
Time: tx.Time(),
- GasFeeCap: tx.GasFeeCap(),
- GasTipCap: tx.GasTipCap(),
+ GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()),
+ GasTipCap: uint256.MustFromBig(tx.GasTipCap()),
Gas: tx.Gas(),
BlobGas: tx.BlobGas(),
})
@@ -114,7 +115,7 @@ func testTransactionPriceNonceSort(t *testing.T, baseFee *big.Int) {
txset := newTransactionsByPriceAndNonce(signer, groups, baseFee)
txs := types.Transactions{}
- for tx := txset.Peek(); tx != nil; tx = txset.Peek() {
+ for tx, _ := txset.Peek(); tx != nil; tx, _ = txset.Peek() {
txs = append(txs, tx.Tx)
txset.Shift()
}
@@ -170,8 +171,8 @@ func TestTransactionTimeSort(t *testing.T) {
Hash: tx.Hash(),
Tx: tx,
Time: tx.Time(),
- GasFeeCap: tx.GasFeeCap(),
- GasTipCap: tx.GasTipCap(),
+ GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()),
+ GasTipCap: uint256.MustFromBig(tx.GasTipCap()),
Gas: tx.Gas(),
BlobGas: tx.BlobGas(),
})
@@ -180,7 +181,7 @@ func TestTransactionTimeSort(t *testing.T) {
txset := newTransactionsByPriceAndNonce(signer, groups, nil)
txs := types.Transactions{}
- for tx := txset.Peek(); tx != nil; tx = txset.Peek() {
+ for tx, _ := txset.Peek(); tx != nil; tx, _ = txset.Peek() {
txs = append(txs, tx.Tx)
txset.Shift()
}
diff --git a/miner/worker.go b/miner/worker.go
index 7c2f7d2e23..360d30d55d 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -52,6 +52,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
+ "github.com/holiman/uint256"
)
const (
@@ -232,25 +233,47 @@ func (w *worker) commitNewWork(predicateContext *precompileconfig.PredicateConte
return nil, err
}
- pending := w.eth.TxPool().PendingWithBaseFee(true, header.BaseFee)
+ // Retrieve the pending transactions pre-filtered by the 1559/4844 dynamic fees
+ filter := txpool.PendingFilter{
+ MinTip: uint256.MustFromBig(w.eth.TxPool().GasTip()),
+ }
+ if env.header.BaseFee != nil {
+ filter.BaseFee = uint256.MustFromBig(env.header.BaseFee)
+ }
+ if env.header.ExcessBlobGas != nil {
+ filter.BlobFee = uint256.MustFromBig(eip4844.CalcBlobFee(*env.header.ExcessBlobGas))
+ }
+ filter.OnlyPlainTxs, filter.OnlyBlobTxs = true, false
+ pendingPlainTxs := w.eth.TxPool().Pending(filter)
+
+ filter.OnlyPlainTxs, filter.OnlyBlobTxs = false, true
+ pendingBlobTxs := w.eth.TxPool().Pending(filter)
// Split the pending transactions into locals and remotes.
- localTxs, remoteTxs := make(map[common.Address][]*txpool.LazyTransaction), pending
+ localPlainTxs, remotePlainTxs := make(map[common.Address][]*txpool.LazyTransaction), pendingPlainTxs
+ localBlobTxs, remoteBlobTxs := make(map[common.Address][]*txpool.LazyTransaction), pendingBlobTxs
for _, account := range w.eth.TxPool().Locals() {
- if txs := remoteTxs[account]; len(txs) > 0 {
- delete(remoteTxs, account)
- localTxs[account] = txs
+ if txs := remotePlainTxs[account]; len(txs) > 0 {
+ delete(remotePlainTxs, account)
+ localPlainTxs[account] = txs
+ }
+ if txs := remoteBlobTxs[account]; len(txs) > 0 {
+ delete(remoteBlobTxs, account)
+ localBlobTxs[account] = txs
}
}
-
// Fill the block with all available pending transactions.
- if len(localTxs) > 0 {
- txs := newTransactionsByPriceAndNonce(env.signer, localTxs, header.BaseFee)
- w.commitTransactions(env, txs, header.Coinbase)
+ if len(localPlainTxs) > 0 || len(localBlobTxs) > 0 {
+ plainTxs := newTransactionsByPriceAndNonce(env.signer, localPlainTxs, env.header.BaseFee)
+ blobTxs := newTransactionsByPriceAndNonce(env.signer, localBlobTxs, env.header.BaseFee)
+
+ w.commitTransactions(env, plainTxs, blobTxs, env.header.Coinbase)
}
- if len(remoteTxs) > 0 {
- txs := newTransactionsByPriceAndNonce(env.signer, remoteTxs, header.BaseFee)
- w.commitTransactions(env, txs, header.Coinbase)
+ if len(remotePlainTxs) > 0 || len(remoteBlobTxs) > 0 {
+ plainTxs := newTransactionsByPriceAndNonce(env.signer, remotePlainTxs, env.header.BaseFee)
+ blobTxs := newTransactionsByPriceAndNonce(env.signer, remoteBlobTxs, env.header.BaseFee)
+
+ w.commitTransactions(env, plainTxs, blobTxs, env.header.Coinbase)
}
return w.commit(env)
@@ -343,15 +366,47 @@ func (w *worker) applyTransaction(env *environment, tx *types.Transaction, coinb
return receipt, err
}
-func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAndNonce, coinbase common.Address) {
+func (w *worker) commitTransactions(env *environment, plainTxs, blobTxs *transactionsByPriceAndNonce, coinbase common.Address) {
for {
// If we don't have enough gas for any further transactions then we're done.
if env.gasPool.Gas() < params.TxGas {
log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
break
}
+ // If we don't have enough blob space for any further blob transactions,
+ // skip that list altogether
+ if !blobTxs.Empty() && env.blobs*params.BlobTxBlobGasPerBlob >= params.MaxBlobGasPerBlock {
+ log.Trace("Not enough blob space for further blob transactions")
+ blobTxs.Clear()
+ // Fall though to pick up any plain txs
+ }
+ // If we don't have enough blob space for any further blob transactions,
+ // skip that list altogether
+ if !blobTxs.Empty() && env.blobs*params.BlobTxBlobGasPerBlob >= params.MaxBlobGasPerBlock {
+ log.Trace("Not enough blob space for further blob transactions")
+ blobTxs.Clear()
+ // Fall though to pick up any plain txs
+ }
// Retrieve the next transaction and abort if all done.
- ltx := txs.Peek()
+ var (
+ ltx *txpool.LazyTransaction
+ txs *transactionsByPriceAndNonce
+ )
+ pltx, ptip := plainTxs.Peek()
+ bltx, btip := blobTxs.Peek()
+
+ switch {
+ case pltx == nil:
+ txs, ltx = blobTxs, bltx
+ case bltx == nil:
+ txs, ltx = plainTxs, pltx
+ default:
+ if ptip.Lt(btip) {
+ txs, ltx = blobTxs, bltx
+ } else {
+ txs, ltx = plainTxs, pltx
+ }
+ }
if ltx == nil {
break
}
@@ -438,7 +493,7 @@ func (w *worker) commit(env *environment) (*types.Block, error) {
func (w *worker) handleResult(env *environment, block *types.Block, createdAt time.Time, unfinishedReceipts []*types.Receipt) (*types.Block, error) {
// Short circuit when receiving duplicate result caused by resubmitting.
- if w.chain.HasBlock(block.Hash(), block.NumberU64()) {
+ if !w.config.TestOnlyAllowDuplicateBlocks && w.chain.HasBlock(block.Hash(), block.NumberU64()) {
return nil, fmt.Errorf("produced duplicate block (Hash: %s, Number %d)", block.Hash(), block.NumberU64())
}
// Different block could share same sealhash, deep copy here to prevent write-write conflict.
diff --git a/node/defaults.go b/node/defaults.go
new file mode 100644
index 0000000000..b19ac3a861
--- /dev/null
+++ b/node/defaults.go
@@ -0,0 +1,22 @@
+// Copyright 2016 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 .
+package node
+
+// DefaultConfig contains reasonable default settings.
+var DefaultConfig = Config{
+ BatchRequestLimit: 1000,
+ BatchResponseMaxSize: 25 * 1000 * 1000,
+}
diff --git a/params/config.go b/params/config.go
index 70cc2159ba..5d5799b92b 100644
--- a/params/config.go
+++ b/params/config.go
@@ -236,7 +236,8 @@ func (c *ChainConfig) Description() string {
}
banner += "Hard forks (timestamp based):\n"
- banner += fmt.Sprintf(" - Cancun Timestamp: @%-10v (https://github.com/ava-labs/avalanchego/releases/tag/v1.12.0)\n", ptrToString(c.CancunTime))
+ banner += fmt.Sprintf(" - Cancun Timestamp: @%-10v (https://github.com/ethereum/execution-specs/blob/master/network-upgrades/mainnet-upgrades/cancun.md)\n", ptrToString(c.CancunTime))
+ banner += fmt.Sprintf(" - Verkle Timestamp: @%-10v", ptrToString(c.VerkleTime))
banner += "Avalanche Upgrades (timestamp based):\n"
banner += c.NetworkUpgrades.Description()
@@ -628,6 +629,7 @@ type EthRules struct {
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
IsCancun bool
+ IsVerkle bool
}
// Rules wraps ChainConfig and is merely syntactic sugar or can be used for functions
@@ -675,6 +677,7 @@ func (c *ChainConfig) rules(num *big.Int, timestamp uint64) Rules {
IsPetersburg: c.IsPetersburg(num),
IsIstanbul: c.IsIstanbul(num),
IsCancun: c.IsCancun(num, timestamp),
+ IsVerkle: c.IsVerkle(num, timestamp),
},
}
}
diff --git a/params/protocol_params.go b/params/protocol_params.go
index 6e4b96f8e7..0f85e6cd2f 100644
--- a/params/protocol_params.go
+++ b/params/protocol_params.go
@@ -172,7 +172,6 @@ const (
BlobTxBytesPerFieldElement = 32 // Size in bytes of a field element
BlobTxFieldElementsPerBlob = 4096 // Number of field elements stored in a single data blob
- BlobTxHashVersion = 0x01 // Version byte of the commitment hash
BlobTxBlobGasPerBlob = 1 << 17 // Gas consumption of a single data blob (== blob byte size)
BlobTxMinBlobGasprice = 1 // Minimum gas price for data blobs
BlobTxBlobGaspriceUpdateFraction = 3338477 // Controls the maximum rate of change for blob gas price
diff --git a/params/version.go b/params/version.go
index be03098ff4..f27e094e03 100644
--- a/params/version.go
+++ b/params/version.go
@@ -33,7 +33,7 @@ import (
const (
VersionMajor = 1 // Major version component of the current release
VersionMinor = 13 // Minor version component of the current release
- VersionPatch = 8 // Patch version component of the current release
+ VersionPatch = 14 // Patch version component of the current release
VersionMeta = "stable" // Version metadata to append to the version string
)
diff --git a/peer/network.go b/peer/network.go
index 13e3cc6220..8f1919c032 100644
--- a/peer/network.go
+++ b/peer/network.go
@@ -123,6 +123,11 @@ func NewNetwork(p2pNetwork *p2p.Network, appSender common.AppSender, codec codec
// Returns the ID of the chosen peer, and an error if the request could not
// be sent to a peer with the desired [minVersion].
func (n *network) SendAppRequestAny(ctx context.Context, minVersion *version.Application, request []byte, handler message.ResponseHandler) (ids.NodeID, error) {
+ // If the context was cancelled, we can skip sending this request.
+ if err := ctx.Err(); err != nil {
+ return ids.EmptyNodeID, err
+ }
+
// Take a slot from total [activeAppRequests] and block until a slot becomes available.
if err := n.activeAppRequests.Acquire(ctx, 1); err != nil {
return ids.EmptyNodeID, errAcquiringSemaphore
@@ -144,6 +149,11 @@ func (n *network) SendAppRequest(ctx context.Context, nodeID ids.NodeID, request
return fmt.Errorf("cannot send request to empty nodeID, nodeID=%s, requestLen=%d", nodeID, len(request))
}
+ // If the context was cancelled, we can skip sending this request.
+ if err := ctx.Err(); err != nil {
+ return err
+ }
+
// Take a slot from total [activeAppRequests] and block until a slot becomes available.
if err := n.activeAppRequests.Acquire(ctx, 1); err != nil {
return errAcquiringSemaphore
diff --git a/plugin/evm/block_builder.go b/plugin/evm/block_builder.go
index cfeb2385ec..cdd5e63889 100644
--- a/plugin/evm/block_builder.go
+++ b/plugin/evm/block_builder.go
@@ -11,6 +11,7 @@ import (
"github.com/ava-labs/subnet-evm/core"
"github.com/ava-labs/subnet-evm/core/txpool"
"github.com/ava-labs/subnet-evm/params"
+ "github.com/holiman/uint256"
"github.com/ava-labs/avalanchego/snow"
commonEng "github.com/ava-labs/avalanchego/snow/engine/common"
@@ -98,7 +99,9 @@ func (b *blockBuilder) handleGenerateBlock() {
// needToBuild returns true if there are outstanding transactions to be issued
// into a block.
func (b *blockBuilder) needToBuild() bool {
- size := b.txPool.PendingSize(true)
+ size := b.txPool.PendingSize(txpool.PendingFilter{
+ MinTip: uint256.MustFromBig(b.txPool.GasTip()),
+ })
return size > 0
}
diff --git a/plugin/evm/config.go b/plugin/evm/config.go
index 47b342537c..9f59775ba9 100644
--- a/plugin/evm/config.go
+++ b/plugin/evm/config.go
@@ -222,6 +222,9 @@ type Config struct {
// Note: only supports AddressedCall payloads as defined here:
// https://github.com/ava-labs/avalanchego/tree/7623ffd4be915a5185c9ed5e11fa9be15a6e1f00/vms/platformvm/warp/payload#addressedcall
WarpOffChainMessages []hexutil.Bytes `json:"warp-off-chain-messages"`
+
+ // RPC settings
+ HttpBodyLimit uint64 `json:"http-body-limit"`
}
// EthAPIs returns an array of strings representing the Eth APIs that should be enabled
diff --git a/plugin/evm/gossip.go b/plugin/evm/gossip.go
index 05270f7a65..f10c63bb43 100644
--- a/plugin/evm/gossip.go
+++ b/plugin/evm/gossip.go
@@ -137,7 +137,7 @@ func (g *GossipEthTxPool) Subscribe(ctx context.Context) {
return
case pendingTxs := <-g.pendingTxs:
g.lock.Lock()
- optimalElements := (g.mempool.PendingSize(false) + len(pendingTxs.Txs)) * txGossipBloomChurnMultiplier
+ optimalElements := (g.mempool.PendingSize(txpool.PendingFilter{}) + len(pendingTxs.Txs)) * txGossipBloomChurnMultiplier
for _, pendingTx := range pendingTxs.Txs {
tx := &GossipEthTx{Tx: pendingTx}
g.bloom.Add(tx)
diff --git a/plugin/evm/gossip_test.go b/plugin/evm/gossip_test.go
index 61772063d4..551d3a8075 100644
--- a/plugin/evm/gossip_test.go
+++ b/plugin/evm/gossip_test.go
@@ -96,14 +96,14 @@ func setupPoolWithConfig(t *testing.T, config *params.ChainConfig, fundedAddress
gspec := &core.Genesis{
Config: config,
- Alloc: core.GenesisAlloc{fundedAddress: core.GenesisAccount{Balance: big.NewInt(1000000000000000000)}},
+ Alloc: types.GenesisAlloc{fundedAddress: {Balance: big.NewInt(1000000000000000000)}},
}
chain, err := core.NewBlockChain(diskdb, core.DefaultCacheConfig, gspec, engine, vm.Config{}, common.Hash{}, false)
require.NoError(t, err)
testTxPoolConfig := legacypool.DefaultConfig
legacyPool := legacypool.New(testTxPoolConfig, chain)
- txPool, err := txpool.New(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), chain, []txpool.SubPool{legacyPool})
+ txPool, err := txpool.New(testTxPoolConfig.PriceLimit, chain, []txpool.SubPool{legacyPool})
require.NoError(t, err)
return txPool
diff --git a/plugin/evm/gossiper_eth_gossiping_test.go b/plugin/evm/gossiper_eth_gossiping_test.go
index 8eb140c3a8..6e4e685ed0 100644
--- a/plugin/evm/gossiper_eth_gossiping_test.go
+++ b/plugin/evm/gossiper_eth_gossiping_test.go
@@ -34,9 +34,9 @@ func fundAddressByGenesis(addrs []common.Address) (string, error) {
Difficulty: common.Big0,
GasLimit: params.TestChainConfig.FeeConfig.GasLimit.Uint64(),
}
- funds := make(map[common.Address]core.GenesisAccount)
+ funds := make(map[common.Address]types.Account)
for _, addr := range addrs {
- funds[addr] = core.GenesisAccount{
+ funds[addr] = types.Account{
Balance: balance,
}
}
diff --git a/plugin/evm/log_test.go b/plugin/evm/log_test.go
new file mode 100644
index 0000000000..eb37e7a9e9
--- /dev/null
+++ b/plugin/evm/log_test.go
@@ -0,0 +1,19 @@
+// (c) 2024, Ava Labs, Inc. All rights reserved.
+// See the file LICENSE for licensing terms.
+
+package evm
+
+import (
+ "os"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/stretchr/testify/require"
+)
+
+func TestInitLogger(t *testing.T) {
+ require := require.New(t)
+ _, err := InitLogger("alias", "info", true, os.Stderr)
+ require.NoError(err)
+ log.Info("test")
+}
diff --git a/plugin/evm/network_handler.go b/plugin/evm/network_handler.go
index 2e46477cc0..e54d40e676 100644
--- a/plugin/evm/network_handler.go
+++ b/plugin/evm/network_handler.go
@@ -12,7 +12,7 @@ import (
"github.com/ava-labs/subnet-evm/plugin/evm/message"
syncHandlers "github.com/ava-labs/subnet-evm/sync/handlers"
syncStats "github.com/ava-labs/subnet-evm/sync/handlers/stats"
- "github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ava-labs/subnet-evm/warp"
warpHandlers "github.com/ava-labs/subnet-evm/warp/handlers"
"github.com/ethereum/go-ethereum/ethdb"
@@ -31,7 +31,7 @@ type networkHandler struct {
func newNetworkHandler(
provider syncHandlers.SyncDataProvider,
diskDB ethdb.KeyValueReader,
- evmTrieDB *trie.Database,
+ evmTrieDB *triedb.Database,
warpBackend warp.Backend,
networkCodec codec.Manager,
) message.RequestHandler {
diff --git a/plugin/evm/static_service_test.go b/plugin/evm/static_service_test.go
index 626f8f30d3..6ac264543d 100644
--- a/plugin/evm/static_service_test.go
+++ b/plugin/evm/static_service_test.go
@@ -10,6 +10,7 @@ import (
"github.com/ava-labs/avalanchego/utils/formatting"
"github.com/ava-labs/subnet-evm/core"
+ "github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/params"
"github.com/stretchr/testify/assert"
)
@@ -25,9 +26,9 @@ func TestBuildGenesis(t *testing.T) {
}
// add test allocs
- testAlloc := core.GenesisAlloc{
- testEthAddrs[0]: core.GenesisAccount{Balance: genesisBalance},
- testEthAddrs[1]: core.GenesisAccount{Balance: genesisBalance},
+ testAlloc := types.GenesisAlloc{
+ testEthAddrs[0]: {Balance: genesisBalance},
+ testEthAddrs[1]: {Balance: genesisBalance},
}
genesis.Alloc = testAlloc
genesis.Config.FeeConfig = params.DefaultFeeConfig
@@ -63,9 +64,9 @@ func TestDecodeGenesis(t *testing.T) {
}
// add test allocs
- testAlloc := core.GenesisAlloc{
- testEthAddrs[0]: core.GenesisAccount{Balance: genesisBalance},
- testEthAddrs[1]: core.GenesisAccount{Balance: genesisBalance},
+ testAlloc := types.GenesisAlloc{
+ testEthAddrs[0]: {Balance: genesisBalance},
+ testEthAddrs[1]: {Balance: genesisBalance},
}
genesis.Alloc = testAlloc
genesis.Config.FeeConfig = params.DefaultFeeConfig
diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go
index 64306ef66c..5a5b260a2d 100644
--- a/plugin/evm/syncervm_test.go
+++ b/plugin/evm/syncervm_test.go
@@ -36,6 +36,7 @@ import (
statesyncclient "github.com/ava-labs/subnet-evm/sync/client"
"github.com/ava-labs/subnet-evm/sync/statesync"
"github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
@@ -295,7 +296,7 @@ func createSyncServerAndClientVMs(t *testing.T, test syncTest, numBlocks int) *s
}, nil)
// make some accounts
- trieDB := trie.NewDatabase(serverVM.chaindb, nil)
+ trieDB := triedb.NewDatabase(serverVM.chaindb, nil)
root, accounts := statesync.FillAccountsWithOverlappingStorage(t, trieDB, types.EmptyRootHash, 1000, 16)
// patch serverVM's lastAcceptedBlock to have the new root
diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go
index 3e49bcc94d..376a8a12c7 100644
--- a/plugin/evm/vm.go
+++ b/plugin/evm/vm.go
@@ -21,6 +21,7 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/ava-labs/subnet-evm/commontype"
+ "github.com/ava-labs/subnet-evm/consensus/dummy"
"github.com/ava-labs/subnet-evm/constants"
"github.com/ava-labs/subnet-evm/core"
"github.com/ava-labs/subnet-evm/core/rawdb"
@@ -35,13 +36,13 @@ import (
"github.com/ava-labs/subnet-evm/params"
"github.com/ava-labs/subnet-evm/peer"
"github.com/ava-labs/subnet-evm/plugin/evm/message"
- "github.com/ava-labs/subnet-evm/trie/triedb/hashdb"
+ "github.com/ava-labs/subnet-evm/triedb"
+ "github.com/ava-labs/subnet-evm/triedb/hashdb"
warpcontract "github.com/ava-labs/subnet-evm/precompile/contracts/warp"
"github.com/ava-labs/subnet-evm/rpc"
statesyncclient "github.com/ava-labs/subnet-evm/sync/client"
"github.com/ava-labs/subnet-evm/sync/client/stats"
- "github.com/ava-labs/subnet-evm/trie"
"github.com/ava-labs/subnet-evm/warp"
"github.com/ava-labs/subnet-evm/warp/handlers"
@@ -547,6 +548,7 @@ func (vm *VM) initializeChain(lastAcceptedHash common.Hash, ethConfig ethconfig.
vm.chaindb,
vm.config.EthBackendSettings(),
lastAcceptedHash,
+ dummy.NewFakerWithClock(&vm.clock),
&vm.clock,
)
if err != nil {
@@ -798,9 +800,9 @@ func (vm *VM) setAppRequestHandlers() {
// Create separate EVM TrieDB (read only) for serving leafs requests.
// We create a separate TrieDB here, so that it has a separate cache from the one
// used by the node when processing blocks.
- evmTrieDB := trie.NewDatabase(
+ evmTrieDB := triedb.NewDatabase(
vm.chaindb,
- &trie.Config{
+ &triedb.Config{
HashDB: &hashdb.Config{
CleanCacheSize: vm.config.StateSyncServerTrieCache * units.MiB,
},
@@ -992,6 +994,10 @@ func newHandler(name string, service interface{}) (http.Handler, error) {
// CreateHandlers makes new http handlers that can handle API calls
func (vm *VM) CreateHandlers(context.Context) (map[string]http.Handler, error) {
handler := rpc.NewServer(vm.config.APIMaxDuration.Duration)
+ if vm.config.HttpBodyLimit > 0 {
+ handler.SetHTTPBodyLimit(int(vm.config.HttpBodyLimit))
+ }
+
enabledAPIs := vm.config.EthAPIs()
if err := attachEthService(handler, vm.eth.APIs(), enabledAPIs); err != nil {
return nil, err
@@ -1040,6 +1046,9 @@ func (vm *VM) CreateHandlers(context.Context) (map[string]http.Handler, error) {
// CreateStaticHandlers makes new http handlers that can handle API calls
func (vm *VM) CreateStaticHandlers(context.Context) (map[string]http.Handler, error) {
handler := rpc.NewServer(0)
+ if vm.config.HttpBodyLimit > 0 {
+ handler.SetHTTPBodyLimit(int(vm.config.HttpBodyLimit))
+ }
if err := handler.RegisterName("static", &StaticService{}); err != nil {
return nil, err
}
diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go
index 6818b773bf..934a999684 100644
--- a/plugin/evm/vm_test.go
+++ b/plugin/evm/vm_test.go
@@ -2707,7 +2707,7 @@ func TestAllowFeeRecipientEnabled(t *testing.T) {
}
balance := blkState.GetBalance(etherBase)
- require.Equal(t, 1, balance.Cmp(common.Big0))
+ require.Equal(t, 1, balance.Cmp(common.U2560))
}
func TestRewardManagerPrecompileSetRewardAddress(t *testing.T) {
@@ -2797,7 +2797,7 @@ func TestRewardManagerPrecompileSetRewardAddress(t *testing.T) {
require.NoError(t, err)
balance := blkState.GetBalance(testAddr)
- require.Equal(t, 1, balance.Cmp(common.Big0))
+ require.Equal(t, 1, balance.Cmp(common.U2560))
// Test Case: Disable reward manager
// This should revert back to enabling fee recipients
@@ -2935,7 +2935,7 @@ func TestRewardManagerPrecompileAllowFeeRecipients(t *testing.T) {
require.NoError(t, err)
balance := blkState.GetBalance(etherBase)
- require.Equal(t, 1, balance.Cmp(common.Big0))
+ require.Equal(t, 1, balance.Cmp(common.U2560))
// Test Case: Disable reward manager
// This should revert back to burning fees
diff --git a/plugin/evm/vm_upgrade_bytes_test.go b/plugin/evm/vm_upgrade_bytes_test.go
index 1960077647..9907f98531 100644
--- a/plugin/evm/vm_upgrade_bytes_test.go
+++ b/plugin/evm/vm_upgrade_bytes_test.go
@@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto"
+ "github.com/holiman/uint256"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -285,7 +286,7 @@ func TestVMStateUpgrade(t *testing.T) {
// Verify the new account doesn't exist yet
genesisState, err := vm.blockChain.State()
require.NoError(t, err)
- require.Equal(t, common.Big0, genesisState.GetBalance(newAccount))
+ require.Equal(t, common.U2560, genesisState.GetBalance(newAccount))
// Advance the chain to the upgrade time
vm.clock.Set(upgradeTimestamp)
@@ -308,9 +309,11 @@ func TestVMStateUpgrade(t *testing.T) {
require.NoError(t, err)
// Existing account
- expectedGenesisAccountBalance := new(big.Int).Add(
- genesisAccount.Balance,
- (*big.Int)(genesisAccountUpgrade.BalanceChange),
+ expectedGenesisAccountBalance := uint256.MustFromBig(
+ new(big.Int).Add(
+ genesisAccount.Balance,
+ (*big.Int)(genesisAccountUpgrade.BalanceChange),
+ ),
)
require.Equal(t, state.GetBalance(testEthAddrs[0]), expectedGenesisAccountBalance)
require.Equal(t, state.GetState(testEthAddrs[0], storageKey), genesisAccountUpgrade.Storage[storageKey])
@@ -319,8 +322,8 @@ func TestVMStateUpgrade(t *testing.T) {
require.Equal(t, state.GetNonce(testEthAddrs[0]), genesisAccount.Nonce) // Nonce should be preserved since it was non-zero
// New account
- expectedNewAccountBalance := newAccountUpgrade.BalanceChange
- require.Equal(t, state.GetBalance(newAccount), (*big.Int)(expectedNewAccountBalance))
+ expectedNewAccountBalance := uint256.MustFromBig((*big.Int)(newAccountUpgrade.BalanceChange))
+ require.Equal(t, state.GetBalance(newAccount), expectedNewAccountBalance)
require.Equal(t, state.GetCode(newAccount), upgradedCode)
require.Equal(t, state.GetCodeHash(newAccount), crypto.Keccak256Hash(upgradedCode))
require.Equal(t, state.GetNonce(newAccount), uint64(1)) // Nonce should be set to 1 when code is set if nonce was 0
diff --git a/precompile/contract/interfaces.go b/precompile/contract/interfaces.go
index 5ac6baa486..7bc5d750a5 100644
--- a/precompile/contract/interfaces.go
+++ b/precompile/contract/interfaces.go
@@ -10,6 +10,7 @@ import (
"github.com/ava-labs/avalanchego/snow"
"github.com/ava-labs/subnet-evm/precompile/precompileconfig"
"github.com/ethereum/go-ethereum/common"
+ "github.com/holiman/uint256"
)
// StatefulPrecompiledContract is the interface for executing a precompiled contract
@@ -26,8 +27,8 @@ type StateDB interface {
SetNonce(common.Address, uint64)
GetNonce(common.Address) uint64
- GetBalance(common.Address) *big.Int
- AddBalance(common.Address, *big.Int)
+ GetBalance(common.Address) *uint256.Int
+ AddBalance(common.Address, *uint256.Int)
CreateAccount(common.Address)
Exist(common.Address) bool
diff --git a/precompile/contract/mocks.go b/precompile/contract/mocks.go
index 6510d2d738..86943cf6e4 100644
--- a/precompile/contract/mocks.go
+++ b/precompile/contract/mocks.go
@@ -16,6 +16,7 @@ import (
snow "github.com/ava-labs/avalanchego/snow"
precompileconfig "github.com/ava-labs/subnet-evm/precompile/precompileconfig"
common "github.com/ethereum/go-ethereum/common"
+ uint256 "github.com/holiman/uint256"
gomock "go.uber.org/mock/gomock"
)
@@ -187,7 +188,7 @@ func (m *MockStateDB) EXPECT() *MockStateDBMockRecorder {
}
// AddBalance mocks base method.
-func (m *MockStateDB) AddBalance(arg0 common.Address, arg1 *big.Int) {
+func (m *MockStateDB) AddBalance(arg0 common.Address, arg1 *uint256.Int) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "AddBalance", arg0, arg1)
}
@@ -237,10 +238,10 @@ func (mr *MockStateDBMockRecorder) Exist(arg0 any) *gomock.Call {
}
// GetBalance mocks base method.
-func (m *MockStateDB) GetBalance(arg0 common.Address) *big.Int {
+func (m *MockStateDB) GetBalance(arg0 common.Address) *uint256.Int {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetBalance", arg0)
- ret0, _ := ret[0].(*big.Int)
+ ret0, _ := ret[0].(*uint256.Int)
return ret0
}
diff --git a/precompile/contracts/nativeminter/contract.go b/precompile/contracts/nativeminter/contract.go
index 2e578fe2cc..4ca751ccf2 100644
--- a/precompile/contracts/nativeminter/contract.go
+++ b/precompile/contracts/nativeminter/contract.go
@@ -13,6 +13,7 @@ import (
"github.com/ava-labs/subnet-evm/precompile/contract"
"github.com/ava-labs/subnet-evm/vmerrs"
"github.com/ethereum/go-ethereum/common"
+ "github.com/holiman/uint256"
)
const (
@@ -117,7 +118,8 @@ func mintNativeCoin(accessibleState contract.AccessibleState, caller common.Addr
stateDB.CreateAccount(to)
}
- stateDB.AddBalance(to, amount)
+ amountU256, _ := uint256.FromBig(amount)
+ stateDB.AddBalance(to, amountU256)
// Return an empty output and the remaining gas
return []byte{}, remainingGas, nil
}
diff --git a/precompile/contracts/nativeminter/contract_test.go b/precompile/contracts/nativeminter/contract_test.go
index 0881918516..6a6cf3574b 100644
--- a/precompile/contracts/nativeminter/contract_test.go
+++ b/precompile/contracts/nativeminter/contract_test.go
@@ -15,6 +15,7 @@ import (
"github.com/ava-labs/subnet-evm/vmerrs"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
+ "github.com/holiman/uint256"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
)
@@ -47,7 +48,8 @@ var (
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, stateDB contract.StateDB) {
- require.Equal(t, common.Big1, stateDB.GetBalance(allowlist.TestEnabledAddr), "expected minted funds")
+ expected := uint256.MustFromBig(common.Big1)
+ require.Equal(t, expected, stateDB.GetBalance(allowlist.TestEnabledAddr), "expected minted funds")
logsTopics, logsData := stateDB.GetLogData()
assertNativeCoinMintedEvent(t, logsTopics, logsData, allowlist.TestEnabledAddr, allowlist.TestEnabledAddr, common.Big1)
@@ -62,7 +64,8 @@ var (
},
},
AfterHook: func(t testing.TB, stateDB contract.StateDB) {
- require.Equal(t, common.Big2, stateDB.GetBalance(allowlist.TestEnabledAddr), "expected minted funds")
+ expected := uint256.MustFromBig(common.Big2)
+ require.Equal(t, expected, stateDB.GetBalance(allowlist.TestEnabledAddr), "expected minted funds")
},
},
"calling mintNativeCoin from Manager should succeed": {
@@ -78,7 +81,8 @@ var (
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, stateDB contract.StateDB) {
- require.Equal(t, common.Big1, stateDB.GetBalance(allowlist.TestEnabledAddr), "expected minted funds")
+ expected := uint256.MustFromBig(common.Big1)
+ require.Equal(t, expected, stateDB.GetBalance(allowlist.TestEnabledAddr), "expected minted funds")
logsTopics, logsData := stateDB.GetLogData()
assertNativeCoinMintedEvent(t, logsTopics, logsData, allowlist.TestManagerAddr, allowlist.TestEnabledAddr, common.Big1)
@@ -97,7 +101,8 @@ var (
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, stateDB contract.StateDB) {
- require.Equal(t, common.Big1, stateDB.GetBalance(allowlist.TestAdminAddr), "expected minted funds")
+ expected := uint256.MustFromBig(common.Big1)
+ require.Equal(t, expected, stateDB.GetBalance(allowlist.TestAdminAddr), "expected minted funds")
logsTopics, logsData := stateDB.GetLogData()
assertNativeCoinMintedEvent(t, logsTopics, logsData, allowlist.TestAdminAddr, allowlist.TestAdminAddr, common.Big1)
@@ -116,7 +121,8 @@ var (
ReadOnly: false,
ExpectedRes: []byte{},
AfterHook: func(t testing.TB, stateDB contract.StateDB) {
- require.Equal(t, math.MaxBig256, stateDB.GetBalance(allowlist.TestAdminAddr), "expected minted funds")
+ expected := uint256.MustFromBig(math.MaxBig256)
+ require.Equal(t, expected, stateDB.GetBalance(allowlist.TestAdminAddr), "expected minted funds")
logsTopics, logsData := stateDB.GetLogData()
assertNativeCoinMintedEvent(t, logsTopics, logsData, allowlist.TestAdminAddr, allowlist.TestAdminAddr, math.MaxBig256)
@@ -239,7 +245,8 @@ var (
SuppliedGas: MintGasCost + NativeCoinMintedEventGasCost,
ReadOnly: false,
AfterHook: func(t testing.TB, state contract.StateDB) {
- require.Equal(t, common.Big1, state.GetBalance(allowlist.TestEnabledAddr), "expected minted funds")
+ expected := uint256.MustFromBig(common.Big1)
+ require.Equal(t, expected, state.GetBalance(allowlist.TestEnabledAddr), "expected minted funds")
logsTopics, logsData := state.GetLogData()
assertNativeCoinMintedEvent(t, logsTopics, logsData, allowlist.TestEnabledAddr, allowlist.TestEnabledAddr, common.Big1)
@@ -261,7 +268,8 @@ func assertNativeCoinMintedEvent(t testing.TB,
logsData [][]byte,
expectedSender common.Address,
expectedRecipient common.Address,
- expectedAmount *big.Int) {
+ expectedAmount *big.Int,
+) {
require.Len(t, logsTopics, 1)
require.Len(t, logsData, 1)
topics := logsTopics[0]
diff --git a/precompile/contracts/nativeminter/module.go b/precompile/contracts/nativeminter/module.go
index ce62cee149..02529e2432 100644
--- a/precompile/contracts/nativeminter/module.go
+++ b/precompile/contracts/nativeminter/module.go
@@ -11,6 +11,7 @@ import (
"github.com/ava-labs/subnet-evm/precompile/modules"
"github.com/ava-labs/subnet-evm/precompile/precompileconfig"
"github.com/ethereum/go-ethereum/common"
+ "github.com/holiman/uint256"
)
var _ contract.Configurator = &configurator{}
@@ -52,8 +53,9 @@ func (*configurator) Configure(chainConfig precompileconfig.ChainConfig, cfg pre
}
for to, amount := range config.InitialMint {
if amount != nil {
- bigIntAmount := (*big.Int)(amount)
- state.AddBalance(to, bigIntAmount)
+ amountBig := (*big.Int)(amount)
+ amountU256, _ := uint256.FromBig(amountBig)
+ state.AddBalance(to, amountU256)
}
}
diff --git a/rpc/http.go b/rpc/http.go
index a3ff1eac7f..b8670a9df8 100644
--- a/rpc/http.go
+++ b/rpc/http.go
@@ -43,8 +43,8 @@ import (
)
const (
- maxRequestContentLength = 1024 * 1024 * 5
- contentType = "application/json"
+ defaultBodyLimit = 5 * 1024 * 1024
+ contentType = "application/json"
)
// https://www.jsonrpc.org/historical/json-rpc-over-http.html#id13
@@ -267,8 +267,8 @@ type httpServerConn struct {
r *http.Request
}
-func newHTTPServerConn(r *http.Request, w http.ResponseWriter) ServerCodec {
- body := io.LimitReader(r.Body, maxRequestContentLength)
+func (s *Server) newHTTPServerConn(r *http.Request, w http.ResponseWriter) ServerCodec {
+ body := io.LimitReader(r.Body, int64(s.httpBodyLimit))
conn := &httpServerConn{Reader: body, Writer: w, r: r}
encoder := func(v any, isErrorResponse bool) error {
@@ -326,7 +326,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
return
}
- if code, err := validateRequest(r); err != nil {
+ if code, err := s.validateRequest(r); err != nil {
http.Error(w, err.Error(), code)
return
}
@@ -344,19 +344,19 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// until EOF, writes the response to w, and orders the server to process a
// single request.
w.Header().Set("content-type", contentType)
- codec := newHTTPServerConn(r, w)
+ codec := s.newHTTPServerConn(r, w)
defer codec.close()
s.serveSingleRequest(ctx, codec)
}
// validateRequest returns a non-zero response code and error message if the
// request is invalid.
-func validateRequest(r *http.Request) (int, error) {
+func (s *Server) validateRequest(r *http.Request) (int, error) {
if r.Method == http.MethodPut || r.Method == http.MethodDelete {
return http.StatusMethodNotAllowed, errors.New("method not allowed")
}
- if r.ContentLength > maxRequestContentLength {
- err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, maxRequestContentLength)
+ if r.ContentLength > int64(s.httpBodyLimit) {
+ err := fmt.Errorf("content length too large (%d>%d)", r.ContentLength, s.httpBodyLimit)
return http.StatusRequestEntityTooLarge, err
}
// Allow OPTIONS (regardless of content-type)
diff --git a/rpc/http_test.go b/rpc/http_test.go
index d5d4ad1158..17e5617058 100644
--- a/rpc/http_test.go
+++ b/rpc/http_test.go
@@ -50,11 +50,13 @@ func confirmStatusCode(t *testing.T, got, want int) {
func confirmRequestValidationCode(t *testing.T, method, contentType, body string, expectedStatusCode int) {
t.Helper()
+
+ s := NewServer(0)
request := httptest.NewRequest(method, "http://url.com", strings.NewReader(body))
if len(contentType) > 0 {
request.Header.Set("Content-Type", contentType)
}
- code, err := validateRequest(request)
+ code, err := s.validateRequest(request)
if code == 0 {
if err != nil {
t.Errorf("validation: got error %v, expected nil", err)
@@ -74,7 +76,7 @@ func TestHTTPErrorResponseWithPut(t *testing.T) {
}
func TestHTTPErrorResponseWithMaxContentLength(t *testing.T) {
- body := make([]rune, maxRequestContentLength+1)
+ body := make([]rune, defaultBodyLimit+1)
confirmRequestValidationCode(t,
http.MethodPost, contentType, string(body), http.StatusRequestEntityTooLarge)
}
@@ -114,7 +116,7 @@ func TestHTTPResponseWithEmptyGet(t *testing.T) {
// This checks that maxRequestContentLength is not applied to the response of a request.
func TestHTTPRespBodyUnlimited(t *testing.T) {
- const respLength = maxRequestContentLength * 3
+ const respLength = defaultBodyLimit * 3
s := NewServer(0)
defer s.Stop()
diff --git a/rpc/server.go b/rpc/server.go
index a993fbe96e..054bd2de92 100644
--- a/rpc/server.go
+++ b/rpc/server.go
@@ -62,6 +62,7 @@ type Server struct {
run atomic.Bool
batchItemLimit int
batchResponseLimit int
+ httpBodyLimit int
}
// NewServer creates a new server instance with no registered handlers.
@@ -74,6 +75,7 @@ func NewServer(maximumDuration time.Duration) *Server {
idgen: randomIDGenerator(),
codecs: make(map[ServerCodec]struct{}),
maximumDuration: maximumDuration,
+ httpBodyLimit: defaultBodyLimit,
}
server.run.Store(true)
// Register the default service providing meta information about the RPC service such
@@ -94,6 +96,13 @@ func (s *Server) SetBatchLimits(itemLimit, maxResponseSize int) {
s.batchResponseLimit = maxResponseSize
}
+// SetHTTPBodyLimit sets the size limit for HTTP requests.
+//
+// This method should be called before processing any requests via ServeHTTP.
+func (s *Server) SetHTTPBodyLimit(limit int) {
+ s.httpBodyLimit = limit
+}
+
// RegisterName creates a service for the given receiver type under the given name. When no
// methods on the given receiver match the criteria to be either a RPC method or a
// subscription an error is returned. Otherwise a new service is created and added to the
diff --git a/rpc/types.go b/rpc/types.go
index 7b0d717dad..b2f5b98528 100644
--- a/rpc/types.go
+++ b/rpc/types.go
@@ -29,6 +29,7 @@ package rpc
import (
"context"
"encoding/json"
+ "errors"
"fmt"
"math"
"strings"
@@ -115,7 +116,7 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
return err
}
if blckNum > math.MaxInt64 {
- return fmt.Errorf("block number larger than int64")
+ return errors.New("block number larger than int64")
}
*bn = BlockNumber(blckNum)
return nil
@@ -174,7 +175,7 @@ func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, &e)
if err == nil {
if e.BlockNumber != nil && e.BlockHash != nil {
- return fmt.Errorf("cannot specify both BlockHash and BlockNumber, choose one or the other")
+ return errors.New("cannot specify both BlockHash and BlockNumber, choose one or the other")
}
bnh.BlockNumber = e.BlockNumber
bnh.BlockHash = e.BlockHash
@@ -223,7 +224,7 @@ func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error {
return err
}
if blckNum > math.MaxInt64 {
- return fmt.Errorf("blocknumber too high")
+ return errors.New("blocknumber too high")
}
bn := BlockNumber(blckNum)
bnh.BlockNumber = &bn
diff --git a/rpc/websocket_test.go b/rpc/websocket_test.go
index ae7203e58f..42a6518310 100644
--- a/rpc/websocket_test.go
+++ b/rpc/websocket_test.go
@@ -108,7 +108,7 @@ func TestWebsocketLargeCall(t *testing.T) {
// This call sends slightly less than the limit and should work.
var result echoResult
- arg := strings.Repeat("x", maxRequestContentLength-200)
+ arg := strings.Repeat("x", defaultBodyLimit-200)
if err := client.Call(&result, "test_echo", arg, 1); err != nil {
t.Fatalf("valid call didn't work: %v", err)
}
@@ -117,7 +117,7 @@ func TestWebsocketLargeCall(t *testing.T) {
}
// This call sends twice the allowed size and shouldn't work.
- arg = strings.Repeat("x", maxRequestContentLength*2)
+ arg = strings.Repeat("x", defaultBodyLimit*2)
err = client.Call(&result, "test_echo", arg)
if err == nil {
t.Fatal("no error for too large call")
diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go
index 64e75c9ed4..89b621d329 100644
--- a/signer/core/apitypes/types.go
+++ b/signer/core/apitypes/types.go
@@ -61,7 +61,7 @@ func (vs *ValidationMessages) Info(msg string) {
vs.Messages = append(vs.Messages, ValidationInfo{INFO, msg})
}
-// getWarnings returns an error with all messages of type WARN of above, or nil if no warnings were present
+// GetWarnings returns an error with all messages of type WARN of above, or nil if no warnings were present
func (v *ValidationMessages) GetWarnings() error {
var messages []string
for _, msg := range v.Messages {
diff --git a/stateupgrade/interfaces.go b/stateupgrade/interfaces.go
index f667980487..a12351ecc9 100644
--- a/stateupgrade/interfaces.go
+++ b/stateupgrade/interfaces.go
@@ -7,13 +7,14 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
+ "github.com/holiman/uint256"
)
// StateDB is the interface for accessing EVM state in state upgrades
type StateDB interface {
SetState(common.Address, common.Hash, common.Hash)
SetCode(common.Address, []byte)
- AddBalance(common.Address, *big.Int)
+ AddBalance(common.Address, *uint256.Int)
GetNonce(common.Address) uint64
SetNonce(common.Address, uint64)
diff --git a/stateupgrade/state_upgrade.go b/stateupgrade/state_upgrade.go
index 682e7cd493..65a1f2dc69 100644
--- a/stateupgrade/state_upgrade.go
+++ b/stateupgrade/state_upgrade.go
@@ -8,6 +8,7 @@ import (
"github.com/ava-labs/subnet-evm/params"
"github.com/ethereum/go-ethereum/common"
+ "github.com/holiman/uint256"
)
// Configure applies the state upgrade to the state.
@@ -29,7 +30,8 @@ func upgradeAccount(account common.Address, upgrade params.StateUpgradeAccount,
}
if upgrade.BalanceChange != nil {
- state.AddBalance(account, (*big.Int)(upgrade.BalanceChange))
+ balanceChange, _ := uint256.FromBig((*big.Int)(upgrade.BalanceChange))
+ state.AddBalance(account, balanceChange)
}
if len(upgrade.Code) != 0 {
// if the nonce is 0, set the nonce to 1 as we would when deploying a contract at
diff --git a/sync/client/client_test.go b/sync/client/client_test.go
index 0ec9cafb77..1e65286085 100644
--- a/sync/client/client_test.go
+++ b/sync/client/client_test.go
@@ -25,7 +25,7 @@ import (
"github.com/ava-labs/subnet-evm/sync/handlers"
handlerstats "github.com/ava-labs/subnet-evm/sync/handlers/stats"
"github.com/ava-labs/subnet-evm/sync/syncutils"
- "github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
@@ -143,7 +143,7 @@ func TestGetBlocks(t *testing.T) {
Config: params.TestChainConfig,
}
memdb := rawdb.NewMemoryDatabase()
- tdb := trie.NewDatabase(memdb, nil)
+ tdb := triedb.NewDatabase(memdb, nil)
genesis := gspec.MustCommit(memdb, tdb)
engine := dummy.NewETHFaker()
numBlocks := 110
@@ -411,7 +411,7 @@ func TestGetLeafs(t *testing.T) {
const leafsLimit = 1024
- trieDB := trie.NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ trieDB := triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)
largeTrieRoot, largeTrieKeys, _ := syncutils.GenerateTrie(t, trieDB, 100_000, common.HashLength)
smallTrieRoot, _, _ := syncutils.GenerateTrie(t, trieDB, leafsLimit, common.HashLength)
@@ -782,7 +782,7 @@ func TestGetLeafs(t *testing.T) {
func TestGetLeafsRetries(t *testing.T) {
rand.Seed(1)
- trieDB := trie.NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ trieDB := triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)
root, _, _ := syncutils.GenerateTrie(t, trieDB, 100_000, common.HashLength)
handler := handlers.NewLeafsRequestHandler(trieDB, nil, message.Codec, handlerstats.NewNoopHandlerStats())
diff --git a/sync/handlers/block_request_test.go b/sync/handlers/block_request_test.go
index 968f71a309..dfc467c188 100644
--- a/sync/handlers/block_request_test.go
+++ b/sync/handlers/block_request_test.go
@@ -17,7 +17,7 @@ import (
"github.com/ava-labs/subnet-evm/params"
"github.com/ava-labs/subnet-evm/plugin/evm/message"
"github.com/ava-labs/subnet-evm/sync/handlers/stats"
- "github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
@@ -106,7 +106,7 @@ func TestBlockRequestHandler(t *testing.T) {
Config: params.TestChainConfig,
}
memdb := rawdb.NewMemoryDatabase()
- tdb := trie.NewDatabase(memdb, nil)
+ tdb := triedb.NewDatabase(memdb, nil)
genesis := gspec.MustCommit(memdb, tdb)
engine := dummy.NewETHFaker()
blocks, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, memdb, 96, 0, func(i int, b *core.BlockGen) {})
@@ -159,12 +159,12 @@ func TestBlockRequestHandlerLargeBlocks(t *testing.T) {
funds = big.NewInt(1000000000000000000)
gspec = &core.Genesis{
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
- Alloc: core.GenesisAlloc{addr1: {Balance: funds}},
+ Alloc: types.GenesisAlloc{addr1: {Balance: funds}},
}
signer = types.LatestSigner(gspec.Config)
)
memdb := rawdb.NewMemoryDatabase()
- tdb := trie.NewDatabase(memdb, nil)
+ tdb := triedb.NewDatabase(memdb, nil)
genesis := gspec.MustCommit(memdb, tdb)
engine := dummy.NewETHFaker()
blocks, _, err := core.GenerateChain(gspec.Config, genesis, engine, memdb, 96, 0, func(i int, b *core.BlockGen) {
@@ -218,7 +218,7 @@ func TestBlockRequestHandlerCtxExpires(t *testing.T) {
Config: params.TestChainConfig,
}
memdb := rawdb.NewMemoryDatabase()
- tdb := trie.NewDatabase(memdb, nil)
+ tdb := triedb.NewDatabase(memdb, nil)
genesis := gspec.MustCommit(memdb, tdb)
engine := dummy.NewETHFaker()
blocks, _, err := core.GenerateChain(params.TestChainConfig, genesis, engine, memdb, 11, 0, func(i int, b *core.BlockGen) {})
diff --git a/sync/handlers/leafs_request.go b/sync/handlers/leafs_request.go
index bc4abc809d..5222091649 100644
--- a/sync/handlers/leafs_request.go
+++ b/sync/handlers/leafs_request.go
@@ -17,6 +17,7 @@ import (
"github.com/ava-labs/subnet-evm/sync/handlers/stats"
"github.com/ava-labs/subnet-evm/sync/syncutils"
"github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ava-labs/subnet-evm/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
@@ -41,14 +42,14 @@ const (
// LeafsRequestHandler is a peer.RequestHandler for types.LeafsRequest
// serving requested trie data
type LeafsRequestHandler struct {
- trieDB *trie.Database
+ trieDB *triedb.Database
snapshotProvider SnapshotProvider
codec codec.Manager
stats stats.LeafsRequestHandlerStats
pool sync.Pool
}
-func NewLeafsRequestHandler(trieDB *trie.Database, snapshotProvider SnapshotProvider, codec codec.Manager, syncerStats stats.LeafsRequestHandlerStats) *LeafsRequestHandler {
+func NewLeafsRequestHandler(trieDB *triedb.Database, snapshotProvider SnapshotProvider, codec codec.Manager, syncerStats stats.LeafsRequestHandlerStats) *LeafsRequestHandler {
return &LeafsRequestHandler{
trieDB: trieDB,
snapshotProvider: snapshotProvider,
diff --git a/sync/handlers/leafs_request_test.go b/sync/handlers/leafs_request_test.go
index d73aa31ad8..7c97c8afa1 100644
--- a/sync/handlers/leafs_request_test.go
+++ b/sync/handlers/leafs_request_test.go
@@ -17,6 +17,7 @@ import (
"github.com/ava-labs/subnet-evm/sync/handlers/stats"
"github.com/ava-labs/subnet-evm/sync/syncutils"
"github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
@@ -27,7 +28,7 @@ func TestLeafsRequestHandler_OnLeafsRequest(t *testing.T) {
rand.Seed(1)
mockHandlerStats := &stats.MockHandlerStats{}
memdb := rawdb.NewMemoryDatabase()
- trieDB := trie.NewDatabase(memdb, nil)
+ trieDB := triedb.NewDatabase(memdb, nil)
corruptedTrieRoot, _, _ := syncutils.GenerateTrie(t, trieDB, 100, common.HashLength)
tr, err := trie.New(trie.TrieID(corruptedTrieRoot), trieDB)
diff --git a/sync/statesync/state_syncer.go b/sync/statesync/state_syncer.go
index 0cb7c33b54..873d81e000 100644
--- a/sync/statesync/state_syncer.go
+++ b/sync/statesync/state_syncer.go
@@ -10,7 +10,7 @@ import (
"github.com/ava-labs/subnet-evm/core/state/snapshot"
syncclient "github.com/ava-labs/subnet-evm/sync/client"
- "github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"golang.org/x/sync/errgroup"
@@ -37,7 +37,7 @@ type StateSyncerConfig struct {
type stateSync struct {
db ethdb.Database // database we are syncing
root common.Hash // root of the EVM state we are syncing to
- trieDB *trie.Database // trieDB on top of db we are syncing. used to restore any existing tries.
+ trieDB *triedb.Database // trieDB on top of db we are syncing. used to restore any existing tries.
snapshot snapshot.Snapshot // used to access the database we are syncing as a snapshot.
batchSize int // write batches when they reach this size
client syncclient.Client // used to contact peers over the network
@@ -68,7 +68,7 @@ func NewStateSyncer(config *StateSyncerConfig) (*stateSync, error) {
db: config.DB,
client: config.Client,
root: config.Root,
- trieDB: trie.NewDatabase(config.DB, nil),
+ trieDB: triedb.NewDatabase(config.DB, nil),
snapshot: snapshot.NewDiskLayer(config.DB),
stats: newTrieSyncStats(),
triesInProgress: make(map[common.Hash]*trieToSync),
diff --git a/sync/statesync/sync_test.go b/sync/statesync/sync_test.go
index 5d098d0856..1fe066e142 100644
--- a/sync/statesync/sync_test.go
+++ b/sync/statesync/sync_test.go
@@ -22,6 +22,7 @@ import (
handlerstats "github.com/ava-labs/subnet-evm/sync/handlers/stats"
"github.com/ava-labs/subnet-evm/sync/syncutils"
"github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
@@ -35,7 +36,7 @@ var errInterrupted = errors.New("interrupted sync")
type syncTest struct {
ctx context.Context
- prepareForTest func(t *testing.T) (clientDB ethdb.Database, serverDB ethdb.Database, serverTrieDB *trie.Database, syncRoot common.Hash)
+ prepareForTest func(t *testing.T) (clientDB ethdb.Database, serverDB ethdb.Database, serverTrieDB *triedb.Database, syncRoot common.Hash)
expectedError error
GetLeafsIntercept func(message.LeafsRequest, message.LeafsResponse) (message.LeafsResponse, error)
GetCodeIntercept func([]common.Hash, [][]byte) ([][]byte, error)
@@ -74,7 +75,7 @@ func testSync(t *testing.T, test syncTest) {
return
}
- assertDBConsistency(t, root, clientDB, serverTrieDB, trie.NewDatabase(clientDB, nil))
+ assertDBConsistency(t, root, clientDB, serverTrieDB, triedb.NewDatabase(clientDB, nil))
}
// testSyncResumes tests a series of syncTests work as expected, invoking a callback function after each
@@ -118,17 +119,17 @@ func TestSimpleSyncCases(t *testing.T) {
)
tests := map[string]syncTest{
"accounts": {
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
serverDB := rawdb.NewMemoryDatabase()
- serverTrieDB := trie.NewDatabase(serverDB, nil)
+ serverTrieDB := triedb.NewDatabase(serverDB, nil)
root, _ := syncutils.FillAccounts(t, serverTrieDB, common.Hash{}, numAccounts, nil)
return rawdb.NewMemoryDatabase(), serverDB, serverTrieDB, root
},
},
"accounts with code": {
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
serverDB := rawdb.NewMemoryDatabase()
- serverTrieDB := trie.NewDatabase(serverDB, nil)
+ serverTrieDB := triedb.NewDatabase(serverDB, nil)
root, _ := syncutils.FillAccounts(t, serverTrieDB, common.Hash{}, numAccounts, func(t *testing.T, index int, account types.StateAccount) types.StateAccount {
if index%3 == 0 {
codeBytes := make([]byte, 256)
@@ -147,17 +148,17 @@ func TestSimpleSyncCases(t *testing.T) {
},
},
"accounts with code and storage": {
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
serverDB := rawdb.NewMemoryDatabase()
- serverTrieDB := trie.NewDatabase(serverDB, nil)
+ serverTrieDB := triedb.NewDatabase(serverDB, nil)
root := fillAccountsWithStorage(t, serverDB, serverTrieDB, common.Hash{}, numAccounts)
return rawdb.NewMemoryDatabase(), serverDB, serverTrieDB, root
},
},
"accounts with storage": {
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
serverDB := rawdb.NewMemoryDatabase()
- serverTrieDB := trie.NewDatabase(serverDB, nil)
+ serverTrieDB := triedb.NewDatabase(serverDB, nil)
root, _ := syncutils.FillAccounts(t, serverTrieDB, common.Hash{}, numAccounts, func(t *testing.T, i int, account types.StateAccount) types.StateAccount {
if i%5 == 0 {
account.Root, _, _ = syncutils.GenerateTrie(t, serverTrieDB, 16, common.HashLength)
@@ -169,17 +170,17 @@ func TestSimpleSyncCases(t *testing.T) {
},
},
"accounts with overlapping storage": {
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
serverDB := rawdb.NewMemoryDatabase()
- serverTrieDB := trie.NewDatabase(serverDB, nil)
+ serverTrieDB := triedb.NewDatabase(serverDB, nil)
root, _ := FillAccountsWithOverlappingStorage(t, serverTrieDB, common.Hash{}, numAccounts, 3)
return rawdb.NewMemoryDatabase(), serverDB, serverTrieDB, root
},
},
"failed to fetch leafs": {
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
serverDB := rawdb.NewMemoryDatabase()
- serverTrieDB := trie.NewDatabase(serverDB, nil)
+ serverTrieDB := triedb.NewDatabase(serverDB, nil)
root, _ := syncutils.FillAccounts(t, serverTrieDB, common.Hash{}, numAccountsSmall, nil)
return rawdb.NewMemoryDatabase(), serverDB, serverTrieDB, root
},
@@ -189,9 +190,9 @@ func TestSimpleSyncCases(t *testing.T) {
expectedError: clientErr,
},
"failed to fetch code": {
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
serverDB := rawdb.NewMemoryDatabase()
- serverTrieDB := trie.NewDatabase(serverDB, nil)
+ serverTrieDB := triedb.NewDatabase(serverDB, nil)
root := fillAccountsWithStorage(t, serverDB, serverTrieDB, common.Hash{}, numAccountsSmall)
return rawdb.NewMemoryDatabase(), serverDB, serverTrieDB, root
},
@@ -211,14 +212,14 @@ func TestSimpleSyncCases(t *testing.T) {
func TestCancelSync(t *testing.T) {
serverDB := rawdb.NewMemoryDatabase()
- serverTrieDB := trie.NewDatabase(serverDB, nil)
+ serverTrieDB := triedb.NewDatabase(serverDB, nil)
// Create trie with 2000 accounts (more than one leaf request)
root := fillAccountsWithStorage(t, serverDB, serverTrieDB, common.Hash{}, 2000)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
testSync(t, syncTest{
ctx: ctx,
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
return rawdb.NewMemoryDatabase(), serverDB, serverTrieDB, root
},
expectedError: context.Canceled,
@@ -252,7 +253,7 @@ func (i *interruptLeafsIntercept) getLeafsIntercept(request message.LeafsRequest
func TestResumeSyncAccountsTrieInterrupted(t *testing.T) {
serverDB := rawdb.NewMemoryDatabase()
- serverTrieDB := trie.NewDatabase(serverDB, nil)
+ serverTrieDB := triedb.NewDatabase(serverDB, nil)
root, _ := FillAccountsWithOverlappingStorage(t, serverTrieDB, common.Hash{}, 2000, 3)
clientDB := rawdb.NewMemoryDatabase()
intercept := &interruptLeafsIntercept{
@@ -260,7 +261,7 @@ func TestResumeSyncAccountsTrieInterrupted(t *testing.T) {
interruptAfter: 1,
}
testSync(t, syncTest{
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
return clientDB, serverDB, serverTrieDB, root
},
expectedError: errInterrupted,
@@ -270,7 +271,7 @@ func TestResumeSyncAccountsTrieInterrupted(t *testing.T) {
assert.EqualValues(t, 2, intercept.numRequests)
testSync(t, syncTest{
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
return clientDB, serverDB, serverTrieDB, root
},
})
@@ -278,7 +279,7 @@ func TestResumeSyncAccountsTrieInterrupted(t *testing.T) {
func TestResumeSyncLargeStorageTrieInterrupted(t *testing.T) {
serverDB := rawdb.NewMemoryDatabase()
- serverTrieDB := trie.NewDatabase(serverDB, nil)
+ serverTrieDB := triedb.NewDatabase(serverDB, nil)
largeStorageRoot, _, _ := syncutils.GenerateTrie(t, serverTrieDB, 2000, common.HashLength)
root, _ := syncutils.FillAccounts(t, serverTrieDB, common.Hash{}, 2000, func(t *testing.T, index int, account types.StateAccount) types.StateAccount {
@@ -294,7 +295,7 @@ func TestResumeSyncLargeStorageTrieInterrupted(t *testing.T) {
interruptAfter: 1,
}
testSync(t, syncTest{
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
return clientDB, serverDB, serverTrieDB, root
},
expectedError: errInterrupted,
@@ -302,7 +303,7 @@ func TestResumeSyncLargeStorageTrieInterrupted(t *testing.T) {
})
testSync(t, syncTest{
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
return clientDB, serverDB, serverTrieDB, root
},
})
@@ -310,7 +311,7 @@ func TestResumeSyncLargeStorageTrieInterrupted(t *testing.T) {
func TestResumeSyncToNewRootAfterLargeStorageTrieInterrupted(t *testing.T) {
serverDB := rawdb.NewMemoryDatabase()
- serverTrieDB := trie.NewDatabase(serverDB, nil)
+ serverTrieDB := triedb.NewDatabase(serverDB, nil)
largeStorageRoot1, _, _ := syncutils.GenerateTrie(t, serverTrieDB, 2000, common.HashLength)
largeStorageRoot2, _, _ := syncutils.GenerateTrie(t, serverTrieDB, 2000, common.HashLength)
@@ -333,7 +334,7 @@ func TestResumeSyncToNewRootAfterLargeStorageTrieInterrupted(t *testing.T) {
interruptAfter: 1,
}
testSync(t, syncTest{
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
return clientDB, serverDB, serverTrieDB, root1
},
expectedError: errInterrupted,
@@ -343,7 +344,7 @@ func TestResumeSyncToNewRootAfterLargeStorageTrieInterrupted(t *testing.T) {
<-snapshot.WipeSnapshot(clientDB, false)
testSync(t, syncTest{
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
return clientDB, serverDB, serverTrieDB, root2
},
})
@@ -351,7 +352,7 @@ func TestResumeSyncToNewRootAfterLargeStorageTrieInterrupted(t *testing.T) {
func TestResumeSyncLargeStorageTrieWithConsecutiveDuplicatesInterrupted(t *testing.T) {
serverDB := rawdb.NewMemoryDatabase()
- serverTrieDB := trie.NewDatabase(serverDB, nil)
+ serverTrieDB := triedb.NewDatabase(serverDB, nil)
largeStorageRoot, _, _ := syncutils.GenerateTrie(t, serverTrieDB, 2000, common.HashLength)
root, _ := syncutils.FillAccounts(t, serverTrieDB, common.Hash{}, 100, func(t *testing.T, index int, account types.StateAccount) types.StateAccount {
@@ -367,7 +368,7 @@ func TestResumeSyncLargeStorageTrieWithConsecutiveDuplicatesInterrupted(t *testi
interruptAfter: 1,
}
testSync(t, syncTest{
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
return clientDB, serverDB, serverTrieDB, root
},
expectedError: errInterrupted,
@@ -375,7 +376,7 @@ func TestResumeSyncLargeStorageTrieWithConsecutiveDuplicatesInterrupted(t *testi
})
testSync(t, syncTest{
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
return clientDB, serverDB, serverTrieDB, root
},
})
@@ -383,7 +384,7 @@ func TestResumeSyncLargeStorageTrieWithConsecutiveDuplicatesInterrupted(t *testi
func TestResumeSyncLargeStorageTrieWithSpreadOutDuplicatesInterrupted(t *testing.T) {
serverDB := rawdb.NewMemoryDatabase()
- serverTrieDB := trie.NewDatabase(serverDB, nil)
+ serverTrieDB := triedb.NewDatabase(serverDB, nil)
largeStorageRoot, _, _ := syncutils.GenerateTrie(t, serverTrieDB, 2000, common.HashLength)
root, _ := syncutils.FillAccounts(t, serverTrieDB, common.Hash{}, 100, func(t *testing.T, index int, account types.StateAccount) types.StateAccount {
@@ -398,7 +399,7 @@ func TestResumeSyncLargeStorageTrieWithSpreadOutDuplicatesInterrupted(t *testing
interruptAfter: 1,
}
testSync(t, syncTest{
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
return clientDB, serverDB, serverTrieDB, root
},
expectedError: errInterrupted,
@@ -406,7 +407,7 @@ func TestResumeSyncLargeStorageTrieWithSpreadOutDuplicatesInterrupted(t *testing
})
testSync(t, syncTest{
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
return clientDB, serverDB, serverTrieDB, root
},
})
@@ -436,7 +437,7 @@ func TestResyncNewRootAfterDeletes(t *testing.T) {
},
"delete intermediate storage nodes": {
deleteBetweenSyncs: func(t *testing.T, root common.Hash, clientDB ethdb.Database) {
- clientTrieDB := trie.NewDatabase(clientDB, nil)
+ clientTrieDB := triedb.NewDatabase(clientDB, nil)
tr, err := trie.New(trie.TrieID(root), clientTrieDB)
if err != nil {
t.Fatal(err)
@@ -482,7 +483,7 @@ func TestResyncNewRootAfterDeletes(t *testing.T) {
},
"delete intermediate account trie nodes": {
deleteBetweenSyncs: func(t *testing.T, root common.Hash, clientDB ethdb.Database) {
- clientTrieDB := trie.NewDatabase(clientDB, nil)
+ clientTrieDB := triedb.NewDatabase(clientDB, nil)
tr, err := trie.New(trie.TrieID(root), clientTrieDB)
if err != nil {
t.Fatal(err)
@@ -501,7 +502,7 @@ func testSyncerSyncsToNewRoot(t *testing.T, deleteBetweenSyncs func(*testing.T,
rand.Seed(1)
clientDB := rawdb.NewMemoryDatabase()
serverDB := rawdb.NewMemoryDatabase()
- serverTrieDB := trie.NewDatabase(serverDB, nil)
+ serverTrieDB := triedb.NewDatabase(serverDB, nil)
root1, _ := FillAccountsWithOverlappingStorage(t, serverTrieDB, common.Hash{}, 1000, 3)
root2, _ := FillAccountsWithOverlappingStorage(t, serverTrieDB, root1, 1000, 3)
@@ -510,12 +511,12 @@ func testSyncerSyncsToNewRoot(t *testing.T, deleteBetweenSyncs func(*testing.T,
testSyncResumes(t, []syncTest{
{
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
return clientDB, serverDB, serverTrieDB, root1
},
},
{
- prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *trie.Database, common.Hash) {
+ prepareForTest: func(t *testing.T) (ethdb.Database, ethdb.Database, *triedb.Database, common.Hash) {
return clientDB, serverDB, serverTrieDB, root2
},
},
diff --git a/sync/statesync/test_sync.go b/sync/statesync/test_sync.go
index a319e34c08..f3e32797fe 100644
--- a/sync/statesync/test_sync.go
+++ b/sync/statesync/test_sync.go
@@ -12,7 +12,7 @@ import (
"github.com/ava-labs/subnet-evm/core/rawdb"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/sync/syncutils"
- "github.com/ava-labs/subnet-evm/trie"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
@@ -23,7 +23,7 @@ import (
// assertDBConsistency checks [serverTrieDB] and [clientTrieDB] have the same EVM state trie at [root],
// and that [clientTrieDB.DiskDB] has corresponding account & snapshot values.
// Also verifies any code referenced by the EVM state is present in [clientTrieDB] and the hash is correct.
-func assertDBConsistency(t testing.TB, root common.Hash, clientDB ethdb.Database, serverTrieDB, clientTrieDB *trie.Database) {
+func assertDBConsistency(t testing.TB, root common.Hash, clientDB ethdb.Database, serverTrieDB, clientTrieDB *triedb.Database) {
numSnapshotAccounts := 0
accountIt := rawdb.IterateAccountSnapshots(clientDB)
defer accountIt.Release()
@@ -88,7 +88,7 @@ func assertDBConsistency(t testing.TB, root common.Hash, clientDB ethdb.Database
assert.Equal(t, trieAccountLeaves, numSnapshotAccounts)
}
-func fillAccountsWithStorage(t *testing.T, serverDB ethdb.Database, serverTrieDB *trie.Database, root common.Hash, numAccounts int) common.Hash {
+func fillAccountsWithStorage(t *testing.T, serverDB ethdb.Database, serverTrieDB *triedb.Database, root common.Hash, numAccounts int) common.Hash {
newRoot, _ := syncutils.FillAccounts(t, serverTrieDB, root, numAccounts, func(t *testing.T, index int, account types.StateAccount) types.StateAccount {
codeBytes := make([]byte, 256)
_, err := rand.Read(codeBytes)
@@ -115,7 +115,7 @@ func fillAccountsWithStorage(t *testing.T, serverDB ethdb.Database, serverTrieDB
// - One has a uniquely generated storage trie,
// returns the new trie root and a map of funded keys to StateAccount structs.
func FillAccountsWithOverlappingStorage(
- t *testing.T, trieDB *trie.Database, root common.Hash, numAccounts int, numOverlappingStorageRoots int,
+ t *testing.T, trieDB *triedb.Database, root common.Hash, numAccounts int, numOverlappingStorageRoots int,
) (common.Hash, map[*keystore.Key]*types.StateAccount) {
storageRoots := make([]common.Hash, 0, numOverlappingStorageRoots)
for i := 0; i < numOverlappingStorageRoots; i++ {
diff --git a/sync/syncutils/test_trie.go b/sync/syncutils/test_trie.go
index 23b603ce73..a02ce03216 100644
--- a/sync/syncutils/test_trie.go
+++ b/sync/syncutils/test_trie.go
@@ -6,7 +6,6 @@ package syncutils
import (
cryptoRand "crypto/rand"
"encoding/binary"
- "math/big"
"math/rand"
"testing"
@@ -15,10 +14,12 @@ import (
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/trie"
"github.com/ava-labs/subnet-evm/trie/trienode"
+ "github.com/ava-labs/subnet-evm/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/holiman/uint256"
"github.com/stretchr/testify/assert"
)
@@ -26,7 +27,7 @@ import (
// Returns the root of the generated trie, the slice of keys inserted into the trie in lexicographical
// order, and the slice of corresponding values.
// GenerateTrie reads from [rand] and the caller should call rand.Seed(n) for deterministic results
-func GenerateTrie(t *testing.T, trieDB *trie.Database, numKeys int, keySize int) (common.Hash, [][]byte, [][]byte) {
+func GenerateTrie(t *testing.T, trieDB *triedb.Database, numKeys int, keySize int) (common.Hash, [][]byte, [][]byte) {
if keySize < wrappers.LongLen+1 {
t.Fatal("key size must be at least 9 bytes (8 bytes for uint64 and 1 random byte)")
}
@@ -36,7 +37,7 @@ func GenerateTrie(t *testing.T, trieDB *trie.Database, numKeys int, keySize int)
// FillTrie fills a given trie with [numKeys] number of keys, each of size [keySize]
// returns inserted keys and values
// FillTrie reads from [rand] and the caller should call rand.Seed(n) for deterministic results
-func FillTrie(t *testing.T, start, numKeys int, keySize int, trieDB *trie.Database, root common.Hash) (common.Hash, [][]byte, [][]byte) {
+func FillTrie(t *testing.T, start, numKeys int, keySize int, trieDB *triedb.Database, root common.Hash) (common.Hash, [][]byte, [][]byte) {
testTrie, err := trie.New(trie.TrieID(root), trieDB)
if err != nil {
t.Fatalf("error creating trie: %v", err)
@@ -75,7 +76,7 @@ func FillTrie(t *testing.T, start, numKeys int, keySize int, trieDB *trie.Databa
// AssertTrieConsistency ensures given trieDB [a] and [b] both have the same
// non-empty trie at [root]. (all key/value pairs must be equal)
-func AssertTrieConsistency(t testing.TB, root common.Hash, a, b *trie.Database, onLeaf func(key, val []byte) error) {
+func AssertTrieConsistency(t testing.TB, root common.Hash, a, b *triedb.Database, onLeaf func(key, val []byte) error) {
trieA, err := trie.New(trie.TrieID(root), a)
if err != nil {
t.Fatalf("error creating trieA, root=%s, err=%v", root, err)
@@ -144,12 +145,12 @@ func CorruptTrie(t *testing.T, diskdb ethdb.Batcher, tr *trie.Trie, n int) {
// [onAccount] is called if non-nil (so the caller can modify the account before it is stored in the secure trie).
// returns the new trie root and a map of funded keys to StateAccount structs.
func FillAccounts(
- t *testing.T, trieDB *trie.Database, root common.Hash, numAccounts int,
+ t *testing.T, trieDB *triedb.Database, root common.Hash, numAccounts int,
onAccount func(*testing.T, int, types.StateAccount) types.StateAccount,
) (common.Hash, map[*keystore.Key]*types.StateAccount) {
var (
- minBalance = big.NewInt(3000000000000000000)
- randBalance = big.NewInt(1000000000000000000)
+ minBalance = uint256.NewInt(3000000000000000000)
+ randBalance = uint256.NewInt(1000000000000000000)
maxNonce = 10
accounts = make(map[*keystore.Key]*types.StateAccount, numAccounts)
)
@@ -162,7 +163,7 @@ func FillAccounts(
for i := 0; i < numAccounts; i++ {
acc := types.StateAccount{
Nonce: uint64(rand.Intn(maxNonce)),
- Balance: new(big.Int).Add(minBalance, randBalance),
+ Balance: new(uint256.Int).Add(minBalance, randBalance),
CodeHash: types.EmptyCodeHash[:],
Root: types.EmptyRootHash,
}
diff --git a/tests/gen_stenv.go b/tests/gen_stenv.go
new file mode 100644
index 0000000000..9bcdb5d162
--- /dev/null
+++ b/tests/gen_stenv.go
@@ -0,0 +1,86 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package tests
+
+import (
+ "encoding/json"
+ "errors"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/math"
+)
+
+var _ = (*stEnvMarshaling)(nil)
+
+// MarshalJSON marshals as JSON.
+func (s stEnv) MarshalJSON() ([]byte, error) {
+ type stEnv struct {
+ Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
+ Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
+ Random *math.HexOrDecimal256 `json:"currentRandom" gencodec:"optional"`
+ GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
+ Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
+ Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
+ BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"`
+ ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas" gencodec:"optional"`
+ }
+ var enc stEnv
+ enc.Coinbase = common.UnprefixedAddress(s.Coinbase)
+ enc.Difficulty = (*math.HexOrDecimal256)(s.Difficulty)
+ enc.Random = (*math.HexOrDecimal256)(s.Random)
+ enc.GasLimit = math.HexOrDecimal64(s.GasLimit)
+ enc.Number = math.HexOrDecimal64(s.Number)
+ enc.Timestamp = math.HexOrDecimal64(s.Timestamp)
+ enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee)
+ enc.ExcessBlobGas = (*math.HexOrDecimal64)(s.ExcessBlobGas)
+ return json.Marshal(&enc)
+}
+
+// UnmarshalJSON unmarshals from JSON.
+func (s *stEnv) UnmarshalJSON(input []byte) error {
+ type stEnv struct {
+ Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
+ Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
+ Random *math.HexOrDecimal256 `json:"currentRandom" gencodec:"optional"`
+ GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
+ Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
+ Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
+ BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"`
+ ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas" gencodec:"optional"`
+ }
+ var dec stEnv
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.Coinbase == nil {
+ return errors.New("missing required field 'currentCoinbase' for stEnv")
+ }
+ s.Coinbase = common.Address(*dec.Coinbase)
+ if dec.Difficulty == nil {
+ return errors.New("missing required field 'currentDifficulty' for stEnv")
+ }
+ s.Difficulty = (*big.Int)(dec.Difficulty)
+ if dec.Random != nil {
+ s.Random = (*big.Int)(dec.Random)
+ }
+ if dec.GasLimit == nil {
+ return errors.New("missing required field 'currentGasLimit' for stEnv")
+ }
+ s.GasLimit = uint64(*dec.GasLimit)
+ if dec.Number == nil {
+ return errors.New("missing required field 'currentNumber' for stEnv")
+ }
+ s.Number = uint64(*dec.Number)
+ if dec.Timestamp == nil {
+ return errors.New("missing required field 'currentTimestamp' for stEnv")
+ }
+ s.Timestamp = uint64(*dec.Timestamp)
+ if dec.BaseFee != nil {
+ s.BaseFee = (*big.Int)(dec.BaseFee)
+ }
+ if dec.ExcessBlobGas != nil {
+ s.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
+ }
+ return nil
+}
diff --git a/tests/state_test_util.go b/tests/state_test_util.go
index a31ea88a95..7ba439accc 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -35,6 +35,7 @@ import (
"strconv"
"strings"
+ "github.com/ava-labs/subnet-evm/consensus/misc/eip4844"
"github.com/ava-labs/subnet-evm/core"
"github.com/ava-labs/subnet-evm/core/rawdb"
"github.com/ava-labs/subnet-evm/core/state"
@@ -42,15 +43,16 @@ import (
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/core/vm"
"github.com/ava-labs/subnet-evm/params"
- "github.com/ava-labs/subnet-evm/trie"
- "github.com/ava-labs/subnet-evm/trie/triedb/hashdb"
- "github.com/ava-labs/subnet-evm/trie/triedb/pathdb"
+ "github.com/ava-labs/subnet-evm/triedb"
+ "github.com/ava-labs/subnet-evm/triedb/hashdb"
+ "github.com/ava-labs/subnet-evm/triedb/pathdb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/holiman/uint256"
"golang.org/x/crypto/sha3"
)
@@ -72,7 +74,7 @@ func (t *StateTest) UnmarshalJSON(in []byte) error {
type stJSON struct {
Env stEnv `json:"env"`
- Pre core.GenesisAlloc `json:"pre"`
+ Pre types.GenesisAlloc `json:"pre"`
Tx stTransaction `json:"transaction"`
Out hexutil.Bytes `json:"out"`
Post map[string][]stPostState `json:"post"`
@@ -92,13 +94,25 @@ type stPostState struct {
//go:generate go run github.com/fjl/gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
type stEnv struct {
- Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
- Difficulty *big.Int `json:"currentDifficulty" gencodec:"required"`
- Random *big.Int `json:"currentRandom" gencodec:"optional"`
- GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
- Number uint64 `json:"currentNumber" gencodec:"required"`
- Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
- BaseFee *big.Int `json:"currentBaseFee" gencodec:"optional"`
+ Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
+ Difficulty *big.Int `json:"currentDifficulty" gencodec:"required"`
+ Random *big.Int `json:"currentRandom" gencodec:"optional"`
+ GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
+ Number uint64 `json:"currentNumber" gencodec:"required"`
+ Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
+ BaseFee *big.Int `json:"currentBaseFee" gencodec:"optional"`
+ ExcessBlobGas *uint64 `json:"currentExcessBlobGas" gencodec:"optional"`
+}
+
+type stEnvMarshaling struct {
+ Coinbase common.UnprefixedAddress
+ Difficulty *math.HexOrDecimal256
+ Random *math.HexOrDecimal256
+ GasLimit math.HexOrDecimal64
+ Number math.HexOrDecimal64
+ Timestamp math.HexOrDecimal64
+ BaseFee *math.HexOrDecimal256
+ ExcessBlobGas *math.HexOrDecimal64
}
//go:generate go run github.com/fjl/gencodec -type stTransaction -field-override stTransactionMarshaling -out gen_sttransaction.go
@@ -203,20 +217,14 @@ func (t *StateTest) checkError(subtest StateSubtest, err error) error {
}
// Run executes a specific subtest and verifies the post-state and logs
-func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string, postCheck func(err error, snaps *snapshot.Tree, state *state.StateDB)) (result error) {
- triedb, snaps, statedb, root, err := t.RunNoVerify(subtest, vmconfig, snapshotter, scheme)
-
+func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string, postCheck func(err error, st *StateTestState)) (result error) {
+ st, root, err := t.RunNoVerify(subtest, vmconfig, snapshotter, scheme)
// Invoke the callback at the end of function for further analysis.
defer func() {
- postCheck(result, snaps, statedb)
-
- if triedb != nil {
- triedb.Close()
- }
- if snaps != nil {
- snaps.Release()
- }
+ postCheck(result, &st)
+ st.Close()
}()
+
checkedErr := t.checkError(subtest, err)
if checkedErr != nil {
return checkedErr
@@ -233,23 +241,24 @@ func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config, snapshotter bo
if root != common.Hash(post.Root) {
return fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root)
}
- if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) {
+ if logs := rlpHash(st.StateDB.Logs()); logs != common.Hash(post.Logs) {
return fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, post.Logs)
}
- statedb, _ = state.New(root, statedb.Database(), snaps)
+ st.StateDB, _ = state.New(root, st.StateDB.Database(), st.Snapshots)
return nil
}
-// RunNoVerify runs a specific subtest and returns the statedb and post-state root
-func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string) (*trie.Database, *snapshot.Tree, *state.StateDB, common.Hash, error) {
+// RunNoVerify runs a specific subtest and returns the statedb and post-state root.
+// Remember to call state.Close after verifying the test result!
+func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapshotter bool, scheme string) (state StateTestState, root common.Hash, err error) {
config, eips, err := GetChainConfig(subtest.Fork)
if err != nil {
- return nil, nil, nil, common.Hash{}, UnsupportedForkError{subtest.Fork}
+ return state, common.Hash{}, UnsupportedForkError{subtest.Fork}
}
vmconfig.ExtraEips = eips
block := t.genesis(config).ToBlock()
- triedb, snaps, statedb := MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, snapshotter, scheme)
+ state = MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, snapshotter, scheme)
var baseFee *big.Int
if config.IsSubnetEVM(0) {
@@ -263,8 +272,18 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
post := t.json.Post[subtest.Fork][subtest.Index]
msg, err := t.json.Tx.toMessage(post, baseFee)
if err != nil {
- triedb.Close()
- return nil, nil, nil, common.Hash{}, err
+ return state, common.Hash{}, err
+ }
+
+ { // Blob transactions may be present after the Cancun fork.
+ // In production,
+ // - the header is verified against the max in eip4844.go:VerifyEIP4844Header
+ // - the block body is verified against the header in block_validator.go:ValidateBody
+ // Here, we just do this shortcut smaller fix, since state tests do not
+ // utilize those codepaths
+ if len(msg.BlobHashes)*params.BlobTxBlobGasPerBlob > params.MaxBlobGasPerBlock {
+ return state, common.Hash{}, errors.New("blob gas exceeds maximum")
+ }
}
// Try to recover tx with current signer
@@ -272,13 +291,10 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
var ttx types.Transaction
err := ttx.UnmarshalBinary(post.TxBytes)
if err != nil {
- triedb.Close()
- return nil, nil, nil, common.Hash{}, err
+ return state, common.Hash{}, err
}
-
if _, err := types.Sender(types.LatestSigner(config), &ttx); err != nil {
- triedb.Close()
- return nil, nil, nil, common.Hash{}, err
+ return state, common.Hash{}, err
}
}
@@ -290,60 +306,29 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
if config.IsSubnetEVM(0) && t.json.Env.Random != nil {
context.Difficulty = big.NewInt(0)
}
- evm := vm.NewEVM(context, txContext, statedb, config, vmconfig)
+ if config.IsCancun(new(big.Int), block.Time()) && t.json.Env.ExcessBlobGas != nil {
+ context.BlobBaseFee = eip4844.CalcBlobFee(*t.json.Env.ExcessBlobGas)
+ }
+ evm := vm.NewEVM(context, txContext, state.StateDB, config, vmconfig)
// Execute the message.
- snapshot := statedb.Snapshot()
+ snapshot := state.StateDB.Snapshot()
gaspool := new(core.GasPool)
gaspool.AddGas(block.GasLimit())
_, err = core.ApplyMessage(evm, msg, gaspool)
if err != nil {
- statedb.RevertToSnapshot(snapshot)
+ state.StateDB.RevertToSnapshot(snapshot)
}
// Add 0-value mining reward. This only makes a difference in the cases
// where
// - the coinbase self-destructed, or
// - there are only 'bad' transactions, which aren't executed. In those cases,
// the coinbase gets no txfee, so isn't created, and thus needs to be touched
- statedb.AddBalance(block.Coinbase(), new(big.Int))
- // Commit block
- root, _ := statedb.Commit(block.NumberU64(), config.IsEIP158(block.Number()), false)
- return triedb, snaps, statedb, root, err
-}
+ state.StateDB.AddBalance(block.Coinbase(), new(uint256.Int))
-func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter bool, scheme string) (*trie.Database, *snapshot.Tree, *state.StateDB) {
- tconf := &trie.Config{Preimages: true}
- if scheme == rawdb.HashScheme {
- tconf.HashDB = hashdb.Defaults
- } else {
- tconf.PathDB = pathdb.Defaults
- }
- triedb := trie.NewDatabase(db, tconf)
- sdb := state.NewDatabaseWithNodeDB(db, triedb)
- statedb, _ := state.New(types.EmptyRootHash, sdb, nil)
- for addr, a := range accounts {
- statedb.SetCode(addr, a.Code)
- statedb.SetNonce(addr, a.Nonce)
- statedb.SetBalance(addr, a.Balance)
- for k, v := range a.Storage {
- statedb.SetState(addr, k, v)
- }
- }
- // Commit and re-open to start with a clean state.
- root, _ := statedb.Commit(0, false, false)
-
- var snaps *snapshot.Tree
- if snapshotter {
- snapconfig := snapshot.Config{
- CacheSize: 1,
- NoBuild: false,
- AsyncBuild: false,
- SkipVerify: true,
- }
- snaps, _ = snapshot.New(snapconfig, db, triedb, common.Hash{}, root)
- }
- statedb, _ = state.New(root, sdb, snaps)
- return triedb, snaps, statedb
+ // Commit state mutations into database.
+ root, _ = state.StateDB.Commit(block.NumberU64(), config.IsEIP158(block.Number()), false)
+ return state, root, err
}
func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis {
@@ -462,3 +447,61 @@ func rlpHash(x interface{}) (h common.Hash) {
func vmTestBlockHash(n uint64) common.Hash {
return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
}
+
+// StateTestState groups all the state database objects together for use in tests.
+type StateTestState struct {
+ StateDB *state.StateDB
+ TrieDB *triedb.Database
+ Snapshots *snapshot.Tree
+}
+
+// MakePreState creates a state containing the given allocation.
+func MakePreState(db ethdb.Database, accounts types.GenesisAlloc, snapshotter bool, scheme string) StateTestState {
+ tconf := &triedb.Config{Preimages: true}
+ if scheme == rawdb.HashScheme {
+ tconf.HashDB = hashdb.Defaults
+ } else {
+ tconf.PathDB = pathdb.Defaults
+ }
+ triedb := triedb.NewDatabase(db, tconf)
+ sdb := state.NewDatabaseWithNodeDB(db, triedb)
+ statedb, _ := state.New(types.EmptyRootHash, sdb, nil)
+ for addr, a := range accounts {
+ statedb.SetCode(addr, a.Code)
+ statedb.SetNonce(addr, a.Nonce)
+ statedb.SetBalance(addr, uint256.MustFromBig(a.Balance))
+ for k, v := range a.Storage {
+ statedb.SetState(addr, k, v)
+ }
+ }
+ // Commit and re-open to start with a clean state.
+ root, _ := statedb.Commit(0, false, false)
+
+ // If snapshot is requested, initialize the snapshotter and use it in state.
+ var snaps *snapshot.Tree
+ if snapshotter {
+ snapconfig := snapshot.Config{
+ CacheSize: 1,
+ NoBuild: false,
+ AsyncBuild: false,
+ SkipVerify: true,
+ }
+ snaps, _ = snapshot.New(snapconfig, db, triedb, common.Hash{}, root)
+ }
+ statedb, _ = state.New(root, sdb, snaps)
+ return StateTestState{statedb, triedb, snaps}
+}
+
+// Close should be called when the state is no longer needed, ie. after running the test.
+func (st *StateTestState) Close() {
+ if st.TrieDB != nil {
+ st.TrieDB.Close()
+ st.TrieDB = nil
+ }
+ if st.Snapshots != nil {
+ // Need to call Disable here to quit the snapshot generator goroutine.
+ st.Snapshots.AbortGeneration()
+ st.Snapshots.Release()
+ st.Snapshots = nil
+ }
+}
diff --git a/trie/committer.go b/trie/committer.go
index 1ce9ccf33d..97d7ff6f6f 100644
--- a/trie/committer.go
+++ b/trie/committer.go
@@ -164,12 +164,12 @@ func (c *committer) store(path []byte, n node) node {
return hash
}
-// mptResolver the children resolver in merkle-patricia-tree.
-type mptResolver struct{}
+// MerkleResolver the children resolver in merkle-patricia-tree.
+type MerkleResolver struct{}
// ForEach implements childResolver, decodes the provided node and
// traverses the children inside.
-func (resolver mptResolver) ForEach(node []byte, onChild func(common.Hash)) {
+func (resolver MerkleResolver) ForEach(node []byte, onChild func(common.Hash)) {
forGatherChildren(mustDecodeNodeUnsafe(nil, node), onChild)
}
diff --git a/trie/database_test.go b/trie/database_test.go
index fc5598df6a..d844eb4a37 100644
--- a/trie/database_test.go
+++ b/trie/database_test.go
@@ -28,23 +28,135 @@ package trie
import (
"github.com/ava-labs/subnet-evm/core/rawdb"
- "github.com/ava-labs/subnet-evm/trie/triedb/hashdb"
- "github.com/ava-labs/subnet-evm/trie/triedb/pathdb"
+ "github.com/ava-labs/subnet-evm/core/types"
+ "github.com/ava-labs/subnet-evm/trie/trienode"
+ "github.com/ava-labs/subnet-evm/triedb/database"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
)
-// newTestDatabase initializes the trie database with specified scheme.
-func newTestDatabase(diskdb ethdb.Database, scheme string) *Database {
- config := &Config{Preimages: false}
- if scheme == rawdb.HashScheme {
- config.HashDB = &hashdb.Config{
- CleanCacheSize: 0,
- } // disable clean cache
- } else {
- config.PathDB = &pathdb.Config{
- CleanCacheSize: 0,
- DirtyCacheSize: 0,
- } // disable clean/dirty cache
- }
- return NewDatabase(diskdb, config)
+// testReader implements database.Reader interface, providing function to
+// access trie nodes.
+type testReader struct {
+ db ethdb.Database
+ scheme string
+ nodes []*trienode.MergedNodeSet // sorted from new to old
+}
+
+// Node implements database.Reader interface, retrieving trie node with
+// all available cached layers.
+func (r *testReader) Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error) {
+ // Check the node presence with the cached layer, from latest to oldest.
+ for _, nodes := range r.nodes {
+ if _, ok := nodes.Sets[owner]; !ok {
+ continue
+ }
+ n, ok := nodes.Sets[owner].Nodes[string(path)]
+ if !ok {
+ continue
+ }
+ if n.IsDeleted() || n.Hash != hash {
+ return nil, &MissingNodeError{Owner: owner, Path: path, NodeHash: hash}
+ }
+ return n.Blob, nil
+ }
+ // Check the node presence in database.
+ return rawdb.ReadTrieNode(r.db, owner, path, hash, r.scheme), nil
+}
+
+// testDb implements database.Database interface, using for testing purpose.
+type testDb struct {
+ disk ethdb.Database
+ root common.Hash
+ scheme string
+ nodes map[common.Hash]*trienode.MergedNodeSet
+ parents map[common.Hash]common.Hash
+}
+
+func newTestDatabase(diskdb ethdb.Database, scheme string) *testDb {
+ return &testDb{
+ disk: diskdb,
+ root: types.EmptyRootHash,
+ scheme: scheme,
+ nodes: make(map[common.Hash]*trienode.MergedNodeSet),
+ parents: make(map[common.Hash]common.Hash),
+ }
+}
+
+func (db *testDb) Reader(stateRoot common.Hash) (database.Reader, error) {
+ nodes, _ := db.dirties(stateRoot, true)
+ return &testReader{db: db.disk, scheme: db.scheme, nodes: nodes}, nil
+}
+
+func (db *testDb) Preimage(hash common.Hash) []byte {
+ return rawdb.ReadPreimage(db.disk, hash)
+}
+
+func (db *testDb) InsertPreimage(preimages map[common.Hash][]byte) {
+ rawdb.WritePreimages(db.disk, preimages)
+}
+
+func (db *testDb) Scheme() string { return db.scheme }
+
+func (db *testDb) Update(root common.Hash, parent common.Hash, nodes *trienode.MergedNodeSet) error {
+ if root == parent {
+ return nil
+ }
+ if _, ok := db.nodes[root]; ok {
+ return nil
+ }
+ db.parents[root] = parent
+ db.nodes[root] = nodes
+ return nil
+}
+
+func (db *testDb) dirties(root common.Hash, topToBottom bool) ([]*trienode.MergedNodeSet, []common.Hash) {
+ var (
+ pending []*trienode.MergedNodeSet
+ roots []common.Hash
+ )
+ for {
+ if root == db.root {
+ break
+ }
+ nodes, ok := db.nodes[root]
+ if !ok {
+ break
+ }
+ if topToBottom {
+ pending = append(pending, nodes)
+ roots = append(roots, root)
+ } else {
+ pending = append([]*trienode.MergedNodeSet{nodes}, pending...)
+ roots = append([]common.Hash{root}, roots...)
+ }
+ root = db.parents[root]
+ }
+ return pending, roots
+}
+
+func (db *testDb) Commit(root common.Hash) error {
+ if root == db.root {
+ return nil
+ }
+ pending, roots := db.dirties(root, false)
+ for i, nodes := range pending {
+ for owner, set := range nodes.Sets {
+ if owner == (common.Hash{}) {
+ continue
+ }
+ set.ForEachWithOrder(func(path string, n *trienode.Node) {
+ rawdb.WriteTrieNode(db.disk, owner, []byte(path), n.Hash, n.Blob, db.scheme)
+ })
+ }
+ nodes.Sets[common.Hash{}].ForEachWithOrder(func(path string, n *trienode.Node) {
+ rawdb.WriteTrieNode(db.disk, common.Hash{}, []byte(path), n.Hash, n.Blob, db.scheme)
+ })
+ db.root = roots[i]
+ }
+ for _, root := range roots {
+ delete(db.nodes, root)
+ delete(db.parents, root)
+ }
+ return nil
}
diff --git a/trie/iterator_test.go b/trie/iterator_test.go
index 2ba4c33e21..a329e27f6e 100644
--- a/trie/iterator_test.go
+++ b/trie/iterator_test.go
@@ -40,7 +40,7 @@ import (
)
func TestEmptyIterator(t *testing.T) {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
iter := trie.MustNodeIterator(nil)
seen := make(map[string]struct{})
@@ -53,7 +53,7 @@ func TestEmptyIterator(t *testing.T) {
}
func TestIterator(t *testing.T) {
- db := NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
trie := NewEmpty(db)
vals := []struct{ k, v string }{
{"do", "verb"},
@@ -70,7 +70,7 @@ func TestIterator(t *testing.T) {
trie.MustUpdate([]byte(val.k), []byte(val.v))
}
root, nodes, _ := trie.Commit(false)
- db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
+ db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
trie, _ = New(TrieID(root), db)
found := make(map[string]string)
@@ -96,7 +96,7 @@ func (k *kv) cmp(other *kv) int {
}
func TestIteratorLargeData(t *testing.T) {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
vals := make(map[string]*kv)
for i := byte(0); i < 255; i++ {
@@ -215,7 +215,7 @@ var testdata2 = []kvs{
}
func TestIteratorSeek(t *testing.T) {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
for _, val := range testdata1 {
trie.MustUpdate([]byte(val.k), []byte(val.v))
}
@@ -256,22 +256,22 @@ func checkIteratorOrder(want []kvs, it *Iterator) error {
}
func TestDifferenceIterator(t *testing.T) {
- dba := NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ dba := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
triea := NewEmpty(dba)
for _, val := range testdata1 {
triea.MustUpdate([]byte(val.k), []byte(val.v))
}
rootA, nodesA, _ := triea.Commit(false)
- dba.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodesA), nil)
+ dba.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodesA))
triea, _ = New(TrieID(rootA), dba)
- dbb := NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ dbb := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
trieb := NewEmpty(dbb)
for _, val := range testdata2 {
trieb.MustUpdate([]byte(val.k), []byte(val.v))
}
rootB, nodesB, _ := trieb.Commit(false)
- dbb.Update(rootB, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodesB), nil)
+ dbb.Update(rootB, types.EmptyRootHash, trienode.NewWithNodeSet(nodesB))
trieb, _ = New(TrieID(rootB), dbb)
found := make(map[string]string)
@@ -298,22 +298,22 @@ func TestDifferenceIterator(t *testing.T) {
}
func TestUnionIterator(t *testing.T) {
- dba := NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ dba := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
triea := NewEmpty(dba)
for _, val := range testdata1 {
triea.MustUpdate([]byte(val.k), []byte(val.v))
}
rootA, nodesA, _ := triea.Commit(false)
- dba.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodesA), nil)
+ dba.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodesA))
triea, _ = New(TrieID(rootA), dba)
- dbb := NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ dbb := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
trieb := NewEmpty(dbb)
for _, val := range testdata2 {
trieb.MustUpdate([]byte(val.k), []byte(val.v))
}
rootB, nodesB, _ := trieb.Commit(false)
- dbb.Update(rootB, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodesB), nil)
+ dbb.Update(rootB, types.EmptyRootHash, trienode.NewWithNodeSet(nodesB))
trieb, _ = New(TrieID(rootB), dbb)
di, _ := NewUnionIterator([]NodeIterator{triea.MustNodeIterator(nil), trieb.MustNodeIterator(nil)})
@@ -351,7 +351,8 @@ func TestUnionIterator(t *testing.T) {
}
func TestIteratorNoDups(t *testing.T) {
- tr := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
+ tr := NewEmpty(db)
for _, val := range testdata1 {
tr.MustUpdate([]byte(val.k), []byte(val.v))
}
@@ -375,9 +376,9 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool, scheme string) {
tr.MustUpdate([]byte(val.k), []byte(val.v))
}
root, nodes, _ := tr.Commit(false)
- tdb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
+ tdb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
if !memonly {
- tdb.Commit(root, false)
+ tdb.Commit(root)
}
tr, _ = New(TrieID(root), tdb)
wantNodeCount := checkIteratorNoDups(t, tr.MustNodeIterator(nil), nil)
@@ -491,9 +492,9 @@ func testIteratorContinueAfterSeekError(t *testing.T, memonly bool, scheme strin
break
}
}
- triedb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
+ triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
if !memonly {
- triedb.Commit(root, false)
+ triedb.Commit(root)
}
var (
barNodeBlob []byte
@@ -565,8 +566,8 @@ func testIteratorNodeBlob(t *testing.T, scheme string) {
trie.MustUpdate([]byte(val.k), []byte(val.v))
}
root, nodes, _ := trie.Commit(false)
- triedb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
- triedb.Commit(root, false)
+ triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
+ triedb.Commit(root)
var found = make(map[common.Hash][]byte)
trie, _ = New(TrieID(root), triedb)
diff --git a/trie/proof.go b/trie/proof.go
index df78f1926e..4bedbf0bc6 100644
--- a/trie/proof.go
+++ b/trie/proof.go
@@ -399,7 +399,7 @@ func unset(parent node, child node, key []byte, pos int, removeLeft bool) error
} else {
if bytes.Compare(cld.Key, key[pos:]) > 0 {
// The key of fork shortnode is greater than the
- // path(it belongs to the range), unset the entrie
+ // path(it belongs to the range), unset the entries
// branch. The parent must be a fullnode.
fn := parent.(*fullNode)
fn.Children[key[pos-1]] = nil
diff --git a/trie/proof_test.go b/trie/proof_test.go
index 3191174667..91a5c64ac0 100644
--- a/trie/proof_test.go
+++ b/trie/proof_test.go
@@ -104,7 +104,7 @@ func TestProof(t *testing.T) {
}
func TestOneElementProof(t *testing.T) {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
updateString(trie, "k", "v")
for i, prover := range makeProvers(trie) {
proof := prover([]byte("k"))
@@ -155,7 +155,7 @@ func TestBadProof(t *testing.T) {
// Tests that missing keys can also be proven. The test explicitly uses a single
// entry trie and checks for missing keys both before and after the single entry.
func TestMissingKeyProof(t *testing.T) {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
updateString(trie, "k", "v")
for i, key := range []string{"a", "j", "l", "z"} {
@@ -353,7 +353,7 @@ func TestOneElementRangeProof(t *testing.T) {
}
// Test the mini trie with only a single element.
- tinyTrie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ tinyTrie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
entry := &kv{randBytes(32), randBytes(20), false}
tinyTrie.MustUpdate(entry.k, entry.v)
@@ -424,7 +424,7 @@ func TestAllElementsProof(t *testing.T) {
// TestSingleSideRangeProof tests the range starts from zero.
func TestSingleSideRangeProof(t *testing.T) {
for i := 0; i < 64; i++ {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
var entries []*kv
for i := 0; i < 4096; i++ {
value := &kv{randBytes(32), randBytes(20), false}
@@ -530,7 +530,7 @@ func TestBadRangeProof(t *testing.T) {
// TestGappedRangeProof focuses on the small trie with embedded nodes.
// If the gapped node is embedded in the trie, it should be detected too.
func TestGappedRangeProof(t *testing.T) {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
var entries []*kv // Sorted entries
for i := byte(0); i < 10; i++ {
value := &kv{common.LeftPadBytes([]byte{i}, 32), []byte{i}, false}
@@ -602,7 +602,7 @@ func TestSameSideProofs(t *testing.T) {
}
func TestHasRightElement(t *testing.T) {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
var entries []*kv
for i := 0; i < 4096; i++ {
value := &kv{randBytes(32), randBytes(20), false}
@@ -944,7 +944,7 @@ func benchmarkVerifyRangeNoProof(b *testing.B, size int) {
}
func randomTrie(n int) (*Trie, map[string]*kv) {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
vals := make(map[string]*kv)
for i := byte(0); i < 100; i++ {
value := &kv{common.LeftPadBytes([]byte{i}, 32), []byte{i}, false}
@@ -963,7 +963,7 @@ func randomTrie(n int) (*Trie, map[string]*kv) {
}
func nonRandomTrie(n int) (*Trie, map[string]*kv) {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
vals := make(map[string]*kv)
max := uint64(0xffffffffffffffff)
for i := uint64(0); i < uint64(n); i++ {
@@ -988,7 +988,7 @@ func TestRangeProofKeysWithSharedPrefix(t *testing.T) {
common.Hex2Bytes("02"),
common.Hex2Bytes("03"),
}
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
for i, key := range keys {
trie.MustUpdate(key, vals[i])
}
diff --git a/trie/secure_trie.go b/trie/secure_trie.go
index ef29bb8404..bcf983e0ee 100644
--- a/trie/secure_trie.go
+++ b/trie/secure_trie.go
@@ -29,6 +29,7 @@ package trie
import (
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/trie/trienode"
+ "github.com/ava-labs/subnet-evm/triedb/database"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -39,7 +40,7 @@ type SecureTrie = StateTrie
// NewSecure creates a new StateTrie.
// Deprecated: use NewStateTrie.
-func NewSecure(stateRoot common.Hash, owner common.Hash, root common.Hash, db *Database) (*SecureTrie, error) {
+func NewSecure(stateRoot common.Hash, owner common.Hash, root common.Hash, db database.Database) (*SecureTrie, error) {
id := &ID{
StateRoot: stateRoot,
Owner: owner,
@@ -60,7 +61,7 @@ func NewSecure(stateRoot common.Hash, owner common.Hash, root common.Hash, db *D
// StateTrie is not safe for concurrent use.
type StateTrie struct {
trie Trie
- preimages *preimageStore
+ db database.Database
hashKeyBuf [common.HashLength]byte
secKeyCache map[string][]byte
secKeyCacheOwner *StateTrie // Pointer to self, replace the key cache on mismatch
@@ -71,7 +72,7 @@ type StateTrie struct {
// If root is the zero hash or the sha3 hash of an empty string, the
// trie is initially empty. Otherwise, New will panic if db is nil
// and returns MissingNodeError if the root node cannot be found.
-func NewStateTrie(id *ID, db *Database) (*StateTrie, error) {
+func NewStateTrie(id *ID, db database.Database) (*StateTrie, error) {
if db == nil {
panic("trie.NewStateTrie called without a database")
}
@@ -79,7 +80,7 @@ func NewStateTrie(id *ID, db *Database) (*StateTrie, error) {
if err != nil {
return nil, err
}
- return &StateTrie{trie: *trie, preimages: db.preimages}, nil
+ return &StateTrie{trie: *trie, db: db}, nil
}
// MustGet returns the value for key stored in the trie.
@@ -220,10 +221,7 @@ func (t *StateTrie) GetKey(shaKey []byte) []byte {
if key, ok := t.getSecKeyCache()[string(shaKey)]; ok {
return key
}
- if t.preimages == nil {
- return nil
- }
- return t.preimages.preimage(common.BytesToHash(shaKey))
+ return t.db.Preimage(common.BytesToHash(shaKey))
}
// Commit collects all dirty nodes in the trie and replaces them with the
@@ -236,13 +234,11 @@ func (t *StateTrie) GetKey(shaKey []byte) []byte {
func (t *StateTrie) Commit(collectLeaf bool) (common.Hash, *trienode.NodeSet, error) {
// Write all the pre-images to the actual disk database
if len(t.getSecKeyCache()) > 0 {
- if t.preimages != nil {
- preimages := make(map[common.Hash][]byte)
- for hk, key := range t.secKeyCache {
- preimages[common.BytesToHash([]byte(hk))] = key
- }
- t.preimages.insertPreimage(preimages)
+ preimages := make(map[common.Hash][]byte)
+ for hk, key := range t.secKeyCache {
+ preimages[common.BytesToHash([]byte(hk))] = key
}
+ t.db.InsertPreimage(preimages)
t.secKeyCache = make(map[string][]byte)
}
// Commit the trie and return its modified nodeset.
@@ -259,7 +255,7 @@ func (t *StateTrie) Hash() common.Hash {
func (t *StateTrie) Copy() *StateTrie {
return &StateTrie{
trie: *t.trie.Copy(),
- preimages: t.preimages,
+ db: t.db,
secKeyCache: t.secKeyCache,
}
}
diff --git a/trie/secure_trie_test.go b/trie/secure_trie_test.go
index fab78d19bd..137ecf1074 100644
--- a/trie/secure_trie_test.go
+++ b/trie/secure_trie_test.go
@@ -41,14 +41,14 @@ import (
)
func newEmptySecure() *StateTrie {
- trie, _ := NewStateTrie(TrieID(types.EmptyRootHash), NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie, _ := NewStateTrie(TrieID(types.EmptyRootHash), newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
return trie
}
// makeTestStateTrie creates a large enough secure trie for testing.
-func makeTestStateTrie() (*Database, *StateTrie, map[string][]byte) {
+func makeTestStateTrie() (*testDb, *StateTrie, map[string][]byte) {
// Create an empty trie
- triedb := NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ triedb := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
trie, _ := NewStateTrie(TrieID(types.EmptyRootHash), triedb)
// Fill it with some arbitrary data
@@ -71,7 +71,7 @@ func makeTestStateTrie() (*Database, *StateTrie, map[string][]byte) {
}
}
root, nodes, _ := trie.Commit(false)
- if err := triedb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil); err != nil {
+ if err := triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)); err != nil {
panic(fmt.Errorf("failed to commit db %v", err))
}
// Re-create the trie based on the new state
diff --git a/trie/stacktrie_fuzzer_test.go b/trie/stacktrie_fuzzer_test.go
index e9014df0d9..379e18026d 100644
--- a/trie/stacktrie_fuzzer_test.go
+++ b/trie/stacktrie_fuzzer_test.go
@@ -42,10 +42,10 @@ func fuzz(data []byte, debugging bool) {
var (
input = bytes.NewReader(data)
spongeA = &spongeDb{sponge: sha3.NewLegacyKeccak256()}
- dbA = NewDatabase(rawdb.NewDatabase(spongeA), nil)
+ dbA = newTestDatabase(rawdb.NewDatabase(spongeA), rawdb.HashScheme)
trieA = NewEmpty(dbA)
spongeB = &spongeDb{sponge: sha3.NewLegacyKeccak256()}
- dbB = NewDatabase(rawdb.NewDatabase(spongeB), nil)
+ dbB = newTestDatabase(rawdb.NewDatabase(spongeB), rawdb.HashScheme)
options = NewStackTrieOptions().WithWriter(func(path []byte, hash common.Hash, blob []byte) {
rawdb.WriteTrieNode(spongeB, common.Hash{}, path, hash, blob, dbB.Scheme())
@@ -87,10 +87,10 @@ func fuzz(data []byte, debugging bool) {
panic(err)
}
if nodes != nil {
- dbA.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
+ dbA.Update(rootA, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
}
// Flush memdb -> disk (sponge)
- dbA.Commit(rootA, false)
+ dbA.Commit(rootA)
// Stacktrie requires sorted insertion
slices.SortFunc(vals, (*kv).cmp)
diff --git a/trie/stacktrie_test.go b/trie/stacktrie_test.go
index 3d7bbc79f9..f9db27ef0e 100644
--- a/trie/stacktrie_test.go
+++ b/trie/stacktrie_test.go
@@ -233,7 +233,7 @@ func TestStackTrieInsertAndHash(t *testing.T) {
func TestSizeBug(t *testing.T) {
st := NewStackTrie(nil)
- nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ nt := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3")
@@ -248,7 +248,7 @@ func TestSizeBug(t *testing.T) {
func TestEmptyBug(t *testing.T) {
st := NewStackTrie(nil)
- nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ nt := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
//leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
//value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3")
@@ -274,7 +274,7 @@ func TestEmptyBug(t *testing.T) {
func TestValLength56(t *testing.T) {
st := NewStackTrie(nil)
- nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ nt := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
//leaf := common.FromHex("290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563")
//value := common.FromHex("94cf40d0d2b44f2b66e07cace1372ca42b73cf21a3")
@@ -299,7 +299,7 @@ func TestValLength56(t *testing.T) {
// which causes a lot of node-within-node. This case was found via fuzzing.
func TestUpdateSmallNodes(t *testing.T) {
st := NewStackTrie(nil)
- nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ nt := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
kvs := []struct {
K string
V string
@@ -327,7 +327,7 @@ func TestUpdateSmallNodes(t *testing.T) {
func TestUpdateVariableKeys(t *testing.T) {
t.SkipNow()
st := NewStackTrie(nil)
- nt := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ nt := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
kvs := []struct {
K string
V string
diff --git a/trie/sync_test.go b/trie/sync_test.go
index e899ba5458..e0838a2059 100644
--- a/trie/sync_test.go
+++ b/trie/sync_test.go
@@ -37,7 +37,7 @@ import (
)
// makeTestTrie create a sample test trie to test node-wise reconstruction.
-func makeTestTrie(scheme string) (ethdb.Database, *Database, *StateTrie, map[string][]byte) {
+func makeTestTrie(scheme string) (ethdb.Database, *testDb, *StateTrie, map[string][]byte) {
// Create an empty trie
db := rawdb.NewMemoryDatabase()
triedb := newTestDatabase(db, scheme)
@@ -63,10 +63,10 @@ func makeTestTrie(scheme string) (ethdb.Database, *Database, *StateTrie, map[str
}
}
root, nodes, _ := trie.Commit(false)
- if err := triedb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil); err != nil {
+ if err := triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes)); err != nil {
panic(fmt.Errorf("failed to commit db %v", err))
}
- if err := triedb.Commit(root, false); err != nil {
+ if err := triedb.Commit(root); err != nil {
panic(err)
}
// Re-create the trie based on the new state
diff --git a/trie/tracer_test.go b/trie/tracer_test.go
index f3406a0250..b3735dd1ff 100644
--- a/trie/tracer_test.go
+++ b/trie/tracer_test.go
@@ -61,7 +61,7 @@ func TestTrieTracer(t *testing.T) {
// Tests if the trie diffs are tracked correctly. Tracer should capture
// all non-leaf dirty nodes, no matter the node is embedded or not.
func testTrieTracer(t *testing.T, vals []struct{ k, v string }) {
- db := NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
trie := NewEmpty(db)
// Determine all new nodes are tracked
@@ -71,7 +71,7 @@ func testTrieTracer(t *testing.T, vals []struct{ k, v string }) {
insertSet := copySet(trie.tracer.inserts) // copy before commit
deleteSet := copySet(trie.tracer.deletes) // copy before commit
root, nodes, _ := trie.Commit(false)
- db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
+ db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
seen := setKeys(iterNodes(db, root))
if !compareSet(insertSet, seen) {
@@ -104,7 +104,8 @@ func TestTrieTracerNoop(t *testing.T) {
}
func testTrieTracerNoop(t *testing.T, vals []struct{ k, v string }) {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
+ trie := NewEmpty(db)
for _, val := range vals {
trie.MustUpdate([]byte(val.k), []byte(val.v))
}
@@ -128,7 +129,7 @@ func TestAccessList(t *testing.T) {
func testAccessList(t *testing.T, vals []struct{ k, v string }) {
var (
- db = NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ db = newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
trie = NewEmpty(db)
orig = trie.Copy()
)
@@ -137,7 +138,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) {
trie.MustUpdate([]byte(val.k), []byte(val.v))
}
root, nodes, _ := trie.Commit(false)
- db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
+ db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
trie, _ = New(TrieID(root), db)
if err := verifyAccessList(orig, trie, nodes); err != nil {
@@ -152,7 +153,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) {
trie.MustUpdate([]byte(val.k), randBytes(32))
}
root, nodes, _ = trie.Commit(false)
- db.Update(root, parent, 0, trienode.NewWithNodeSet(nodes), nil)
+ db.Update(root, parent, trienode.NewWithNodeSet(nodes))
trie, _ = New(TrieID(root), db)
if err := verifyAccessList(orig, trie, nodes); err != nil {
@@ -170,7 +171,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) {
trie.MustUpdate(key, randBytes(32))
}
root, nodes, _ = trie.Commit(false)
- db.Update(root, parent, 0, trienode.NewWithNodeSet(nodes), nil)
+ db.Update(root, parent, trienode.NewWithNodeSet(nodes))
trie, _ = New(TrieID(root), db)
if err := verifyAccessList(orig, trie, nodes); err != nil {
@@ -185,7 +186,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) {
trie.MustUpdate([]byte(key), nil)
}
root, nodes, _ = trie.Commit(false)
- db.Update(root, parent, 0, trienode.NewWithNodeSet(nodes), nil)
+ db.Update(root, parent, trienode.NewWithNodeSet(nodes))
trie, _ = New(TrieID(root), db)
if err := verifyAccessList(orig, trie, nodes); err != nil {
@@ -200,7 +201,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) {
trie.MustUpdate([]byte(val.k), nil)
}
root, nodes, _ = trie.Commit(false)
- db.Update(root, parent, 0, trienode.NewWithNodeSet(nodes), nil)
+ db.Update(root, parent, trienode.NewWithNodeSet(nodes))
trie, _ = New(TrieID(root), db)
if err := verifyAccessList(orig, trie, nodes); err != nil {
@@ -211,7 +212,7 @@ func testAccessList(t *testing.T, vals []struct{ k, v string }) {
// Tests origin values won't be tracked in Iterator or Prover
func TestAccessListLeak(t *testing.T) {
var (
- db = NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ db = newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
trie = NewEmpty(db)
)
// Create trie from scratch
@@ -219,7 +220,7 @@ func TestAccessListLeak(t *testing.T) {
trie.MustUpdate([]byte(val.k), []byte(val.v))
}
root, nodes, _ := trie.Commit(false)
- db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
+ db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
var cases = []struct {
op func(tr *Trie)
@@ -262,14 +263,14 @@ func TestAccessListLeak(t *testing.T) {
// in its parent due to the smaller size of the original tree node.
func TestTinyTree(t *testing.T) {
var (
- db = NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ db = newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
trie = NewEmpty(db)
)
for _, val := range tiny {
trie.MustUpdate([]byte(val.k), randBytes(32))
}
root, set, _ := trie.Commit(false)
- db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(set), nil)
+ db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(set))
parent := root
trie, _ = New(TrieID(root), db)
@@ -278,7 +279,7 @@ func TestTinyTree(t *testing.T) {
trie.MustUpdate([]byte(val.k), []byte(val.v))
}
root, set, _ = trie.Commit(false)
- db.Update(root, parent, 0, trienode.NewWithNodeSet(set), nil)
+ db.Update(root, parent, trienode.NewWithNodeSet(set))
trie, _ = New(TrieID(root), db)
if err := verifyAccessList(orig, trie, set); err != nil {
@@ -312,7 +313,7 @@ func forNodes(tr *Trie) map[string][]byte {
return nodes
}
-func iterNodes(db *Database, root common.Hash) map[string][]byte {
+func iterNodes(db *testDb, root common.Hash) map[string][]byte {
tr, _ := New(TrieID(root), db)
return forNodes(tr)
}
diff --git a/trie/trie.go b/trie/trie.go
index 168f2b9730..4370f22f20 100644
--- a/trie/trie.go
+++ b/trie/trie.go
@@ -34,6 +34,7 @@ import (
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/trie/trienode"
+ "github.com/ava-labs/subnet-evm/triedb/database"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
)
@@ -89,7 +90,7 @@ func (t *Trie) Copy() *Trie {
// zero hash or the sha3 hash of an empty string, then trie is initially
// empty, otherwise, the root node must be present in database or returns
// a MissingNodeError if not.
-func New(id *ID, db *Database) (*Trie, error) {
+func New(id *ID, db database.Database) (*Trie, error) {
reader, err := newTrieReader(id.StateRoot, id.Owner, db)
if err != nil {
return nil, err
@@ -110,7 +111,7 @@ func New(id *ID, db *Database) (*Trie, error) {
}
// NewEmpty is a shortcut to create empty tree. It's mostly used in tests.
-func NewEmpty(db *Database) *Trie {
+func NewEmpty(db database.Database) *Trie {
tr, _ := New(TrieID(types.EmptyRootHash), db)
return tr
}
diff --git a/trie/trie_reader.go b/trie/trie_reader.go
index b43f8cec29..74c5c2a25c 100644
--- a/trie/trie_reader.go
+++ b/trie/trie_reader.go
@@ -29,33 +29,21 @@ package trie
import (
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/trie/triestate"
+ "github.com/ava-labs/subnet-evm/triedb/database"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
)
-// Reader wraps the Node method of a backing trie store.
-type Reader interface {
- // Node retrieves the trie node blob with the provided trie identifier, node path and
- // the corresponding node hash. No error will be returned if the node is not found.
- //
- // When looking up nodes in the account trie, 'owner' is the zero hash. For contract
- // storage trie nodes, 'owner' is the hash of the account address that containing the
- // storage.
- //
- // TODO(rjl493456442): remove the 'hash' parameter, it's redundant in PBSS.
- Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error)
-}
-
// trieReader is a wrapper of the underlying node reader. It's not safe
// for concurrent usage.
type trieReader struct {
owner common.Hash
- reader Reader
+ reader database.Reader
banned map[string]struct{} // Marker to prevent node from being accessed, for tests
}
// newTrieReader initializes the trie reader with the given node reader.
-func newTrieReader(stateRoot, owner common.Hash, db *Database) (*trieReader, error) {
+func newTrieReader(stateRoot, owner common.Hash, db database.Database) (*trieReader, error) {
if stateRoot == (common.Hash{}) || stateRoot == types.EmptyRootHash {
if stateRoot == (common.Hash{}) {
log.Error("Zero state root hash!")
@@ -95,17 +83,22 @@ func (r *trieReader) node(path []byte, hash common.Hash) ([]byte, error) {
return blob, nil
}
-// trieLoader implements triestate.TrieLoader for constructing tries.
-type trieLoader struct {
- db *Database
+// MerkleLoader implements triestate.TrieLoader for constructing tries.
+type MerkleLoader struct {
+ db database.Database
+}
+
+// NewMerkleLoader creates the merkle trie loader.
+func NewMerkleLoader(db database.Database) *MerkleLoader {
+ return &MerkleLoader{db: db}
}
// OpenTrie opens the main account trie.
-func (l *trieLoader) OpenTrie(root common.Hash) (triestate.Trie, error) {
+func (l *MerkleLoader) OpenTrie(root common.Hash) (triestate.Trie, error) {
return New(TrieID(root), l.db)
}
// OpenStorageTrie opens the storage trie of an account.
-func (l *trieLoader) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (triestate.Trie, error) {
+func (l *MerkleLoader) OpenStorageTrie(stateRoot common.Hash, addrHash, root common.Hash) (triestate.Trie, error) {
return New(StorageTrieID(stateRoot, addrHash, root), l.db)
}
diff --git a/trie/trie_test.go b/trie/trie_test.go
index c714e41de3..edcf805525 100644
--- a/trie/trie_test.go
+++ b/trie/trie_test.go
@@ -33,9 +33,9 @@ import (
"fmt"
"hash"
"io"
- "math/big"
"math/rand"
"reflect"
+ "sort"
"testing"
"testing/quick"
@@ -47,6 +47,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/holiman/uint256"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/sha3"
)
@@ -57,7 +58,7 @@ func init() {
}
func TestEmptyTrie(t *testing.T) {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
res := trie.Hash()
exp := types.EmptyRootHash
if res != exp {
@@ -66,7 +67,7 @@ func TestEmptyTrie(t *testing.T) {
}
func TestNull(t *testing.T) {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
key := make([]byte, 32)
value := []byte("test")
trie.MustUpdate(key, value)
@@ -106,10 +107,10 @@ func testMissingNode(t *testing.T, memonly bool, scheme string) {
updateString(trie, "120000", "qwerqwerqwerqwerqwerqwerqwerqwer")
updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf")
root, nodes, _ := trie.Commit(false)
- triedb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
+ triedb.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
if !memonly {
- require.NoError(t, triedb.Commit(root, false))
+ require.NoError(t, triedb.Commit(root))
}
trie, _ = New(TrieID(root), triedb)
@@ -178,7 +179,7 @@ func testMissingNode(t *testing.T, memonly bool, scheme string) {
}
func TestInsert(t *testing.T) {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
updateString(trie, "doe", "reindeer")
updateString(trie, "dog", "puppy")
@@ -190,7 +191,7 @@ func TestInsert(t *testing.T) {
t.Errorf("case 1: exp %x got %x", exp, root)
}
- trie = NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie = NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
updateString(trie, "A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
exp = common.HexToHash("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab")
@@ -201,7 +202,7 @@ func TestInsert(t *testing.T) {
}
func TestGet(t *testing.T) {
- db := NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
trie := NewEmpty(db)
updateString(trie, "doe", "reindeer")
updateString(trie, "dog", "puppy")
@@ -220,13 +221,14 @@ func TestGet(t *testing.T) {
return
}
root, nodes, _ := trie.Commit(false)
- db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
+ db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
trie, _ = New(TrieID(root), db)
}
}
func TestDelete(t *testing.T) {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
+ trie := NewEmpty(db)
vals := []struct{ k, v string }{
{"do", "verb"},
{"ether", "wookiedoo"},
@@ -253,7 +255,7 @@ func TestDelete(t *testing.T) {
}
func TestEmptyValues(t *testing.T) {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
vals := []struct{ k, v string }{
{"do", "verb"},
@@ -277,7 +279,7 @@ func TestEmptyValues(t *testing.T) {
}
func TestReplication(t *testing.T) {
- db := NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
trie := NewEmpty(db)
vals := []struct{ k, v string }{
{"do", "verb"},
@@ -292,7 +294,7 @@ func TestReplication(t *testing.T) {
updateString(trie, val.k, val.v)
}
root, nodes, _ := trie.Commit(false)
- db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
+ db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
// create a new trie on top of the database and check that lookups work.
trie2, err := New(TrieID(root), db)
@@ -311,7 +313,7 @@ func TestReplication(t *testing.T) {
// recreate the trie after commit
if nodes != nil {
- db.Update(hash, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
+ db.Update(hash, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
}
trie2, err = New(TrieID(hash), db)
if err != nil {
@@ -338,13 +340,13 @@ func TestReplication(t *testing.T) {
}
func TestLargeValue(t *testing.T) {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
trie.MustUpdate([]byte("key1"), []byte{99, 99, 99, 99})
trie.MustUpdate([]byte("key2"), bytes.Repeat([]byte{1}, 32))
trie.Hash()
}
-// TestRandomCases tests som cases that were found via random fuzzing
+// TestRandomCases tests some cases that were found via random fuzzing
func TestRandomCases(t *testing.T) {
var rt = []randTestStep{
{op: 6, key: common.Hex2Bytes(""), value: common.Hex2Bytes("")}, // step 0
@@ -541,7 +543,7 @@ func runRandTest(rt randTest) error {
case opCommit:
root, nodes, _ := tr.Commit(true)
if nodes != nil {
- triedb.Update(root, origin, 0, trienode.NewWithNodeSet(nodes), nil)
+ triedb.Update(root, origin, trienode.NewWithNodeSet(nodes))
}
newtr, err := New(TrieID(root), triedb)
if err != nil {
@@ -642,7 +644,7 @@ func BenchmarkUpdateLE(b *testing.B) { benchUpdate(b, binary.LittleEndian) }
const benchElemCount = 20000
func benchGet(b *testing.B) {
- triedb := NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ triedb := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)
trie := NewEmpty(triedb)
k := make([]byte, 32)
for i := 0; i < benchElemCount; i++ {
@@ -661,7 +663,7 @@ func benchGet(b *testing.B) {
}
func benchUpdate(b *testing.B, e binary.ByteOrder) *Trie {
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
k := make([]byte, 32)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
@@ -693,7 +695,7 @@ func BenchmarkHash(b *testing.B) {
// entries, then adding N more.
addresses, accounts := makeAccounts(2 * b.N)
// Insert the accounts into the trie and hash it
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
i := 0
for ; i < len(addresses)/2; i++ {
trie.MustUpdate(crypto.Keccak256(addresses[i][:]), accounts[i])
@@ -708,13 +710,6 @@ func BenchmarkHash(b *testing.B) {
trie.Hash()
}
-type account struct {
- Nonce uint64
- Balance *big.Int
- Root common.Hash
- CodeHash []byte
-}
-
// Benchmarks the trie Commit following a Hash. Since the trie caches the result of any operation,
// we cannot use b.N as the number of hashing rounds, since all rounds apart from
// the first one will be NOOP. As such, we'll use b.N as the number of account to
@@ -731,7 +726,7 @@ func BenchmarkCommitAfterHash(b *testing.B) {
func benchmarkCommitAfterHash(b *testing.B, collectLeaf bool) {
// Make the random benchmark deterministic
addresses, accounts := makeAccounts(b.N)
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
for i := 0; i < len(addresses); i++ {
trie.MustUpdate(crypto.Keccak256(addresses[i][:]), accounts[i])
}
@@ -745,7 +740,7 @@ func benchmarkCommitAfterHash(b *testing.B, collectLeaf bool) {
func TestTinyTrie(t *testing.T) {
// Create a realistic account trie to hash
_, accounts := makeAccounts(5)
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
trie.MustUpdate(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001337"), accounts[3])
if exp, root := common.HexToHash("8c6a85a4d9fda98feff88450299e574e5378e32391f75a055d470ac0653f1005"), trie.Hash(); exp != root {
t.Errorf("1: got %x, exp %x", root, exp)
@@ -758,7 +753,7 @@ func TestTinyTrie(t *testing.T) {
if exp, root := common.HexToHash("0608c1d1dc3905fa22204c7a0e43644831c3b6d3def0f274be623a948197e64a"), trie.Hash(); exp != root {
t.Errorf("3: got %x, exp %x", root, exp)
}
- checktr := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ checktr := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
it := NewIterator(trie.MustNodeIterator(nil))
for it.Next() {
checktr.MustUpdate(it.Key, it.Value)
@@ -771,7 +766,7 @@ func TestTinyTrie(t *testing.T) {
func TestCommitAfterHash(t *testing.T) {
// Create a realistic account trie to hash
addresses, accounts := makeAccounts(1000)
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
for i := 0; i < len(addresses); i++ {
trie.MustUpdate(crypto.Keccak256(addresses[i][:]), accounts[i])
}
@@ -813,8 +808,8 @@ func makeAccounts(size int) (addresses [][20]byte, accounts [][]byte) {
numBytes := random.Uint32() % 33 // [0, 32] bytes
balanceBytes := make([]byte, numBytes)
random.Read(balanceBytes)
- balance := new(big.Int).SetBytes(balanceBytes)
- data, _ := rlp.EncodeToBytes(&account{Nonce: nonce, Balance: balance, Root: root, CodeHash: code})
+ balance := new(uint256.Int).SetBytes(balanceBytes)
+ data, _ := rlp.EncodeToBytes(&types.StateAccount{Nonce: nonce, Balance: balance, Root: root, CodeHash: code})
accounts[i] = data
}
return addresses, accounts
@@ -825,6 +820,8 @@ type spongeDb struct {
sponge hash.Hash
id string
journal []string
+ keys []string
+ values map[string]string
}
func (s *spongeDb) Has(key []byte) (bool, error) { panic("implement me") }
@@ -848,12 +845,27 @@ func (s *spongeDb) Put(key []byte, value []byte) error {
valbrief = valbrief[:8]
}
s.journal = append(s.journal, fmt.Sprintf("%v: PUT([%x...], [%d bytes] %x...)\n", s.id, keybrief, len(value), valbrief))
- s.sponge.Write(key)
- s.sponge.Write(value)
+
+ if s.values == nil {
+ s.sponge.Write(key)
+ s.sponge.Write(value)
+ } else {
+ s.keys = append(s.keys, string(key))
+ s.values[string(key)] = string(value)
+ }
return nil
}
func (s *spongeDb) NewIterator(prefix []byte, start []byte) ethdb.Iterator { panic("implement me") }
+func (s *spongeDb) Flush() {
+ // Bottom-up, the longest path first
+ sort.Sort(sort.Reverse(sort.StringSlice(s.keys)))
+ for _, key := range s.keys {
+ s.sponge.Write([]byte(key))
+ s.sponge.Write([]byte(s.values[key]))
+ }
+}
+
// spongeBatch is a dummy batch which immediately writes to the underlying spongedb
type spongeBatch struct {
db *spongeDb
@@ -878,14 +890,14 @@ func TestCommitSequence(t *testing.T) {
count int
expWriteSeqHash []byte
}{
- {20, common.FromHex("873c78df73d60e59d4a2bcf3716e8bfe14554549fea2fc147cb54129382a8066")},
- {200, common.FromHex("ba03d891bb15408c940eea5ee3d54d419595102648d02774a0268d892add9c8e")},
- {2000, common.FromHex("f7a184f20df01c94f09537401d11e68d97ad0c00115233107f51b9c287ce60c7")},
+ {20, common.FromHex("330b0afae2853d96b9f015791fbe0fb7f239bf65f335f16dfc04b76c7536276d")},
+ {200, common.FromHex("5162b3735c06b5d606b043a3ee8adbdbbb408543f4966bca9dcc63da82684eeb")},
+ {2000, common.FromHex("4574cd8e6b17f3fe8ad89140d1d0bf4f1bd7a87a8ac3fb623b33550544c77635")},
} {
addresses, accounts := makeAccounts(tc.count)
// This spongeDb is used to check the sequence of disk-db-writes
s := &spongeDb{sponge: sha3.NewLegacyKeccak256()}
- db := NewDatabase(rawdb.NewDatabase(s), nil)
+ db := newTestDatabase(rawdb.NewDatabase(s), rawdb.HashScheme)
trie := NewEmpty(db)
// Fill the trie with elements
for i := 0; i < tc.count; i++ {
@@ -893,9 +905,9 @@ func TestCommitSequence(t *testing.T) {
}
// Flush trie -> database
root, nodes, _ := trie.Commit(false)
- db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
+ db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
// Flush memdb -> disk (sponge)
- db.Commit(root, false)
+ db.Commit(root)
if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) {
t.Errorf("test %d, disk write sequence wrong:\ngot %x exp %x\n", i, got, exp)
}
@@ -909,14 +921,14 @@ func TestCommitSequenceRandomBlobs(t *testing.T) {
count int
expWriteSeqHash []byte
}{
- {20, common.FromHex("8e4a01548551d139fa9e833ebc4e66fc1ba40a4b9b7259d80db32cff7b64ebbc")},
- {200, common.FromHex("6869b4e7b95f3097a19ddb30ff735f922b915314047e041614df06958fc50554")},
- {2000, common.FromHex("444200e6f4e2df49f77752f629a96ccf7445d4698c164f962bbd85a0526ef424")},
+ {20, common.FromHex("8016650c7a50cf88485fd06cde52d634a89711051107f00d21fae98234f2f13d")},
+ {200, common.FromHex("dde92ca9812e068e6982d04b40846dc65a61a9fd4996fc0f55f2fde172a8e13c")},
+ {2000, common.FromHex("ab553a7f9aff82e3929c382908e30ef7dd17a332933e92ba3fe873fc661ef382")},
} {
prng := rand.New(rand.NewSource(int64(i)))
// This spongeDb is used to check the sequence of disk-db-writes
s := &spongeDb{sponge: sha3.NewLegacyKeccak256()}
- db := NewDatabase(rawdb.NewDatabase(s), nil)
+ db := newTestDatabase(rawdb.NewDatabase(s), rawdb.HashScheme)
trie := NewEmpty(db)
// Fill the trie with elements
for i := 0; i < tc.count; i++ {
@@ -934,9 +946,9 @@ func TestCommitSequenceRandomBlobs(t *testing.T) {
}
// Flush trie -> database
root, nodes, _ := trie.Commit(false)
- db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
+ db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
// Flush memdb -> disk (sponge)
- db.Commit(root, false)
+ db.Commit(root)
if got, exp := s.sponge.Sum(nil), tc.expWriteSeqHash; !bytes.Equal(got, exp) {
t.Fatalf("test %d, disk write sequence wrong:\ngot %x exp %x\n", i, got, exp)
}
@@ -947,17 +959,26 @@ func TestCommitSequenceStackTrie(t *testing.T) {
for count := 1; count < 200; count++ {
prng := rand.New(rand.NewSource(int64(count)))
// This spongeDb is used to check the sequence of disk-db-writes
- s := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "a"}
- db := NewDatabase(rawdb.NewDatabase(s), nil)
+ s := &spongeDb{
+ sponge: sha3.NewLegacyKeccak256(),
+ id: "a",
+ values: make(map[string]string),
+ }
+ db := newTestDatabase(rawdb.NewDatabase(s), rawdb.HashScheme)
trie := NewEmpty(db)
- // Another sponge is used for the stacktrie commits
- stackTrieSponge := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "b"}
+ // Another sponge is used for the stacktrie commits
+ stackTrieSponge := &spongeDb{
+ sponge: sha3.NewLegacyKeccak256(),
+ id: "b",
+ values: make(map[string]string),
+ }
options := NewStackTrieOptions()
options = options.WithWriter(func(path []byte, hash common.Hash, blob []byte) {
rawdb.WriteTrieNode(stackTrieSponge, common.Hash{}, path, hash, blob, db.Scheme())
})
stTrie := NewStackTrie(options)
+
// Fill the trie with elements
for i := 0; i < count; i++ {
// For the stack trie, we need to do inserts in proper order
@@ -977,13 +998,16 @@ func TestCommitSequenceStackTrie(t *testing.T) {
// Flush trie -> database
root, nodes, _ := trie.Commit(false)
// Flush memdb -> disk (sponge)
- db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
- db.Commit(root, false)
+ db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
+ db.Commit(root)
+ s.Flush()
+
// And flush stacktrie -> disk
stRoot := stTrie.Commit()
if stRoot != root {
t.Fatalf("root wrong, got %x exp %x", stRoot, root)
}
+ stackTrieSponge.Flush()
if got, exp := stackTrieSponge.sponge.Sum(nil), s.sponge.Sum(nil); !bytes.Equal(got, exp) {
// Show the journal
t.Logf("Expected:")
@@ -1006,34 +1030,47 @@ func TestCommitSequenceStackTrie(t *testing.T) {
// that even a small trie which contains a leaf will have an extension making it
// not fit into 32 bytes, rlp-encoded. However, it's still the correct thing to do.
func TestCommitSequenceSmallRoot(t *testing.T) {
- s := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "a"}
- db := NewDatabase(rawdb.NewDatabase(s), nil)
+ s := &spongeDb{
+ sponge: sha3.NewLegacyKeccak256(),
+ id: "a",
+ values: make(map[string]string),
+ }
+ db := newTestDatabase(rawdb.NewDatabase(s), rawdb.HashScheme)
trie := NewEmpty(db)
- // Another sponge is used for the stacktrie commits
- stackTrieSponge := &spongeDb{sponge: sha3.NewLegacyKeccak256(), id: "b"}
+ // Another sponge is used for the stacktrie commits
+ stackTrieSponge := &spongeDb{
+ sponge: sha3.NewLegacyKeccak256(),
+ id: "b",
+ values: make(map[string]string),
+ }
options := NewStackTrieOptions()
options = options.WithWriter(func(path []byte, hash common.Hash, blob []byte) {
rawdb.WriteTrieNode(stackTrieSponge, common.Hash{}, path, hash, blob, db.Scheme())
})
stTrie := NewStackTrie(options)
+
// Add a single small-element to the trie(s)
key := make([]byte, 5)
key[0] = 1
trie.Update(key, []byte{0x1})
stTrie.Update(key, []byte{0x1})
+
// Flush trie -> database
root, nodes, _ := trie.Commit(false)
// Flush memdb -> disk (sponge)
- db.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
- db.Commit(root, false)
+ db.Update(root, types.EmptyRootHash, trienode.NewWithNodeSet(nodes))
+ db.Commit(root)
+
// And flush stacktrie -> disk
stRoot := stTrie.Commit()
if stRoot != root {
t.Fatalf("root wrong, got %x exp %x", stRoot, root)
}
-
t.Logf("root: %x\n", stRoot)
+
+ s.Flush()
+ stackTrieSponge.Flush()
if got, exp := stackTrieSponge.sponge.Sum(nil), s.sponge.Sum(nil); !bytes.Equal(got, exp) {
t.Fatalf("test, disk write sequence wrong:\ngot %x exp %x\n", got, exp)
}
@@ -1084,7 +1121,7 @@ func BenchmarkHashFixedSize(b *testing.B) {
func benchmarkHashFixedSize(b *testing.B, addresses [][20]byte, accounts [][]byte) {
b.ReportAllocs()
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
for i := 0; i < len(addresses); i++ {
trie.MustUpdate(crypto.Keccak256(addresses[i][:]), accounts[i])
}
@@ -1135,7 +1172,7 @@ func BenchmarkCommitAfterHashFixedSize(b *testing.B) {
func benchmarkCommitAfterHashFixedSize(b *testing.B, addresses [][20]byte, accounts [][]byte) {
b.ReportAllocs()
- trie := NewEmpty(NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
for i := 0; i < len(addresses); i++ {
trie.MustUpdate(crypto.Keccak256(addresses[i][:]), accounts[i])
}
@@ -1146,60 +1183,6 @@ func benchmarkCommitAfterHashFixedSize(b *testing.B, addresses [][20]byte, accou
b.StopTimer()
}
-func BenchmarkDerefRootFixedSize(b *testing.B) {
- b.Run("10", func(b *testing.B) {
- b.StopTimer()
- acc, add := makeAccounts(20)
- for i := 0; i < b.N; i++ {
- benchmarkDerefRootFixedSize(b, acc, add)
- }
- })
- b.Run("100", func(b *testing.B) {
- b.StopTimer()
- acc, add := makeAccounts(100)
- for i := 0; i < b.N; i++ {
- benchmarkDerefRootFixedSize(b, acc, add)
- }
- })
-
- b.Run("1K", func(b *testing.B) {
- b.StopTimer()
- acc, add := makeAccounts(1000)
- for i := 0; i < b.N; i++ {
- benchmarkDerefRootFixedSize(b, acc, add)
- }
- })
- b.Run("10K", func(b *testing.B) {
- b.StopTimer()
- acc, add := makeAccounts(10000)
- for i := 0; i < b.N; i++ {
- benchmarkDerefRootFixedSize(b, acc, add)
- }
- })
- b.Run("100K", func(b *testing.B) {
- b.StopTimer()
- acc, add := makeAccounts(100000)
- for i := 0; i < b.N; i++ {
- benchmarkDerefRootFixedSize(b, acc, add)
- }
- })
-}
-
-func benchmarkDerefRootFixedSize(b *testing.B, addresses [][20]byte, accounts [][]byte) {
- b.ReportAllocs()
- triedb := NewDatabase(rawdb.NewMemoryDatabase(), nil)
- trie := NewEmpty(triedb)
- for i := 0; i < len(addresses); i++ {
- trie.MustUpdate(crypto.Keccak256(addresses[i][:]), accounts[i])
- }
- h := trie.Hash()
- root, nodes, _ := trie.Commit(false)
- triedb.Update(root, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
- b.StartTimer()
- triedb.Dereference(h)
- b.StopTimer()
-}
-
func getString(trie *Trie, k string) []byte {
return trie.MustGet([]byte(k))
}
diff --git a/trie/verkle.go b/trie/verkle.go
index a32f8810b2..e6f60a8e85 100644
--- a/trie/verkle.go
+++ b/trie/verkle.go
@@ -20,11 +20,11 @@ import (
"encoding/binary"
"errors"
"fmt"
- "math/big"
"github.com/ava-labs/subnet-evm/core/types"
"github.com/ava-labs/subnet-evm/trie/trienode"
"github.com/ava-labs/subnet-evm/trie/utils"
+ "github.com/ava-labs/subnet-evm/triedb/database"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/gballet/go-verkle"
@@ -40,13 +40,12 @@ var (
// interface so that Verkle trees can be reused verbatim.
type VerkleTrie struct {
root verkle.VerkleNode
- db *Database
cache *utils.PointCache
reader *trieReader
}
// NewVerkleTrie constructs a verkle tree based on the specified root hash.
-func NewVerkleTrie(root common.Hash, db *Database, cache *utils.PointCache) (*VerkleTrie, error) {
+func NewVerkleTrie(root common.Hash, db database.Database, cache *utils.PointCache) (*VerkleTrie, error) {
reader, err := newTrieReader(root, common.Hash{}, db)
if err != nil {
return nil, err
@@ -65,7 +64,6 @@ func NewVerkleTrie(root common.Hash, db *Database, cache *utils.PointCache) (*Ve
}
return &VerkleTrie{
root: node,
- db: db,
cache: cache,
reader: reader,
}, nil
@@ -108,7 +106,7 @@ func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error
for i := 0; i < len(balance)/2; i++ {
balance[len(balance)-i-1], balance[i] = balance[i], balance[len(balance)-i-1]
}
- acc.Balance = new(big.Int).SetBytes(balance[:])
+ acc.Balance = new(uint256.Int).SetBytes32(balance[:])
// Decode codehash
acc.CodeHash = values[utils.CodeKeccakLeafKey]
@@ -262,7 +260,6 @@ func (t *VerkleTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
func (t *VerkleTrie) Copy() *VerkleTrie {
return &VerkleTrie{
root: t.root.Copy(),
- db: t.db,
cache: t.cache,
reader: t.reader,
}
diff --git a/trie/verkle_test.go b/trie/verkle_test.go
index 559e68d888..e491a4bbb3 100644
--- a/trie/verkle_test.go
+++ b/trie/verkle_test.go
@@ -18,27 +18,26 @@ package trie
import (
"bytes"
- "math/big"
"reflect"
"testing"
"github.com/ava-labs/subnet-evm/core/rawdb"
"github.com/ava-labs/subnet-evm/core/types"
- "github.com/ava-labs/subnet-evm/trie/triedb/pathdb"
"github.com/ava-labs/subnet-evm/trie/utils"
"github.com/ethereum/go-ethereum/common"
+ "github.com/holiman/uint256"
)
var (
accounts = map[common.Address]*types.StateAccount{
{1}: {
Nonce: 100,
- Balance: big.NewInt(100),
+ Balance: uint256.NewInt(100),
CodeHash: common.Hash{0x1}.Bytes(),
},
{2}: {
Nonce: 200,
- Balance: big.NewInt(200),
+ Balance: uint256.NewInt(200),
CodeHash: common.Hash{0x2}.Bytes(),
},
}
@@ -57,12 +56,7 @@ var (
)
func TestVerkleTreeReadWrite(t *testing.T) {
- db := NewDatabase(rawdb.NewMemoryDatabase(), &Config{
- IsVerkle: true,
- PathDB: pathdb.Defaults,
- })
- defer db.Close()
-
+ db := newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.PathScheme)
tr, _ := NewVerkleTrie(types.EmptyVerkleHash, db, utils.NewPointCache(100))
for addr, acct := range accounts {
diff --git a/trie/database.go b/triedb/database.go
similarity index 91%
rename from trie/database.go
rename to triedb/database.go
index 62550facc7..5383b57540 100644
--- a/trie/database.go
+++ b/triedb/database.go
@@ -14,15 +14,17 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package trie
+package triedb
import (
"errors"
- "github.com/ava-labs/subnet-evm/trie/triedb/hashdb"
- "github.com/ava-labs/subnet-evm/trie/triedb/pathdb"
+ "github.com/ava-labs/subnet-evm/trie"
"github.com/ava-labs/subnet-evm/trie/trienode"
"github.com/ava-labs/subnet-evm/trie/triestate"
+ "github.com/ava-labs/subnet-evm/triedb/database"
+ "github.com/ava-labs/subnet-evm/triedb/hashdb"
+ "github.com/ava-labs/subnet-evm/triedb/pathdb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
@@ -108,14 +110,21 @@ func NewDatabase(diskdb ethdb.Database, config *Config) *Database {
if config.PathDB != nil {
db.backend = pathdb.New(diskdb, config.PathDB)
} else {
- db.backend = hashdb.New(diskdb, config.HashDB, mptResolver{})
+ var resolver hashdb.ChildResolver
+ if config.IsVerkle {
+ // TODO define verkle resolver
+ log.Crit("Verkle node resolver is not defined")
+ } else {
+ resolver = trie.MerkleResolver{}
+ }
+ db.backend = hashdb.New(diskdb, config.HashDB, resolver)
}
return db
}
// Reader returns a reader for accessing all trie nodes with provided state root.
// An error will be returned if the requested state is not available.
-func (db *Database) Reader(blockRoot common.Hash) (Reader, error) {
+func (db *Database) Reader(blockRoot common.Hash) (database.Reader, error) {
switch b := db.backend.(type) {
case *hashdb.Database:
return b.Reader(blockRoot)
@@ -201,8 +210,7 @@ func (db *Database) WritePreimages() {
}
}
-// Preimage retrieves a cached trie node pre-image from memory. If it cannot be
-// found cached, the method queries the persistent database for the content.
+// Preimage retrieves a cached trie node pre-image from preimage store.
func (db *Database) Preimage(hash common.Hash) []byte {
if db.preimages == nil {
return nil
@@ -210,6 +218,14 @@ func (db *Database) Preimage(hash common.Hash) []byte {
return db.preimages.preimage(hash)
}
+// InsertPreimage writes pre-images of trie node to the preimage store.
+func (db *Database) InsertPreimage(preimages map[common.Hash][]byte) {
+ if db.preimages == nil {
+ return
+ }
+ db.preimages.insertPreimage(preimages)
+}
+
// Cap iteratively flushes old but still referenced trie nodes until the total
// memory usage goes below the given threshold. The held pre-images accumulated
// up to this point will be flushed in case the size exceeds the threshold.
@@ -260,7 +276,14 @@ func (db *Database) Recover(target common.Hash) error {
if !ok {
return errors.New("not supported")
}
- return pdb.Recover(target, &trieLoader{db: db})
+ var loader triestate.TrieLoader
+ if db.config.IsVerkle {
+ // TODO define verkle loader
+ log.Crit("Verkle loader is not defined")
+ } else {
+ loader = trie.NewMerkleLoader(db)
+ }
+ return pdb.Recover(target, loader)
}
// Recoverable returns the indicator if the specified state is enabled to be
diff --git a/triedb/database/database.go b/triedb/database/database.go
new file mode 100644
index 0000000000..18a8f454e2
--- /dev/null
+++ b/triedb/database/database.go
@@ -0,0 +1,48 @@
+// Copyright 2024 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 .
+
+package database
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+)
+
+// Reader wraps the Node method of a backing trie reader.
+type Reader interface {
+ // Node retrieves the trie node blob with the provided trie identifier,
+ // node path and the corresponding node hash. No error will be returned
+ // if the node is not found.
+ Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error)
+}
+
+// PreimageStore wraps the methods of a backing store for reading and writing
+// trie node preimages.
+type PreimageStore interface {
+ // Preimage retrieves the preimage of the specified hash.
+ Preimage(hash common.Hash) []byte
+
+ // InsertPreimage commits a set of preimages along with their hashes.
+ InsertPreimage(preimages map[common.Hash][]byte)
+}
+
+// Database wraps the methods of a backing trie store.
+type Database interface {
+ PreimageStore
+
+ // Reader returns a node reader associated with the specific state.
+ // An error will be returned if the specified state is not available.
+ Reader(stateRoot common.Hash) (Reader, error)
+}
diff --git a/trie/triedb/hashdb/database.go b/triedb/hashdb/database.go
similarity index 100%
rename from trie/triedb/hashdb/database.go
rename to triedb/hashdb/database.go
diff --git a/trie/triedb/pathdb/database.go b/triedb/pathdb/database.go
similarity index 100%
rename from trie/triedb/pathdb/database.go
rename to triedb/pathdb/database.go
diff --git a/trie/triedb/pathdb/database_test.go b/triedb/pathdb/database_test.go
similarity index 99%
rename from trie/triedb/pathdb/database_test.go
rename to triedb/pathdb/database_test.go
index 7abfcb09f9..b452ac2a44 100644
--- a/trie/triedb/pathdb/database_test.go
+++ b/triedb/pathdb/database_test.go
@@ -30,7 +30,6 @@ import (
"bytes"
"errors"
"fmt"
- "math/big"
"math/rand"
"testing"
@@ -42,6 +41,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/holiman/uint256"
"github.com/stretchr/testify/require"
)
@@ -64,7 +64,7 @@ func updateTrie(addrHash common.Hash, root common.Hash, dirties, cleans map[comm
func generateAccount(storageRoot common.Hash) types.StateAccount {
return types.StateAccount{
Nonce: uint64(rand.Intn(100)),
- Balance: big.NewInt(rand.Int63()),
+ Balance: uint256.NewInt(rand.Uint64()),
CodeHash: testutil.RandBytes(32),
Root: storageRoot,
}
diff --git a/trie/triedb/pathdb/difflayer.go b/triedb/pathdb/difflayer.go
similarity index 100%
rename from trie/triedb/pathdb/difflayer.go
rename to triedb/pathdb/difflayer.go
diff --git a/trie/triedb/pathdb/difflayer_test.go b/triedb/pathdb/difflayer_test.go
similarity index 100%
rename from trie/triedb/pathdb/difflayer_test.go
rename to triedb/pathdb/difflayer_test.go
diff --git a/trie/triedb/pathdb/disklayer.go b/triedb/pathdb/disklayer.go
similarity index 100%
rename from trie/triedb/pathdb/disklayer.go
rename to triedb/pathdb/disklayer.go
diff --git a/trie/triedb/pathdb/errors.go b/triedb/pathdb/errors.go
similarity index 100%
rename from trie/triedb/pathdb/errors.go
rename to triedb/pathdb/errors.go
diff --git a/trie/triedb/pathdb/history.go b/triedb/pathdb/history.go
similarity index 100%
rename from trie/triedb/pathdb/history.go
rename to triedb/pathdb/history.go
diff --git a/trie/triedb/pathdb/history_test.go b/triedb/pathdb/history_test.go
similarity index 100%
rename from trie/triedb/pathdb/history_test.go
rename to triedb/pathdb/history_test.go
diff --git a/trie/triedb/pathdb/journal.go b/triedb/pathdb/journal.go
similarity index 100%
rename from trie/triedb/pathdb/journal.go
rename to triedb/pathdb/journal.go
diff --git a/trie/triedb/pathdb/layertree.go b/triedb/pathdb/layertree.go
similarity index 100%
rename from trie/triedb/pathdb/layertree.go
rename to triedb/pathdb/layertree.go
diff --git a/trie/triedb/pathdb/metrics.go b/triedb/pathdb/metrics.go
similarity index 100%
rename from trie/triedb/pathdb/metrics.go
rename to triedb/pathdb/metrics.go
diff --git a/trie/triedb/pathdb/nodebuffer.go b/triedb/pathdb/nodebuffer.go
similarity index 100%
rename from trie/triedb/pathdb/nodebuffer.go
rename to triedb/pathdb/nodebuffer.go
diff --git a/trie/triedb/pathdb/testutils.go b/triedb/pathdb/testutils.go
similarity index 100%
rename from trie/triedb/pathdb/testutils.go
rename to triedb/pathdb/testutils.go
diff --git a/trie/preimages.go b/triedb/preimages.go
similarity index 99%
rename from trie/preimages.go
rename to triedb/preimages.go
index e8977e2a57..be337a1fc5 100644
--- a/trie/preimages.go
+++ b/triedb/preimages.go
@@ -24,7 +24,7 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package trie
+package triedb
import (
"sync"