diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 4de07544cd..0362e905b7 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -6,4 +6,5 @@
# review whenever someone opens a pull request.
-* @darioush @ceyonur
+* @ceyonur @darioush @ava-labs/platform-evm
+
diff --git a/.golangci.yml b/.golangci.yml
index 31c41897e1..c6c8d5748a 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -8,8 +8,6 @@ run:
skip-dirs-use-default: true
# Include non-test files tagged as test-only.
# Context: https://github.com/ava-labs/avalanchego/pull/3173
- build-tags:
- - test
linters:
disable-all: true
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 846ffbca05..4cd21c399b 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 31aa31b0ce..f1594e525b 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/coreth/eth"
- "github.com/ava-labs/coreth/vmerrs"
-
- "github.com/ava-labs/coreth/accounts/abi"
"github.com/ava-labs/coreth/accounts/abi/bind"
- "github.com/ava-labs/coreth/consensus/dummy"
- "github.com/ava-labs/coreth/core"
- "github.com/ava-labs/coreth/core/bloombits"
- "github.com/ava-labs/coreth/core/rawdb"
- "github.com/ava-labs/coreth/core/state"
"github.com/ava-labs/coreth/core/types"
- "github.com/ava-labs/coreth/core/vm"
- "github.com/ava-labs/coreth/eth/filters"
+ "github.com/ava-labs/coreth/ethclient/simulated"
"github.com/ava-labs/coreth/interfaces"
- "github.com/ava-labs/coreth/params"
- "github.com/ava-labs/coreth/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/coreth/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().IsApricotPhase3(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/coreth/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 b3eaf65048..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/coreth/accounts/abi"
- "github.com/ava-labs/coreth/accounts/abi/bind"
- "github.com/ava-labs/coreth/core"
- "github.com/ava-labs/coreth/core/types"
- "github.com/ava-labs/coreth/interfaces"
- "github.com/ava-labs/coreth/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 coreth 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 958128ffa8..34f60300e4 100644
--- a/accounts/abi/bind/base.go
+++ b/accounts/abi/bind/base.go
@@ -38,6 +38,7 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/core/vm"
"github.com/ava-labs/coreth/interfaces"
+ "github.com/ava-labs/coreth/rpc"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event"
@@ -437,7 +438,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 141df4657e..aa750dc208 100644
--- a/accounts/abi/bind/base_test.go
+++ b/accounts/abi/bind/base_test.go
@@ -65,7 +65,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 3da6421150..7f82c5dd51 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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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/coreth/accounts/abi/bind"
"github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/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 2acf86fb0c..d5754c603a 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/coreth/accounts/abi/bind"
- "github.com/ava-labs/coreth/accounts/abi/bind/backends"
- "github.com/ava-labs/coreth/core"
"github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/coreth/ethclient/simulated"
+ "github.com/ava-labs/coreth/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/keystore_test.go b/accounts/keystore/keystore_test.go
index ed51fb8e76..25dc517c6b 100644
--- a/accounts/keystore/keystore_test.go
+++ b/accounts/keystore/keystore_test.go
@@ -293,9 +293,6 @@ type walletEvent struct {
// Tests that wallet notifications and correctly fired when accounts are added
// or deleted from the keystore.
func TestWalletNotifications(t *testing.T) {
- if os.Getenv("RUN_FLAKY_TESTS") != "true" {
- t.Skip("FLAKY")
- }
t.Parallel()
_, ks := tmpKeyStore(t, false)
diff --git a/accounts/keystore/passphrase.go b/accounts/keystore/passphrase.go
index acf944f717..73318da4ff 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 d2449bfd81..89fb564140 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/consensus/dummy/consensus.go b/consensus/dummy/consensus.go
index 717f41af66..1452335a95 100644
--- a/consensus/dummy/consensus.go
+++ b/consensus/dummy/consensus.go
@@ -89,6 +89,13 @@ func NewFakerWithMode(cb ConsensusCallbacks, 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{},
@@ -103,7 +110,7 @@ func NewFullFaker() *DummyEngine {
}
}
-func (self *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, header *types.Header, parent *types.Header) error {
+func (eng *DummyEngine) verifyHeaderGasFields(config *params.ChainConfig, header *types.Header, parent *types.Header) 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)
@@ -204,7 +211,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 {
@@ -225,12 +232,12 @@ func (self *DummyEngine) verifyHeader(chain consensus.ChainHeaderReader, header
}
}
// Ensure gas-related header fields are correct
- if err := self.verifyHeaderGasFields(config, header, parent); err != nil {
+ if err := eng.verifyHeaderGasFields(config, header, parent); 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
@@ -270,13 +277,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
@@ -289,29 +296,29 @@ 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,
extraStateChangeContribution *big.Int,
) error {
- if self.consensusMode.ModeSkipBlockFee {
+ if eng.consensusMode.ModeSkipBlockFee {
return nil
}
if baseFee == nil || baseFee.Sign() <= 0 {
@@ -373,14 +380,14 @@ 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 {
// Perform extra state change while finalizing the block
var (
contribution, extDataGasUsed *big.Int
err error
)
- if self.cb.OnExtraStateChange != nil {
- contribution, extDataGasUsed, err = self.cb.OnExtraStateChange(block, state)
+ if eng.cb.OnExtraStateChange != nil {
+ contribution, extDataGasUsed, err = eng.cb.OnExtraStateChange(block, state)
if err != nil {
return err
}
@@ -415,7 +422,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(),
@@ -429,7 +436,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) {
var (
@@ -437,8 +444,8 @@ func (self *DummyEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader,
extraData []byte
err error
)
- if self.cb.OnFinalizeAndAssemble != nil {
- extraData, contribution, extDataGasUsed, err = self.cb.OnFinalizeAndAssemble(header, state, txs)
+ if eng.cb.OnFinalizeAndAssemble != nil {
+ extraData, contribution, extDataGasUsed, err = eng.cb.OnFinalizeAndAssemble(header, state, txs)
if err != nil {
return nil, err
}
@@ -462,7 +469,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,
@@ -482,10 +489,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/consensus/dummy/dynamic_fees.go b/consensus/dummy/dynamic_fees.go
index 72fb03cbfd..de273fbbb5 100644
--- a/consensus/dummy/dynamic_fees.go
+++ b/consensus/dummy/dynamic_fees.go
@@ -145,34 +145,31 @@ func CalcBaseFee(config *params.ChainConfig, parent *types.Header, timestamp uin
return newRollupWindow, baseFee, nil
}
+ num := new(big.Int)
+
if totalGas > parentGasTarget {
// If the parent block used more gas than its target, the baseFee should increase.
- gasUsedDelta := new(big.Int).SetUint64(totalGas - parentGasTarget)
- x := new(big.Int).Mul(parent.BaseFee, gasUsedDelta)
- y := x.Div(x, parentGasTargetBig)
- baseFeeDelta := math.BigMax(
- x.Div(y, baseFeeChangeDenominator),
- common.Big1,
- )
+ num.SetUint64(totalGas - parentGasTarget)
+ num.Mul(num, parent.BaseFee)
+ num.Div(num, parentGasTargetBig)
+ num.Div(num, baseFeeChangeDenominator)
+ baseFeeDelta := math.BigMax(num, common.Big1)
baseFee.Add(baseFee, baseFeeDelta)
} else {
// Otherwise if the parent block used less gas than its target, the baseFee should decrease.
- gasUsedDelta := new(big.Int).SetUint64(parentGasTarget - totalGas)
- x := new(big.Int).Mul(parent.BaseFee, gasUsedDelta)
- y := x.Div(x, parentGasTargetBig)
- baseFeeDelta := math.BigMax(
- x.Div(y, baseFeeChangeDenominator),
- common.Big1,
- )
-
+ num.SetUint64(parentGasTarget - totalGas)
+ num.Mul(num, parent.BaseFee)
+ num.Div(num, parentGasTargetBig)
+ num.Div(num, baseFeeChangeDenominator)
+ baseFeeDelta := math.BigMax(num, common.Big1)
// If [roll] is greater than [rollupWindow], apply the state transition to the base fee to account
// for the interval during which no blocks were produced.
// We use roll/rollupWindow, so that the transition is applied for every [rollupWindow] seconds
// that has elapsed between the parent and this block.
if roll > rollupWindow {
// Note: roll/rollupWindow must be greater than 1 since we've checked that roll > rollupWindow
- baseFeeDelta = baseFeeDelta.Mul(baseFeeDelta, new(big.Int).SetUint64(roll/rollupWindow))
+ baseFeeDelta = new(big.Int).Mul(baseFeeDelta, new(big.Int).SetUint64(roll/rollupWindow))
}
baseFee.Sub(baseFee, baseFeeDelta)
}
diff --git a/consensus/dummy/dynamic_fees_test.go b/consensus/dummy/dynamic_fees_test.go
index 3ed5401f32..c86daf0ffb 100644
--- a/consensus/dummy/dynamic_fees_test.go
+++ b/consensus/dummy/dynamic_fees_test.go
@@ -10,6 +10,7 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/params"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/assert"
@@ -564,3 +565,20 @@ func TestDynamicFeesEtna(t *testing.T) {
// lower than the prior base fee minimum.
require.Less(nextBaseFee.Int64(), params.ApricotPhase4MinBaseFee)
}
+
+func TestCalcBaseFeeRegression(t *testing.T) {
+ parentTimestamp := uint64(1)
+ timestamp := parentTimestamp + params.RollupWindow + 1000
+
+ parentHeader := &types.Header{
+ Time: parentTimestamp,
+ GasUsed: 14_999_999,
+ Number: big.NewInt(1),
+ BaseFee: big.NewInt(1),
+ Extra: make([]byte, params.DynamicFeeExtraDataSize),
+ }
+
+ _, _, err := CalcBaseFee(params.TestChainConfig, parentHeader, timestamp)
+ require.NoError(t, err)
+ require.Equalf(t, 0, common.Big1.Cmp(big.NewInt(1)), "big1 should be 1, got %s", common.Big1)
+}
diff --git a/core/bench_test.go b/core/bench_test.go
index 34d46e09f7..ceb8c83f48 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 cac2bb82b9..2dcd2738a6 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -50,8 +50,9 @@ import (
"github.com/ava-labs/coreth/metrics"
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/trie"
- "github.com/ava-labs/coreth/trie/triedb/hashdb"
- "github.com/ava-labs/coreth/trie/triedb/pathdb"
+ "github.com/ava-labs/coreth/triedb"
+ "github.com/ava-labs/coreth/triedb/hashdb"
+ "github.com/ava-labs/coreth/triedb/pathdb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/ethdb"
@@ -164,7 +165,7 @@ type CacheConfig struct {
SnapshotVerify bool // Verify generated snapshots
Preimages bool // Whether to store preimage of trie key to the disk
AcceptedCacheSize int // Depth of accepted headers cache and accepted logs cache at the accepted tip
- TxLookupLimit uint64 // Number of recent blocks for which to maintain transaction lookup indices
+ TransactionHistory uint64 // Number of recent blocks for which to maintain transaction lookup indices
SkipTxIndexing bool // Whether to skip transaction indexing
StateHistory uint64 // Number of blocks from head whose state histories are reserved.
StateScheme string // Scheme used to store ethereum states and merkle tree nodes on top
@@ -174,8 +175,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,
@@ -215,6 +216,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.
//
@@ -233,10 +241,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
@@ -258,11 +267,11 @@ 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
+ 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
stopping atomic.Bool // false if chain is running, true when stopped
@@ -330,7 +339,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
@@ -358,7 +367,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),
engine: engine,
vmConfig: vmConfig,
@@ -425,115 +434,21 @@ func NewBlockChain(
bc.warmAcceptedCaches()
// if txlookup limit is 0 (uindexing disabled), we don't need to repair the tx index tail.
- if bc.cacheConfig.TxLookupLimit != 0 {
+ 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.
- if bc.cacheConfig.TxLookupLimit != 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)
- }()
+ // Start tx indexer if it's enabled.
+ if bc.cacheConfig.TransactionHistory != 0 {
+ 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.TxLookupLimit
- 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.TxLookupLimit
-
- // 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
@@ -929,12 +844,12 @@ func (bc *BlockChain) ValidateCanonicalChain() error {
// that the transactions have been indexed, if we are checking below the last accepted
// block.
shouldIndexTxs := !bc.cacheConfig.SkipTxIndexing &&
- (bc.cacheConfig.TxLookupLimit == 0 || bc.lastAccepted.NumberU64() < current.Number.Uint64()+bc.cacheConfig.TxLookupLimit)
+ (bc.cacheConfig.TransactionHistory == 0 || bc.lastAccepted.NumberU64() < current.Number.Uint64()+bc.cacheConfig.TransactionHistory)
if current.Number.Uint64() <= bc.lastAccepted.NumberU64() && shouldIndexTxs {
// 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())
}
@@ -991,6 +906,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)
@@ -1165,6 +1084,9 @@ func (bc *BlockChain) Reject(block *types.Block) error {
return fmt.Errorf("failed to write delete block batch: %w", err)
}
+ // Remove the block from the block cache (ignore return value of whether it was in the cache)
+ _ = bc.blockCache.Remove(block.Hash())
+
return nil
}
@@ -1370,7 +1292,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()
}
@@ -1582,6 +1504,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-- {
@@ -1721,32 +1649,6 @@ func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, e
log.Debug(reason.String())
}
-func (bc *BlockChain) RemoveRejectedBlocks(start, end uint64) error {
- batch := bc.db.NewBatch()
-
- for i := start; i < end; i++ {
- hashes := rawdb.ReadAllHashes(bc.db, i)
- canonicalBlock := bc.GetBlockByNumber((i))
- if canonicalBlock == nil {
- return fmt.Errorf("failed to retrieve block by number at height %d", i)
- }
- canonicalHash := canonicalBlock.Hash()
- for _, hash := range hashes {
- if hash == canonicalHash {
- continue
- }
- rawdb.DeleteBlock(batch, hash, i)
- }
-
- if err := batch.Write(); err != nil {
- return fmt.Errorf("failed to write delete rejected block batch at height %d", i)
- }
- batch.Reset()
- }
-
- return nil
-}
-
// reprocessBlock reprocesses a previously accepted block. This is often used
// to regenerate previously pruned state tries.
func (bc *BlockChain) reprocessBlock(parent *types.Block, current *types.Block) (common.Hash, error) {
@@ -2160,8 +2062,8 @@ 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.TxLookupLimit != 0 {
- bc.setTxIndexTail(block.NumberU64())
+ if bc.cacheConfig.TransactionHistory != 0 {
+ bc.repairTxIndexTail(block.NumberU64())
}
// Update all in-memory chain markers
@@ -2197,18 +2099,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_log_test.go b/core/blockchain_log_test.go
index 5c7df458d3..f76a2a291d 100644
--- a/core/blockchain_log_test.go
+++ b/core/blockchain_log_test.go
@@ -42,7 +42,7 @@ func TestAcceptedLogsSubscription(t *testing.T) {
funds = new(big.Int).Mul(big.NewInt(100), big.NewInt(params.Ether))
gspec = &Genesis{
Config: params.TestChainConfig,
- Alloc: GenesisAlloc{addr1: {Balance: funds}},
+ Alloc: types.GenesisAlloc{addr1: {Balance: funds}},
BaseFee: big.NewInt(params.ApricotPhase3InitialBaseFee),
}
contractAddress = crypto.CreateAddress(addr1, 0)
diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go
index 4c2f81e14b..8f73445f43 100644
--- a/core/blockchain_reader.go
+++ b/core/blockchain_reader.go
@@ -34,7 +34,7 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/core/vm"
"github.com/ava-labs/coreth/params"
- "github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/event"
)
@@ -197,20 +197,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.
@@ -282,10 +299,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 9fbf21da6e..6fdd572d79 100644
--- a/core/blockchain_repair_test.go
+++ b/core/blockchain_repair_test.go
@@ -39,7 +39,7 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/core/vm"
"github.com/ava-labs/coreth/params"
- "github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
@@ -534,7 +534,7 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
gspec = &Genesis{
BaseFee: big.NewInt(params.ApricotPhase3InitialBaseFee),
Config: params.TestChainConfig,
- Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(params.Ether)}},
+ Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(params.Ether)}},
}
signer = types.LatestSigner(gspec.Config)
engine = dummy.NewFullFaker()
@@ -562,7 +562,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, dummy.ApricotPhase3InitialBaseFee, nil), signer, key1)
@@ -575,7 +575,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 069d8fd8fc..1c51be82da 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -9,6 +9,7 @@ import (
"os"
"testing"
+ "github.com/ava-labs/avalanchego/upgrade"
"github.com/ava-labs/coreth/consensus/dummy"
"github.com/ava-labs/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/state"
@@ -20,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 (
@@ -310,7 +311,7 @@ func testRepopulateMissingTriesParallel(t *testing.T, parallelism int) {
genesisBalance := big.NewInt(1000000)
gspec := &Genesis{
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
- Alloc: GenesisAlloc{addr1: {Balance: genesisBalance}},
+ Alloc: types.GenesisAlloc{addr1: {Balance: genesisBalance}},
}
blockchain, err := createBlockChain(chainDB, pruningConfig, gspec, common.Hash{})
@@ -423,7 +424,7 @@ func TestUngracefulAsyncShutdown(t *testing.T) {
genesisBalance := big.NewInt(1000000)
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{})
@@ -477,13 +478,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)
}
@@ -495,9 +496,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)
}
@@ -526,7 +528,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)
}
@@ -534,207 +536,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.NewFakerWithCallbacks(TestCallbacks), 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.NewFakerWithCallbacks(TestCallbacks), 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.TxLookupLimit = 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.NewFakerWithCallbacks(TestCallbacks), 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.NewFakerWithCallbacks(TestCallbacks), 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.TxLookupLimit = 2
- chainDB = rawdb.NewMemoryDatabase()
- chain, err = createAndInsertChain(chainDB, conf, gspec, blocks, common.Hash{},
- func(b *types.Block) {
- bNumber := b.NumberU64()
- tail := bNumber - conf.TxLookupLimit + 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.TxLookupLimit = 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.TxLookupLimit = 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.TxLookupLimit + 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) {
@@ -781,7 +582,7 @@ func testCanonicalHashMarker(t *testing.T, scheme string) {
var (
gspec = &Genesis{
Config: params.TestChainConfig,
- Alloc: GenesisAlloc{},
+ Alloc: types.GenesisAlloc{},
BaseFee: big.NewInt(params.ApricotPhase3InitialBaseFee),
}
engine = dummy.NewCoinbaseFaker()
@@ -862,7 +663,7 @@ func TestTxLookupBlockChain(t *testing.T) {
SnapshotLimit: 256,
SnapshotNoBuild: true, // Ensure the test errors if snapshot initialization fails
AcceptorQueueLimit: 64, // ensure channel doesn't block
- TxLookupLimit: 5,
+ TransactionHistory: 5,
}
createTxLookupBlockChain := func(db ethdb.Database, gspec *Genesis, lastAcceptedHash common.Hash) (*BlockChain, error) {
return createBlockChain(db, cacheConf, gspec, lastAcceptedHash)
@@ -885,7 +686,7 @@ func TestTxLookupSkipIndexingBlockChain(t *testing.T) {
SnapshotLimit: 256,
SnapshotNoBuild: true, // Ensure the test errors if snapshot initialization fails
AcceptorQueueLimit: 64, // ensure channel doesn't block
- TxLookupLimit: 5,
+ TransactionHistory: 5,
SkipTxIndexing: true,
}
createTxLookupBlockChain := func(db ethdb.Database, gspec *Genesis, lastAcceptedHash common.Hash) (*BlockChain, error) {
@@ -944,7 +745,7 @@ func testCreateThenDelete(t *testing.T, config *params.ChainConfig) {
}...)
gspec := &Genesis{
Config: config,
- Alloc: GenesisAlloc{
+ Alloc: types.GenesisAlloc{
address: {Balance: funds},
},
}
@@ -1030,7 +831,7 @@ func TestDeleteThenCreate(t *testing.T) {
gspec := &Genesis{
Config: params.TestChainConfig,
- Alloc: GenesisAlloc{
+ Alloc: types.GenesisAlloc{
address: {Balance: funds},
},
}
@@ -1146,7 +947,7 @@ func TestTransientStorageReset(t *testing.T) {
}...)
gspec := &Genesis{
Config: params.TestChainConfig,
- Alloc: GenesisAlloc{
+ Alloc: types.GenesisAlloc{
address: {Balance: funds},
},
}
@@ -1212,7 +1013,8 @@ func TestEIP3651(t *testing.T) {
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
funds = new(big.Int).Mul(common.Big1, big.NewInt(params.Ether))
gspec = &Genesis{
- Config: params.TestChainConfig,
+ Config: params.TestChainConfig,
+ Timestamp: uint64(upgrade.InitiallyActiveTime.Unix()),
Alloc: GenesisAlloc{
addr1: {Balance: funds},
addr2: {Balance: funds},
@@ -1291,7 +1093,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())
@@ -1302,31 +1104,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 df0175bc9b..f8831ade81 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -38,9 +38,10 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/core/vm"
"github.com/ava-labs/coreth/params"
- "github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
+ "github.com/holiman/uint256"
)
// BlockGen creates blocks for testing.
@@ -167,7 +168,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)
}
@@ -277,7 +278,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)
@@ -309,8 +310,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++ {
@@ -358,7 +360,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 668cfba6f6..34c53bd7f1 100644
--- a/core/chain_makers_test.go
+++ b/core/chain_makers_test.go
@@ -35,7 +35,7 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/core/vm"
"github.com/ava-labs/coreth/params"
- "github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/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 461efa3e28..e00fbd83f7 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 896cbfac12..bc33411714 100644
--- a/core/evm.go
+++ b/core/evm.go
@@ -36,7 +36,7 @@ import (
"github.com/ava-labs/coreth/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
@@ -167,7 +167,7 @@ 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
}
@@ -176,7 +176,7 @@ func CanTransferMC(db vm.StateDB, addr common.Address, to common.Address, coinID
}
// 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 a05cee87f1..1112a6bab6 100644
--- a/core/gen_genesis.go
+++ b/core/gen_genesis.go
@@ -7,6 +7,7 @@ import (
"errors"
"math/big"
+ "github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/params"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
@@ -18,21 +19,21 @@ 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"`
- 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.GenesisAccount `json:"alloc" gencodec:"required"`
+ 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
@@ -44,7 +45,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.GenesisAccount, len(g.Alloc))
for k, v := range g.Alloc {
enc.Alloc[common.UnprefixedAddress(k)] = v
}
@@ -61,21 +62,21 @@ 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"`
- 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.GenesisAccount `json:"alloc" gencodec:"required"`
+ 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 {
@@ -110,7 +111,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 bb8de14c80..0a69436296 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -28,7 +28,6 @@ package core
import (
"bytes"
- "encoding/hex"
"encoding/json"
"errors"
"fmt"
@@ -39,19 +38,26 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/trie"
- "github.com/ava-labs/coreth/trie/triedb/pathdb"
+ "github.com/ava-labs/coreth/triedb"
+ "github.com/ava-labs/coreth/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/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.GenesisAccount
+
+// Deprecated: use types.GenesisAlloc instead.
+type GenesisAlloc = types.GenesisAlloc
+
// Genesis specifies the header fields, state of a genesis block. It also defines hard
// fork switch-over blocks through the chain configuration.
type Genesis struct {
@@ -63,7 +69,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"`
// These fields are used for consensus tests. Please don't use them
// in actual genesis blocks.
@@ -75,33 +81,7 @@ 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
-}
-
-type GenesisMultiCoinBalance map[common.Hash]*big.Int
-
// 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"`
- MCBalance GenesisMultiCoinBalance `json:"mcbalance,omitempty"`
- Nonce uint64 `json:"nonce,omitempty"`
- PrivateKey []byte `json:"secretKey,omitempty"` // for tests
-}
-
// field type overrides for gencodec
type genesisSpecMarshaling struct {
Nonce math.HexOrDecimal64
@@ -111,40 +91,12 @@ type genesisSpecMarshaling struct {
GasUsed math.HexOrDecimal64
Number math.HexOrDecimal64
Difficulty *math.HexOrDecimal256
+ Alloc map[common.UnprefixedAddress]types.Account
BaseFee *math.HexOrDecimal256
- Alloc map[common.UnprefixedAddress]GenesisAccount
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 {
@@ -171,7 +123,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
@@ -258,21 +210,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)
@@ -299,7 +251,7 @@ func (g *Genesis) toBlock(db ethdb.Database, triedb *trie.Database) *types.Block
}
for addr, account := range g.Alloc {
- statedb.AddBalance(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 {
@@ -358,7 +310,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")
@@ -381,7 +333,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)
@@ -393,10 +345,10 @@ func (g *Genesis) MustCommit(db ethdb.Database, triedb *trie.Database) *types.Bl
func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block {
g := Genesis{
Config: params.TestChainConfig,
- Alloc: GenesisAlloc{addr: {Balance: balance}},
+ Alloc: types.GenesisAlloc{addr: {Balance: balance}},
BaseFee: big.NewInt(params.ApricotPhase3InitialBaseFee),
}
- 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 5deb4a8bf9..35adbab77f 100644
--- a/core/genesis_test.go
+++ b/core/genesis_test.go
@@ -40,7 +40,8 @@ import (
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/precompile/contracts/warp"
"github.com/ava-labs/coreth/trie"
- "github.com/ava-labs/coreth/trie/triedb/pathdb"
+ "github.com/ava-labs/coreth/triedb"
+ "github.com/ava-labs/coreth/triedb/pathdb"
"github.com/ava-labs/coreth/utils"
"github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/common"
@@ -48,7 +49,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)
}
@@ -72,7 +73,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
customghash = common.HexToHash("0x1099a11e9e454bd3ef31d688cf21936671966407bc330f051d754b5ce401e7ed")
customg = Genesis{
Config: &apricotPhase1Config,
- Alloc: GenesisAlloc{
+ Alloc: types.GenesisAlloc{
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
},
}
@@ -92,7 +93,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,
@@ -100,7 +101,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,
@@ -108,7 +109,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{})
},
@@ -118,7 +119,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)
},
@@ -130,7 +131,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
// Commit the 'old' genesis block with ApricotPhase1 transition at 90.
// Advance to block #4, past the ApricotPhase1 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)
@@ -195,7 +196,7 @@ func TestNetworkUpgradeBetweenHeadAndAcceptedBlock(t *testing.T) {
db := rawdb.NewMemoryDatabase()
customg := Genesis{
Config: params.TestApricotPhase1Config,
- Alloc: GenesisAlloc{
+ Alloc: types.GenesisAlloc{
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
},
}
@@ -232,7 +233,7 @@ func TestNetworkUpgradeBetweenHeadAndAcceptedBlock(t *testing.T) {
require.Less(bc.lastAccepted.Time(), *apricotPhase2Timestamp)
// This should not return any error since the last accepted block is before the activation block.
- config, _, err := setupGenesisBlock(db, trie.NewDatabase(db, nil), &activatedGenesis, bc.lastAccepted.Hash())
+ config, _, err := setupGenesisBlock(db, triedb.NewDatabase(db, nil), &activatedGenesis, bc.lastAccepted.Hash())
require.NoError(err)
if !reflect.DeepEqual(config, activatedGenesis.Config) {
t.Errorf("returned %v\nwant %v", config, activatedGenesis.Config)
@@ -244,13 +245,13 @@ 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}}},
},
}
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)
@@ -258,7 +259,7 @@ func TestGenesisWriteUpgradesRegression(t *testing.T) {
genesis.Config.UpgradeConfig.PrecompileUpgrades = []params.PrecompileUpgrade{
{
- Config: warp.NewConfig(utils.NewUint64(51), 0),
+ Config: warp.NewConfig(utils.NewUint64(51), 0, false),
},
}
_, _, err = SetupGenesisBlock(db, trieDB, genesis, genesisBlock.Hash(), false)
@@ -280,11 +281,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) {
@@ -309,7 +310,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}}},
},
}
@@ -321,7 +322,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 f302c99cb6..5f449e4b6c 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 178946e7f8..0e4e762789 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 0fbec54093..75922eba5d 100644
--- a/core/rawdb/database.go
+++ b/core/rawdb/database.go
@@ -458,3 +458,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 9c9614f800..d6a36ef6b1 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 f65fd75821..e29e9b8d78 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -35,6 +35,7 @@ import (
"github.com/ava-labs/coreth/trie"
"github.com/ava-labs/coreth/trie/trienode"
"github.com/ava-labs/coreth/trie/utils"
+ "github.com/ava-labs/coreth/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 5a1ee1aef6..d412716874 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
}
multiCoinEnable struct {
account *common.Address
diff --git a/core/state/pruner/bloom.go b/core/state/pruner/bloom.go
index 158077a4a9..b7f673b80d 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 27f7be0b1f..29431a0ecd 100644
--- a/core/state/pruner/pruner.go
+++ b/core/state/pruner/pruner.go
@@ -41,6 +41,7 @@ import (
"github.com/ava-labs/coreth/core/state/snapshot"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/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 0674839fe4..d8d3b750e2 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 b2a0aa4f5a..4f38072637 100644
--- a/core/state/snapshot/disklayer.go
+++ b/core/state/snapshot/disklayer.go
@@ -33,7 +33,7 @@ import (
"github.com/ava-labs/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/types"
- "github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/triedb"
"github.com/ava-labs/coreth/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 3dede62341..32c04c0a83 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 f051fcd5c4..9d10afd3f9 100644
--- a/core/state/snapshot/generate.go
+++ b/core/state/snapshot/generate.go
@@ -34,6 +34,7 @@ import (
"github.com/ava-labs/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/triedb"
"github.com/ava-labs/coreth/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 c805348925..a31756d06b 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/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/trie"
- "github.com/ava-labs/coreth/trie/triedb/hashdb"
- "github.com/ava-labs/coreth/trie/triedb/pathdb"
"github.com/ava-labs/coreth/trie/trienode"
+ "github.com/ava-labs/coreth/triedb"
+ "github.com/ava-labs/coreth/triedb/hashdb"
+ "github.com/ava-labs/coreth/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()}) // 0x7dd654835190324640832972b7c4c6eaa0c50541e36766d054ed57721f1dc7eb
- helper.addTrieAccount("acc-2", &types.StateAccount{Balance: big.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0xf73118e0254ce091588d66038744a0afae5f65a194de67cff310c683ae43329e
- helper.addTrieAccount("acc-3", &types.StateAccount{Balance: big.NewInt(3), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x515d3de35e143cd976ad476398d910aa7bf8a02e8fd7eb9e3baacddbbcbfcb41
+ helper.addTrieAccount("acc-1", &types.StateAccount{Balance: uint256.NewInt(1), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x7dd654835190324640832972b7c4c6eaa0c50541e36766d054ed57721f1dc7eb
+ helper.addTrieAccount("acc-2", &types.StateAccount{Balance: uint256.NewInt(2), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0xf73118e0254ce091588d66038744a0afae5f65a194de67cff310c683ae43329e
+ helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: types.EmptyRootHash, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x515d3de35e143cd976ad476398d910aa7bf8a02e8fd7eb9e3baacddbbcbfcb41
root := helper.Commit() // Root: 0xfa04f652e8bd3938971bf7d71c3c688574af334ca8bc20e64b01ba610ae93cad
@@ -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()}) // 0x70da4ebd7602dd313c936b39000ed9ab7f849986a90ea934f0c3ec4cc9840441
+ helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x70da4ebd7602dd313c936b39000ed9ab7f849986a90ea934f0c3ec4cc9840441
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()}) // 0x70da4ebd7602dd313c936b39000ed9ab7f849986a90ea934f0c3ec4cc9840441
+ helper.addTrieAccount("acc-3", &types.StateAccount{Balance: uint256.NewInt(3), Root: stRoot, CodeHash: types.EmptyCodeHash.Bytes()}) // 0x70da4ebd7602dd313c936b39000ed9ab7f849986a90ea934f0c3ec4cc9840441
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) // 0x547b07c3a71669c00eda14077d85c7fd14575b92d459572540b25b9a11914dcb
@@ -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: 0x2609234ce43f5e471202c87e017ffb4dfecdb3163cfcbaa55de04baa59cad42d
@@ -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 cc73974ef0..7e5c4c0aa0 100644
--- a/core/state/snapshot/journal.go
+++ b/core/state/snapshot/journal.go
@@ -33,7 +33,7 @@ import (
"time"
"github.com/ava-labs/coreth/core/rawdb"
- "github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/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 a82c49c7e0..abb3051762 100644
--- a/core/state/snapshot/snapshot.go
+++ b/core/state/snapshot/snapshot.go
@@ -37,7 +37,7 @@ import (
"github.com/ava-labs/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/metrics"
- "github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/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 01c1bf104d..67f6ba40f5 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/coreth/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 e8c8879871..8f0493ed3d 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -39,6 +39,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 +104,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()) && !s.data.IsMultiCoin
+ return s.data.Nonce == 0 && s.data.Balance.IsZero() && bytes.Equal(s.data.CodeHash, types.EmptyCodeHash.Bytes()) && !s.data.IsMultiCoin
}
// newObject creates a state object.
@@ -415,31 +416,31 @@ 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)
}
@@ -472,7 +473,7 @@ func (s *stateObject) SetBalanceMultiCoin(coinID common.Hash, amount *big.Int, d
s.SetState(coinID, common.BigToHash(amount))
}
-func (s *stateObject) setBalance(amount *big.Int) {
+func (s *stateObject) setBalance(amount *uint256.Int) {
s.data.Balance = amount
}
@@ -575,7 +576,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 ad096e9cbf..0d6f7d6445 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/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/types"
- "github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/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,81 @@ 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": "1d75ab73e172edb7c3b3c0fd004d9896992fb96b617f6f954641d7618159e5e4",
+ "accounts": {
+ "0x0000000000000000000000000000000000000001": {
+ "balance": "22",
+ "nonce": 0,
+ "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
+ "isMultiCoin": false,
+ "address": "0x0000000000000000000000000000000000000001",
+ "key": "0x1468288056310c82aa4c01a7e12a10f8111a0560e72b700555479031b86c357d"
+ },
+ "0x0000000000000000000000000000000000000002": {
+ "balance": "44",
+ "nonce": 0,
+ "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
+ "isMultiCoin": false,
+ "address": "0x0000000000000000000000000000000000000002",
+ "key": "0xd52688a8f926c816ca1e079067caba944f158e764817b83fc43594370ca9cf62"
+ },
+ "0x0000000000000000000000000000000000000102": {
+ "balance": "0",
+ "nonce": 0,
+ "root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "codeHash": "0x87874902497a5bb968da31a2998d8f22e949d1ef6214bcdedd8bae24cca4b9e3",
+ "code": "0x03030303030303",
+ "isMultiCoin": false,
+ "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 +147,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 6d49444cfb..9eda070321 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -45,6 +45,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 +334,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
}
// Retrieve the balance from the given address or 0 if object not found
@@ -393,10 +394,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.
@@ -446,23 +447,23 @@ 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)
}
@@ -470,7 +471,7 @@ func (s *StateDB) SetBalance(addr common.Address, amount *big.Int) {
// AddBalance adds amount to the account associated with addr.
func (s *StateDB) AddBalanceMultiCoin(addr common.Address, coinID common.Hash, amount *big.Int) {
- stateObject := s.GetOrNewStateObject(addr)
+ stateObject := s.getOrNewStateObject(addr)
if stateObject != nil {
stateObject.AddBalanceMultiCoin(coinID, amount, s.db)
}
@@ -478,35 +479,35 @@ func (s *StateDB) AddBalanceMultiCoin(addr common.Address, coinID common.Hash, a
// SubBalance subtracts amount from the account associated with addr.
func (s *StateDB) SubBalanceMultiCoin(addr common.Address, coinID common.Hash, amount *big.Int) {
- stateObject := s.GetOrNewStateObject(addr)
+ stateObject := s.getOrNewStateObject(addr)
if stateObject != nil {
stateObject.SubBalanceMultiCoin(coinID, amount, s.db)
}
}
func (s *StateDB) SetBalanceMultiCoin(addr common.Address, coinID common.Hash, amount *big.Int) {
- stateObject := s.GetOrNewStateObject(addr)
+ stateObject := s.getOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetBalanceMultiCoin(coinID, amount, s.db)
}
}
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 {
NormalizeStateKey(&key)
stateObject.SetState(key, value)
@@ -528,7 +529,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)
}
@@ -547,10 +548,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) {
@@ -712,8 +713,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 7187d574ec..f694cb2a0f 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/coreth/core/state/snapshot"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/trie"
- "github.com/ava-labs/coreth/trie/triedb/pathdb"
"github.com/ava-labs/coreth/trie/triestate"
+ "github.com/ava-labs/coreth/triedb"
+ "github.com/ava-labs/coreth/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 19f1a5198f..7ae230fbed 100644
--- a/core/state/statedb_test.go
+++ b/core/state/statedb_test.go
@@ -44,9 +44,10 @@ import (
"github.com/ava-labs/coreth/core/state/snapshot"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/trie"
- "github.com/ava-labs/coreth/trie/triedb/hashdb"
- "github.com/ava-labs/coreth/trie/triedb/pathdb"
"github.com/ava-labs/coreth/trie/trienode"
+ "github.com/ava-labs/coreth/triedb"
+ "github.com/ava-labs/coreth/triedb/hashdb"
+ "github.com/ava-labs/coreth/triedb/pathdb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
@@ -59,14 +60,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 +96,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 +177,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 +191,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 +219,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 +277,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 +542,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 +563,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 +586,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 +604,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 +618,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 +633,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 +659,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 +677,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 +691,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 +705,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 +728,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 +747,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 +777,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 +787,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 +810,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 +857,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)
@@ -1042,11 +1043,11 @@ func TestMultiCoinOperations(t *testing.T) {
addr := common.Address{1}
assetID := common.Hash{2}
- s.state.GetOrNewStateObject(addr)
+ s.state.getOrNewStateObject(addr)
root, _ := s.state.Commit(0, false, false)
s.state, _ = NewWithSnapshot(root, s.state.db, s.state.snap)
- s.state.AddBalance(addr, new(big.Int))
+ s.state.AddBalance(addr, new(uint256.Int))
balance := s.state.GetBalanceMultiCoin(addr, assetID)
if balance.Cmp(big.NewInt(0)) != 0 {
@@ -1079,15 +1080,15 @@ func TestMultiCoinSnapshot(t *testing.T) {
var stateDB *StateDB
assertBalances := func(regular, multicoin1, multicoin2 int64) {
balance := stateDB.GetBalance(addr)
- if balance.Cmp(big.NewInt(regular)) != 0 {
+ if balance.Cmp(uint256.NewInt(uint64(regular))) != 0 {
t.Fatal("incorrect non-multicoin balance")
}
- balance = stateDB.GetBalanceMultiCoin(addr, assetID1)
- if balance.Cmp(big.NewInt(multicoin1)) != 0 {
+ balanceBig := stateDB.GetBalanceMultiCoin(addr, assetID1)
+ if balanceBig.Cmp(big.NewInt(multicoin1)) != 0 {
t.Fatal("incorrect multicoin1 balance")
}
- balance = stateDB.GetBalanceMultiCoin(addr, assetID2)
- if balance.Cmp(big.NewInt(multicoin2)) != 0 {
+ balanceBig = stateDB.GetBalanceMultiCoin(addr, assetID2)
+ if balanceBig.Cmp(big.NewInt(multicoin2)) != 0 {
t.Fatal("incorrect multicoin2 balance")
}
}
@@ -1096,7 +1097,7 @@ func TestMultiCoinSnapshot(t *testing.T) {
stateDB, _ = New(root, sdb, snapTree)
assertBalances(0, 0, 0)
- stateDB.AddBalance(addr, big.NewInt(10))
+ stateDB.AddBalance(addr, uint256.NewInt(10))
assertBalances(10, 0, 0)
// Commit and get the new root
@@ -1122,7 +1123,7 @@ func TestMultiCoinSnapshot(t *testing.T) {
// Do one more add, including the regular balance which is now in the
// collapsed snapshot
stateDB, _ = New(root, sdb, snapTree)
- stateDB.AddBalance(addr, big.NewInt(1))
+ stateDB.AddBalance(addr, uint256.NewInt(1))
stateDB.AddBalanceMultiCoin(addr, assetID1, big.NewInt(1))
root, _ = stateDB.Commit(0, false, false)
stateDB, _ = New(root, sdb, snapTree)
@@ -1196,7 +1197,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)
)
@@ -1268,7 +1269,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)
@@ -1277,13 +1278,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)
@@ -1302,14 +1303,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())
@@ -1321,7 +1322,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/sync_test.go b/core/state/sync_test.go
index 22eda7fa8d..5b1c9e3866 100644
--- a/core/state/sync_test.go
+++ b/core/state/sync_test.go
@@ -21,12 +21,13 @@ import (
"github.com/ava-labs/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/types"
- "github.com/ava-labs/coreth/trie"
- "github.com/ava-labs/coreth/trie/triedb/hashdb"
- "github.com/ava-labs/coreth/trie/triedb/pathdb"
+ "github.com/ava-labs/coreth/triedb"
+ "github.com/ava-labs/coreth/triedb/hashdb"
+ "github.com/ava-labs/coreth/triedb/pathdb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
+ "github.com/holiman/uint256"
)
// testAccount is the data associated with an account used by the state tests.
@@ -38,26 +39,26 @@ type testAccount struct {
}
// makeTestState create a sample test state to test node-wise reconstruction.
-func makeTestState(scheme string) (ethdb.Database, Database, *trie.Database, common.Hash, []*testAccount) {
+func makeTestState(scheme string) (ethdb.Database, Database, *triedb.Database, common.Hash, []*testAccount) {
// Create an empty state
- config := &trie.Config{Preimages: true}
+ config := &triedb.Config{Preimages: true}
if scheme == rawdb.PathScheme {
config.PathDB = pathdb.Defaults
} else {
config.HashDB = hashdb.Defaults
}
db := rawdb.NewMemoryDatabase()
- nodeDb := trie.NewDatabase(db, config)
+ nodeDb := triedb.NewDatabase(db, config)
sdb := NewDatabaseWithNodeDB(db, nodeDb)
state, _ := New(types.EmptyRootHash, sdb, nil)
// Fill it with some arbitrary data
var accounts []*testAccount
for i := byte(0); i < 96; i++ {
- obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
+ obj := state.getOrNewStateObject(common.BytesToAddress([]byte{i}))
acc := &testAccount{address: common.BytesToAddress([]byte{i})}
- obj.AddBalance(big.NewInt(int64(11 * i)))
+ obj.AddBalance(uint256.NewInt(uint64(11 * i)))
acc.balance = big.NewInt(int64(11 * i))
obj.SetNonce(uint64(42 * i))
diff --git a/core/state/trie_prefetcher_test.go b/core/state/trie_prefetcher_test.go
index 285a7b16da..4f70b49179 100644
--- a/core/state/trie_prefetcher_test.go
+++ b/core/state/trie_prefetcher_test.go
@@ -34,6 +34,7 @@ import (
"github.com/ava-labs/coreth/core/rawdb"
"github.com/ava-labs/coreth/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 4304744e3d..af5b888686 100644
--- a/core/state_processor.go
+++ b/core/state_processor.go
@@ -198,7 +198,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 5646604d11..028f49479a 100644
--- a/core/state_processor_test.go
+++ b/core/state_processor_test.go
@@ -31,6 +31,7 @@ import (
"math/big"
"testing"
+ "github.com/ava-labs/avalanchego/upgrade"
"github.com/ava-labs/coreth/consensus"
"github.com/ava-labs/coreth/consensus/dummy"
"github.com/ava-labs/coreth/consensus/misc/eip4844"
@@ -108,9 +109,10 @@ func TestStateProcessorErrors(t *testing.T) {
var (
db = rawdb.NewMemoryDatabase()
gspec = &Genesis{
- Config: config,
- Alloc: GenesisAlloc{
- common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
+ Config: config,
+ Timestamp: uint64(upgrade.InitiallyActiveTime.Unix()),
+ Alloc: types.GenesisAlloc{
+ common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): types.GenesisAccount{
Balance: big.NewInt(4000000000000000000), // 4 ether
Nonce: 0,
},
@@ -216,7 +218,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{
@@ -270,8 +272,8 @@ func TestStateProcessorErrors(t *testing.T) {
ApricotPhase2BlockTimestamp: utils.NewUint64(0),
},
},
- Alloc: GenesisAlloc{
- common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
+ Alloc: types.GenesisAlloc{
+ common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): types.GenesisAccount{
Balance: big.NewInt(1000000000000000000), // 1 ether
Nonce: 0,
},
@@ -309,8 +311,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.GenesisAccount{
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 3bb37897e7..df8c679a7a 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -27,7 +27,6 @@
package core
import (
- "errors"
"fmt"
"math"
"math/big"
@@ -39,6 +38,8 @@ import (
"github.com/ava-labs/coreth/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
@@ -310,7 +311,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 {
@@ -319,7 +324,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
}
@@ -377,13 +383,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)
}
}
}
@@ -456,7 +467,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())
}
@@ -475,14 +490,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.IsApricotPhase1)
- 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(),
@@ -505,7 +526,8 @@ func (st *StateTransition) refundGas(apricotPhase1 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/state_transition_test.go b/core/state_transition_test.go
index 9aae990e2a..8aed625bc1 100644
--- a/core/state_transition_test.go
+++ b/core/state_transition_test.go
@@ -98,8 +98,8 @@ func executeStateTransitionTest(t *testing.T, st stateTransitionTest) {
db = rawdb.NewMemoryDatabase()
gspec = &Genesis{
Config: st.config,
- Alloc: GenesisAlloc{
- common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{
+ Alloc: types.GenesisAlloc{
+ common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): types.GenesisAccount{
Balance: big.NewInt(2000000000000000000), // 2 ether
Nonce: 0,
},
diff --git a/core/test_blockchain.go b/core/test_blockchain.go
index 7c0807b829..41d5657b92 100644
--- a/core/test_blockchain.go
+++ b/core/test_blockchain.go
@@ -18,6 +18,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"
)
@@ -203,7 +204,7 @@ func TestInsertChainAcceptSingleBlock(t *testing.T, create func(db ethdb.Databas
genesisBalance := big.NewInt(1000000)
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{})
if err != nil {
@@ -236,9 +237,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)
}
@@ -272,7 +274,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{})
@@ -403,8 +405,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{})
@@ -494,6 +497,7 @@ func TestAcceptNonCanonicalBlock(t *testing.T, create func(db ethdb.Database, gs
if err := blockchain.Reject(chain1[i]); err != nil {
t.Fatal(err)
}
+ require.False(t, blockchain.HasBlock(chain1[i].Hash(), chain1[i].NumberU64()))
}
lastAcceptedBlock := blockchain.LastConsensusAcceptedBlock()
@@ -512,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)
}
@@ -544,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{})
@@ -606,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)
}
@@ -614,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
@@ -639,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)
}
@@ -676,7 +683,7 @@ func TestBuildOnVariousStages(t *testing.T, create func(db ethdb.Database, gspec
genesisBalance := big.NewInt(1000000)
gspec := &Genesis{
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
- Alloc: GenesisAlloc{
+ Alloc: types.GenesisAlloc{
addr1: {Balance: genesisBalance},
addr3: {Balance: genesisBalance},
},
@@ -795,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)
@@ -812,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)
}
@@ -833,7 +841,7 @@ func TestEmptyBlocks(t *testing.T, create func(db ethdb.Database, gspec *Genesis
// Ensure that key1 has some funds in the genesis block.
gspec := &Genesis{
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
- Alloc: GenesisAlloc{},
+ Alloc: types.GenesisAlloc{},
}
blockchain, err := create(chainDB, gspec, common.Hash{})
@@ -882,7 +890,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}},
}
genesis := gspec.ToBlock()
@@ -943,8 +951,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)
}
@@ -988,7 +997,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{})
@@ -1086,7 +1095,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)
}
@@ -1095,6 +1104,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)
}
@@ -1131,7 +1141,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{})
@@ -1253,7 +1263,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)
}
@@ -1262,6 +1272,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)
}
@@ -1284,7 +1295,7 @@ func TestGenerateChainInvalidBlockFee(t *testing.T, create func(db ethdb.Databas
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{})
@@ -1333,7 +1344,7 @@ func TestInsertChainInvalidBlockFee(t *testing.T, create func(db ethdb.Database,
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{})
@@ -1389,7 +1400,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{})
@@ -1441,9 +1452,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)
- feeSpend := new(big.Int).Mul(new(big.Int).Add(big.NewInt(225*params.GWei), tip), new(big.Int).SetUint64(params.TxGas))
- expectedBalance1.Sub(expectedBalance1, feeSpend)
+ transfer := uint256.MustFromBig(transfer)
+ genesisBalance := uint256.MustFromBig(genesisBalance)
+ expectedBalance1 := new(uint256.Int).Sub(genesisBalance, transfer)
+ baseFee := big.NewInt(225 * params.GWei)
+ feeSpend := new(big.Int).Mul(new(big.Int).Add(baseFee, tip), new(big.Int).SetUint64(params.TxGas))
+ 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..39c5fe51b0
--- /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/coreth/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..5c212bc953
--- /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/coreth/consensus/dummy"
+ "github.com/ava-labs/coreth/core/rawdb"
+ "github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/coreth/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: types.GenesisAlloc{addr1: {Balance: funds}},
+ }
+ signer = types.LatestSigner(gspec.Config)
+ )
+ genDb, blocks, _, err := GenerateChainWithGenesis(gspec, dummy.NewFakerWithCallbacks(TestCallbacks), 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.NewFakerWithCallbacks(TestCallbacks), 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: types.GenesisAlloc{addr1: {Balance: funds}},
+ }
+ signer = types.LatestSigner(gspec.Config)
+ )
+ genDb, blocks, _, err := GenerateChainWithGenesis(gspec, dummy.NewFakerWithCallbacks(TestCallbacks), 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.NewFakerWithCallbacks(TestCallbacks), 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 ad28c69ba4..63d79e4372 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()
@@ -420,7 +422,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))
@@ -438,7 +440,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
@@ -454,7 +456,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)
}
@@ -478,7 +480,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
@@ -489,11 +491,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
@@ -552,15 +560,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 {
@@ -591,6 +601,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)
@@ -605,7 +617,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
@@ -622,10 +634,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 (
@@ -643,6 +675,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)
@@ -654,7 +688,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 {
@@ -679,7 +713,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)
@@ -687,6 +721,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)
@@ -717,6 +753,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)
@@ -733,7 +771,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.
@@ -791,7 +829,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 {
@@ -802,7 +840,7 @@ func (p *BlobPool) Reset(oldHead, newHead *types.Header) {
if p.chain.Config().IsCancun(p.head.Number, p.head.Time) {
p.limbo.finalize(p.chain.CurrentFinalBlock())
}
- _, baseFee, err := dummy.EstimateNextBaseFee(
+ _, baseFeeBig, err := dummy.EstimateNextBaseFee(
p.chain.Config(),
p.head,
uint64(time.Now().Unix()),
@@ -814,7 +852,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 {
@@ -984,7 +1022,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 {
@@ -1051,6 +1089,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)
@@ -1198,7 +1238,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))
@@ -1218,10 +1258,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()
@@ -1235,6 +1275,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
@@ -1242,6 +1298,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() {
@@ -1281,6 +1338,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
@@ -1359,6 +1418,7 @@ func (p *BlobPool) add(tx *types.Transaction) (err error) {
}
p.updateStorageMetrics()
+ addValidMeter.Mark(1)
return nil
}
@@ -1392,7 +1452,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 {
@@ -1408,7 +1468,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)
}
@@ -1416,7 +1478,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
@@ -1426,20 +1496,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,
})
@@ -1451,10 +1541,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 {
@@ -1525,7 +1611,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 b4c4591836..5290153bba 100644
--- a/core/txpool/blobpool/blobpool_test.go
+++ b/core/txpool/blobpool/blobpool_test.go
@@ -61,21 +61,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.
@@ -223,7 +211,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{
@@ -343,7 +331,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)))
@@ -376,7 +373,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()
@@ -395,7 +392,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{})
@@ -408,7 +405,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{})
@@ -420,8 +417,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{})
@@ -438,7 +435,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{})
@@ -457,7 +454,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{})
@@ -479,7 +476,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{})
@@ -497,7 +494,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{})
@@ -519,7 +516,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{})
@@ -534,21 +531,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{
@@ -558,7 +593,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()
@@ -592,6 +627,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{}{}
}
@@ -622,7 +661,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) {
@@ -636,7 +675,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)
@@ -663,7 +702,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{
@@ -673,7 +712,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()
@@ -764,9 +803,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{
@@ -776,7 +815,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()
@@ -845,9 +884,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(params.Ether))
- statedb.AddBalance(addr2, big.NewInt(params.Ether))
- statedb.AddBalance(addr3, big.NewInt(params.Ether))
+ 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{
@@ -857,7 +896,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
@@ -1217,6 +1256,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
@@ -1237,8 +1294,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
@@ -1259,7 +1316,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)
@@ -1277,3 +1334,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 bb00d920e7..63f31031ab 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 6e98de0855..d2d2b7e23c 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 68769933bd..c586cab543 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 3ef445d8c6..f34701da78 100644
--- a/core/txpool/legacypool/legacypool.go
+++ b/core/txpool/legacypool/legacypool.go
@@ -48,6 +48,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 (
@@ -220,7 +221,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
@@ -314,15 +315,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 {
@@ -465,11 +466,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 {
@@ -477,7 +480,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) {
@@ -554,23 +557,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))
@@ -578,9 +591,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
}
@@ -594,8 +607,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(),
}
@@ -658,7 +671,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 d8aaa31644..116e80a707 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/coreth/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 28064a2fd8..4ebbf97c4f 100644
--- a/core/txpool/subpool.go
+++ b/core/txpool/subpool.go
@@ -34,6 +34,7 @@ import (
"github.com/ava-labs/coreth/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 39da21323d..adfc616a82 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
minFee atomic.Pointer[big.Int] // Remember last value set so it can be retrieved (in tests)
@@ -96,7 +99,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.
@@ -106,7 +109,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-- {
@@ -146,7 +153,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 +212,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 +224,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 +253,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 +277,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)
@@ -372,19 +419,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
}
}
@@ -393,13 +433,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)
}
}
@@ -419,9 +458,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...))
}
@@ -519,3 +562,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 56cc1de453..374471e978 100644
--- a/core/txpool/validation.go
+++ b/core/txpool/validation.go
@@ -41,6 +41,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 {
@@ -118,15 +124,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")
@@ -140,6 +148,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
}
@@ -160,17 +169,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
@@ -242,7 +244,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..28a663009e
--- /dev/null
+++ b/core/types/account.go
@@ -0,0 +1,108 @@
+// 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
+//go:generate go run github.com/fjl/gencodec -type GenesisAccount -field-override genesisAccountMarshaling -out gen_genesis_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]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
+}
+
+type GenesisMultiCoinBalance map[common.Hash]*big.Int
+
+// 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"`
+ MCBalance GenesisMultiCoinBalance `json:"mcbalance,omitempty"`
+ Nonce uint64 `json:"nonce,omitempty"`
+ PrivateKey []byte `json:"secretKey,omitempty"` // for tests
+}
+
+type genesisAccountMarshaling struct {
+ Code hexutil.Bytes
+ Balance *math.HexOrDecimal256
+ Nonce math.HexOrDecimal64
+ Storage map[storageJSON]storageJSON
+ PrivateKey hexutil.Bytes
+}
diff --git a/core/types/gen_account.go b/core/types/gen_account.go
new file mode 100644
index 0000000000..4e475896a7
--- /dev/null
+++ b/core/types/gen_account.go
@@ -0,0 +1,73 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package types
+
+import (
+ "encoding/json"
+ "errors"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/common/math"
+)
+
+var _ = (*accountMarshaling)(nil)
+
+// MarshalJSON marshals as JSON.
+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 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)(a.Balance)
+ enc.Nonce = math.HexOrDecimal64(a.Nonce)
+ enc.PrivateKey = a.PrivateKey
+ return json.Marshal(&enc)
+}
+
+// UnmarshalJSON unmarshals from JSON.
+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 Account
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.Code != nil {
+ a.Code = *dec.Code
+ }
+ if dec.Storage != nil {
+ a.Storage = make(map[common.Hash]common.Hash, len(dec.Storage))
+ for k, v := range dec.Storage {
+ a.Storage[common.Hash(k)] = common.Hash(v)
+ }
+ }
+ if dec.Balance == nil {
+ return errors.New("missing required field 'balance' for Account")
+ }
+ a.Balance = (*big.Int)(dec.Balance)
+ if dec.Nonce != nil {
+ a.Nonce = uint64(*dec.Nonce)
+ }
+ if dec.PrivateKey != nil {
+ a.PrivateKey = *dec.PrivateKey
+ }
+ return nil
+}
diff --git a/core/types/gen_account_rlp.go b/core/types/gen_account_rlp.go
index cf0e572336..382bedcd38 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/gen_genesis_account.go b/core/types/gen_genesis_account.go
similarity index 99%
rename from core/gen_genesis_account.go
rename to core/types/gen_genesis_account.go
index a2f503fe57..f4c352ec7e 100644
--- a/core/gen_genesis_account.go
+++ b/core/types/gen_genesis_account.go
@@ -1,6 +1,6 @@
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
-package core
+package types
import (
"encoding/json"
diff --git a/core/types/hashing_test.go b/core/types/hashing_test.go
index 17874cf747..82e17b817f 100644
--- a/core/types/hashing_test.go
+++ b/core/types/hashing_test.go
@@ -37,6 +37,7 @@ import (
"github.com/ava-labs/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/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 54d22c8ba9..9f8a6108a3 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
IsMultiCoin bool
@@ -49,7 +49,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(),
}
@@ -57,9 +57,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,
@@ -75,7 +75,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
IsMultiCoin bool
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..5735c1539d 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/coreth/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 631ddaa511..6769f6ebc8 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_stateful_test.go b/core/vm/contracts_stateful_test.go
index ef3eb24a8e..9ac7652108 100644
--- a/core/vm/contracts_stateful_test.go
+++ b/core/vm/contracts_stateful_test.go
@@ -13,6 +13,7 @@ import (
"github.com/ava-labs/coreth/vmerrs"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
+ "github.com/holiman/uint256"
"github.com/stretchr/testify/assert"
)
@@ -33,7 +34,7 @@ func TestPrecompiledContractSpendsGas(t *testing.T) {
// 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 StateDB, addr common.Address, amount *big.Int) bool {
+func CanTransfer(db StateDB, addr common.Address, amount *uint256.Int) bool {
return db.GetBalance(addr).Cmp(amount) >= 0
}
@@ -43,7 +44,7 @@ func CanTransferMC(db StateDB, addr common.Address, to common.Address, coinID co
}
// Transfer subtracts amount from sender and adds amount to recipient using the given Db
-func Transfer(db StateDB, sender, recipient common.Address, amount *big.Int) {
+func Transfer(db StateDB, sender, recipient common.Address, amount *uint256.Int) {
db.SubBalance(sender, amount)
db.AddBalance(recipient, amount)
}
@@ -85,7 +86,7 @@ func TestStatefulPrecompile(t *testing.T) {
from common.Address
precompileAddr common.Address
input []byte
- value *big.Int
+ value *uint256.Int
gasInput uint64
expectedGasRemaining uint64
expectedErr error
@@ -98,9 +99,11 @@ func TestStatefulPrecompile(t *testing.T) {
userAddr2 := common.BytesToAddress([]byte("user2"))
assetID := common.BytesToHash([]byte("ScoobyCoin"))
zeroBytes := make([]byte, 32)
+ big0.FillBytes(zeroBytes)
+ big0 := uint256.NewInt(0)
bigHundred := big.NewInt(100)
+ u256Hundred := uint256.NewInt(100)
oneHundredBytes := make([]byte, 32)
- big0.FillBytes(zeroBytes)
bigFifty := big.NewInt(50)
fiftyBytes := make([]byte, 32)
bigFifty.FillBytes(fiftyBytes)
@@ -116,7 +119,7 @@ func TestStatefulPrecompile(t *testing.T) {
// Create account
statedb.CreateAccount(userAddr1)
// Set balance to pay for gas fee
- statedb.SetBalance(userAddr1, bigHundred)
+ statedb.SetBalance(userAddr1, u256Hundred)
// Set MultiCoin balance
statedb.Finalise(true)
return statedb
@@ -140,7 +143,7 @@ func TestStatefulPrecompile(t *testing.T) {
// Create account
statedb.CreateAccount(userAddr1)
// Set balance to pay for gas fee
- statedb.SetBalance(userAddr1, bigHundred)
+ statedb.SetBalance(userAddr1, u256Hundred)
// Initialize multicoin balance and set it back to 0
statedb.AddBalanceMultiCoin(userAddr1, assetID, bigHundred)
statedb.SubBalanceMultiCoin(userAddr1, assetID, bigHundred)
@@ -166,7 +169,7 @@ func TestStatefulPrecompile(t *testing.T) {
// Create account
statedb.CreateAccount(userAddr1)
// Set balance to pay for gas fee
- statedb.SetBalance(userAddr1, bigHundred)
+ statedb.SetBalance(userAddr1, u256Hundred)
// Initialize multicoin balance to 100
statedb.AddBalanceMultiCoin(userAddr1, assetID, bigHundred)
statedb.Finalise(true)
@@ -229,7 +232,7 @@ func TestStatefulPrecompile(t *testing.T) {
from: userAddr1,
precompileAddr: NativeAssetBalanceAddr,
input: PackNativeAssetBalanceInput(userAddr1, assetID),
- value: bigHundred,
+ value: u256Hundred,
gasInput: params.AssetBalanceApricot,
expectedGasRemaining: params.AssetBalanceApricot,
expectedErr: vmerrs.ErrInsufficientBalance,
@@ -242,7 +245,7 @@ func TestStatefulPrecompile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- statedb.SetBalance(userAddr1, bigHundred)
+ statedb.SetBalance(userAddr1, u256Hundred)
statedb.SetBalanceMultiCoin(userAddr1, assetID, bigHundred)
statedb.Finalise(true)
return statedb
@@ -263,7 +266,7 @@ func TestStatefulPrecompile(t *testing.T) {
user2AssetBalance := stateDB.GetBalanceMultiCoin(userAddr2, assetID)
expectedBalance := big.NewInt(50)
- assert.Equal(t, bigHundred, user1Balance, "user 1 balance")
+ assert.Equal(t, u256Hundred, user1Balance, "user 1 balance")
assert.Equal(t, big0, user2Balance, "user 2 balance")
assert.Equal(t, expectedBalance, user1AssetBalance, "user 1 asset balance")
assert.Equal(t, expectedBalance, user2AssetBalance, "user 2 asset balance")
@@ -275,7 +278,7 @@ func TestStatefulPrecompile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- statedb.SetBalance(userAddr1, bigHundred)
+ statedb.SetBalance(userAddr1, u256Hundred)
statedb.SetBalanceMultiCoin(userAddr1, assetID, bigHundred)
statedb.Finalise(true)
return statedb
@@ -283,7 +286,7 @@ func TestStatefulPrecompile(t *testing.T) {
from: userAddr1,
precompileAddr: NativeAssetCallAddr,
input: PackNativeAssetCallInput(userAddr2, assetID, big.NewInt(50), nil),
- value: big.NewInt(49),
+ value: uint256.NewInt(49),
gasInput: params.AssetCallApricot + params.CallNewAccountGas,
expectedGasRemaining: 0,
expectedErr: nil,
@@ -297,9 +300,9 @@ func TestStatefulPrecompile(t *testing.T) {
user2AssetBalance := stateDB.GetBalanceMultiCoin(userAddr2, assetID)
expectedBalance := big.NewInt(50)
- assert.Equal(t, big.NewInt(51), user1Balance, "user 1 balance")
+ assert.Equal(t, uint256.NewInt(51), user1Balance, "user 1 balance")
assert.Equal(t, big0, user2Balance, "user 2 balance")
- assert.Equal(t, big.NewInt(49), nativeAssetCallAddrBalance, "native asset call addr balance")
+ assert.Equal(t, uint256.NewInt(49), nativeAssetCallAddrBalance, "native asset call addr balance")
assert.Equal(t, expectedBalance, user1AssetBalance, "user 1 asset balance")
assert.Equal(t, expectedBalance, user2AssetBalance, "user 2 asset balance")
},
@@ -310,7 +313,7 @@ func TestStatefulPrecompile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- statedb.SetBalance(userAddr1, bigHundred)
+ statedb.SetBalance(userAddr1, u256Hundred)
statedb.SetBalanceMultiCoin(userAddr1, assetID, big.NewInt(50))
statedb.Finalise(true)
return statedb
@@ -318,7 +321,7 @@ func TestStatefulPrecompile(t *testing.T) {
from: userAddr1,
precompileAddr: NativeAssetCallAddr,
input: PackNativeAssetCallInput(userAddr2, assetID, big.NewInt(51), nil),
- value: big.NewInt(50),
+ value: uint256.NewInt(50),
gasInput: params.AssetCallApricot,
expectedGasRemaining: 0,
expectedErr: vmerrs.ErrInsufficientBalance,
@@ -342,7 +345,7 @@ func TestStatefulPrecompile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- statedb.SetBalance(userAddr1, big.NewInt(50))
+ statedb.SetBalance(userAddr1, uint256.NewInt(50))
statedb.SetBalanceMultiCoin(userAddr1, assetID, big.NewInt(50))
statedb.Finalise(true)
return statedb
@@ -350,7 +353,7 @@ func TestStatefulPrecompile(t *testing.T) {
from: userAddr1,
precompileAddr: NativeAssetCallAddr,
input: PackNativeAssetCallInput(userAddr2, assetID, big.NewInt(50), nil),
- value: big.NewInt(51),
+ value: uint256.NewInt(51),
gasInput: params.AssetCallApricot,
expectedGasRemaining: params.AssetCallApricot,
expectedErr: vmerrs.ErrInsufficientBalance,
@@ -374,7 +377,7 @@ func TestStatefulPrecompile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- statedb.SetBalance(userAddr1, bigHundred)
+ statedb.SetBalance(userAddr1, u256Hundred)
statedb.SetBalanceMultiCoin(userAddr1, assetID, bigHundred)
statedb.Finalise(true)
return statedb
@@ -382,7 +385,7 @@ func TestStatefulPrecompile(t *testing.T) {
from: userAddr1,
precompileAddr: NativeAssetCallAddr,
input: PackNativeAssetCallInput(userAddr2, assetID, big.NewInt(50), nil),
- value: big.NewInt(50),
+ value: uint256.NewInt(50),
gasInput: params.AssetCallApricot - 1,
expectedGasRemaining: 0,
expectedErr: vmerrs.ErrOutOfGas,
@@ -395,7 +398,7 @@ func TestStatefulPrecompile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- statedb.SetBalance(userAddr1, bigHundred)
+ statedb.SetBalance(userAddr1, u256Hundred)
statedb.SetBalanceMultiCoin(userAddr1, assetID, bigHundred)
statedb.Finalise(true)
return statedb
@@ -403,7 +406,7 @@ func TestStatefulPrecompile(t *testing.T) {
from: userAddr1,
precompileAddr: NativeAssetCallAddr,
input: PackNativeAssetCallInput(userAddr2, assetID, big.NewInt(50), nil),
- value: big.NewInt(50),
+ value: uint256.NewInt(50),
gasInput: params.AssetCallApricot + params.CallNewAccountGas - 1,
expectedGasRemaining: 0,
expectedErr: vmerrs.ErrOutOfGas,
@@ -427,7 +430,7 @@ func TestStatefulPrecompile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- statedb.SetBalance(userAddr1, bigHundred)
+ statedb.SetBalance(userAddr1, u256Hundred)
statedb.SetBalanceMultiCoin(userAddr1, assetID, bigHundred)
statedb.Finalise(true)
return statedb
@@ -435,7 +438,7 @@ func TestStatefulPrecompile(t *testing.T) {
from: userAddr1,
precompileAddr: NativeAssetCallAddr,
input: make([]byte, 24),
- value: big.NewInt(50),
+ value: uint256.NewInt(50),
gasInput: params.AssetCallApricot + params.CallNewAccountGas,
expectedGasRemaining: params.CallNewAccountGas,
expectedErr: vmerrs.ErrExecutionReverted,
@@ -448,7 +451,7 @@ func TestStatefulPrecompile(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- statedb.SetBalance(userAddr1, bigHundred)
+ statedb.SetBalance(userAddr1, u256Hundred)
statedb.SetBalanceMultiCoin(userAddr1, assetID, bigHundred)
statedb.Finalise(true)
return statedb
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 de98b10850..71748718f9 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 9cfc38413d..9c1193927f 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -62,10 +62,10 @@ 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
CanTransferMCFunc func(StateDB, common.Address, common.Address, common.Hash, *big.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)
TransferMCFunc func(StateDB, common.Address, common.Address, common.Hash, *big.Int)
// GetHashFunc returns the n'th block hash in the blockchain
// and is used by the BLOCKHASH EVM op code.
@@ -266,16 +266,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()
@@ -283,14 +280,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)
}
}
@@ -303,13 +300,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)
@@ -350,7 +347,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
}
// This allows the user transfer balance of a specified coinId in addition to a normal Call().
-func (evm *EVM) CallExpert(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int, coinID common.Hash, value2 *big.Int) (ret []byte, leftOverGas uint64, err error) {
+func (evm *EVM) CallExpert(caller ContractRef, addr common.Address, input []byte, gas uint64, value *uint256.Int, coinID common.Hash, value2 *big.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
@@ -388,7 +385,7 @@ func (evm *EVM) CallExpert(caller ContractRef, addr common.Address, input []byte
// Capture the tracer start/end events in debug mode
debug := evm.Config.Tracer != nil
if debug && 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, startTime time.Time) { // Lazy evaluation of the parameters
evm.Config.Tracer.CaptureEnd(ret, startGas-gas, err)
}(gas, time.Now())
@@ -434,7 +431,7 @@ func (evm *EVM) CallExpert(caller ContractRef, addr common.Address, input []byte
//
// 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
@@ -453,7 +450,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)
@@ -498,7 +495,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)
@@ -544,7 +541,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 {
@@ -563,7 +560,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
@@ -593,7 +590,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) {
@@ -640,9 +637,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())
}
}
@@ -692,7 +689,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)
}
@@ -701,7 +698,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)
@@ -750,7 +747,7 @@ func (evm *EVM) NativeAssetCall(caller common.Address, input []byte, suppliedGas
// Send [assetAmount] of [assetID] to [to] address
evm.Context.TransferMultiCoin(evm.StateDB, caller, to, assetID, assetAmount)
- ret, remainingGas, err = evm.Call(AccountRef(caller), to, callData, remainingGas, new(big.Int))
+ ret, remainingGas, err = evm.Call(AccountRef(caller), to, callData, remainingGas, new(uint256.Int))
// 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
diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go
index a6ae777ddc..c6da37c789 100644
--- a/core/vm/gas_table_test.go
+++ b/core/vm/gas_table_test.go
@@ -40,6 +40,7 @@ import (
"github.com/ava-labs/coreth/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.TestCortinaChainConfig, 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 e31282dc2d..912293f367 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -28,6 +28,7 @@ package vm
import (
"errors"
+ "math"
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/vmerrs"
@@ -272,7 +273,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
}
@@ -298,8 +299,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
}
@@ -371,9 +371,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
}
@@ -385,7 +383,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)
@@ -403,7 +401,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())
@@ -609,13 +607,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
@@ -654,13 +647,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()
@@ -693,16 +681,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 {
@@ -737,14 +720,6 @@ func opCallExpert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
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()
- }
var bigVal2 = big0
//TODO: use uint256.Int instead of converting with toBig()
@@ -754,7 +729,7 @@ func opCallExpert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
bigVal2 = value2.ToBig()
}
- ret, returnGas, err := interpreter.evm.CallExpert(scope.Contract, toAddr, args, gas, bigVal, coinID, bigVal2)
+ ret, returnGas, err := interpreter.evm.CallExpert(scope.Contract, toAddr, args, gas, &value, coinID, bigVal2)
if err != nil {
temp.Clear()
} else {
@@ -781,14 +756,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 {
@@ -892,7 +864,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
@@ -908,7 +880,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 1c6c874c37..cf28289974 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 b044b74482..f9bc01ca86 100644
--- a/core/vm/interface.go
+++ b/core/vm/interface.go
@@ -32,15 +32,16 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/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
SubBalanceMultiCoin(common.Address, common.Hash, *big.Int)
AddBalanceMultiCoin(common.Address, common.Hash, *big.Int)
diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
index bbcac5b6a4..3c60b10e9a 100644
--- a/core/vm/interpreter.go
+++ b/core/vm/interpreter.go
@@ -178,7 +178,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 018d7af188..32453ddb79 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/coreth/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_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 5e946590d5..3cc56ef8be 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 c7bb160064..eb76962ef3 100644
--- a/core/vm/runtime/runtime.go
+++ b/core/vm/runtime/runtime.go
@@ -37,6 +37,7 @@ import (
"github.com/ava-labs/coreth/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
@@ -150,7 +151,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
}
@@ -180,7 +181,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
}
@@ -195,7 +196,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)
)
@@ -210,7 +211,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 624da0f71f..2ccf6c160f 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/coreth/eth/tracers/js"
+ "github.com/holiman/uint256"
)
func TestDefaults(t *testing.T) {
@@ -372,12 +373,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 f64466cede..b66c4b5415 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -37,8 +37,8 @@ import (
"github.com/ava-labs/coreth/consensus/dummy"
"github.com/ava-labs/coreth/core"
"github.com/ava-labs/coreth/core/bloombits"
- "github.com/ava-labs/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/state"
+ "github.com/ava-labs/coreth/core/txpool"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/core/vm"
"github.com/ava-labs/coreth/eth/gasprice"
@@ -294,7 +294,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 {
@@ -348,7 +348,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 {
@@ -364,10 +364,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
@@ -375,12 +379,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 c67284fb6c..5ff163664e 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/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/state"
"github.com/ava-labs/coreth/core/types"
- "github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/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{}
diff --git a/eth/backend.go b/eth/backend.go
index 1dbb48fd19..480730d5d3 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/coreth/accounts"
"github.com/ava-labs/coreth/consensus"
- "github.com/ava-labs/coreth/consensus/dummy"
"github.com/ava-labs/coreth/core"
"github.com/ava-labs/coreth/core/bloombits"
"github.com/ava-labs/coreth/core/rawdb"
@@ -127,11 +126,11 @@ func roundUpCacheSize(input int, allocSize int) int {
func New(
stack *node.Node,
config *Config,
- cb dummy.ConsensusCallbacks,
gossiper PushGossiper,
chainDb ethdb.Database,
settings Settings,
lastAcceptedHash common.Hash,
+ engine consensus.Engine,
clock *mockable.Clock,
) (*Ethereum, error) {
if chainDb == nil {
@@ -177,7 +176,7 @@ func New(
chainDb: chainDb,
eventMux: new(event.TypeMux),
accountManager: stack.AccountManager(),
- engine: dummy.NewFakerWithClock(cb, clock),
+ engine: engine,
closeBloomHandler: make(chan struct{}),
networkID: networkID,
etherbase: config.Miner.Etherbase,
@@ -223,7 +222,7 @@ func New(
SnapshotNoBuild: config.SkipSnapshotRebuild,
Preimages: config.Preimages,
AcceptedCacheSize: config.AcceptedCacheSize,
- TxLookupLimit: config.TxLookupLimit,
+ TransactionHistory: config.TransactionHistory,
SkipTxIndexing: config.SkipTxIndexing,
StateHistory: config.StateHistory,
StateScheme: scheme,
@@ -251,7 +250,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
}
@@ -274,6 +273,7 @@ func New(
log.Info("Unprotected transactions allowed")
}
gpoParams := config.GPO
+ gpoParams.MinPrice = new(big.Int).SetUint64(config.TxPool.PriceLimit)
eth.APIBackend.gpo, err = gasprice.NewOracle(eth.APIBackend, gpoParams)
if err != nil {
return nil, err
diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go
index 7f28c81721..e82d73d4e2 100644
--- a/eth/ethconfig/config.go
+++ b/eth/ethconfig/config.go
@@ -157,12 +157,10 @@ type Config struct {
// identical state with the pre-upgrade ruleset.
SkipUpgradeCheck bool
- // TxLookupLimit is the maximum number of blocks from head whose tx indices
+ // TransactionHistory is the maximum number of blocks from head whose tx indices
// are reserved:
// * 0: means no limit
// * N: means N block limit [HEAD-N+1, HEAD] and delete extra indexes
- // Deprecated, use 'TransactionHistory' instead.
- TxLookupLimit uint64 `toml:",omitempty"` // The maximum number of blocks from head whose tx indices are reserved.
TransactionHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose tx indices are reserved.
StateHistory uint64 `toml:",omitempty"` // The maximum number of blocks from head whose state histories are reserved.
@@ -173,6 +171,6 @@ type Config struct {
// SkipTxIndexing skips indexing transactions.
// This is useful for validators that don't need to index transactions.
- // TxLookupLimit can be still used to control unindexing old transactions.
+ // TransactionHistory can be still used to control unindexing old transactions.
SkipTxIndexing bool
}
diff --git a/eth/filters/api.go b/eth/filters/api.go
index c51117968d..2416e91061 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_system.go b/eth/filters/filter_system.go
index 3987826a88..6a312df2ee 100644
--- a/eth/filters/filter_system.go
+++ b/eth/filters/filter_system.go
@@ -520,14 +520,15 @@ func (es *EventSystem) handlePendingLogs(filters filterIndex, ev []*types.Log) {
}
}
-func (es *EventSystem) handleTxsEvent(filters filterIndex, ev core.NewTxsEvent, accepted bool) {
+func (es *EventSystem) handleTxsEvent(filters filterIndex, ev core.NewTxsEvent) {
for _, f := range filters[PendingTransactionsSubscription] {
f.txs <- ev.Txs
}
- if accepted {
- for _, f := range filters[AcceptedTransactionsSubscription] {
- f.txs <- ev.Txs
- }
+}
+
+func (es *EventSystem) handleTxsAcceptedEvent(filters filterIndex, ev core.NewTxsEvent) {
+ for _, f := range filters[AcceptedTransactionsSubscription] {
+ f.txs <- ev.Txs
}
}
@@ -565,7 +566,7 @@ func (es *EventSystem) eventLoop() {
for {
select {
case ev := <-es.txsCh:
- es.handleTxsEvent(index, ev, false)
+ es.handleTxsEvent(index, ev)
case ev := <-es.logsCh:
es.handleLogs(index, ev)
case ev := <-es.logsAcceptedCh:
@@ -579,7 +580,7 @@ func (es *EventSystem) eventLoop() {
case ev := <-es.chainAcceptedCh:
es.handleChainAcceptedEvent(index, ev)
case ev := <-es.txsAcceptedCh:
- es.handleTxsEvent(index, ev, true)
+ es.handleTxsAcceptedEvent(index, ev)
case f := <-es.install:
if f.typ == MinedAndPendingLogsSubscription {
diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go
index b4867ab1a9..4acb7c45c0 100644
--- a/eth/filters/filter_test.go
+++ b/eth/filters/filter_test.go
@@ -42,7 +42,7 @@ import (
"github.com/ava-labs/coreth/core/vm"
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/rpc"
- "github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/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 b4699c07c8..291e806b84 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 521d48e98e..448c2a69db 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 82941c015e..19ea33878d 100644
--- a/eth/gasprice/gasprice_test.go
+++ b/eth/gasprice/gasprice_test.go
@@ -95,7 +95,7 @@ func (b *testBackend) teardown() {
func newTestBackendFakerEngine(t *testing.T, config *params.ChainConfig, numBlocks int, extDataGasUsage *big.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()
@@ -122,7 +122,7 @@ func newTestBackendFakerEngine(t *testing.T, config *params.ChainConfig, numBloc
func newTestBackend(t *testing.T, config *params.ChainConfig, numBlocks int, extDataGasUsage *big.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.NewFakerWithCallbacks(dummy.ConsensusCallbacks{
diff --git a/eth/state_accessor.go b/eth/state_accessor.go
index 60d2849160..15b14b559a 100644
--- a/eth/state_accessor.go
+++ b/eth/state_accessor.go
@@ -39,6 +39,7 @@ import (
"github.com/ava-labs/coreth/core/vm"
"github.com/ava-labs/coreth/eth/tracers"
"github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/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 f596bf2638..605a2495fe 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
}
@@ -1101,10 +1101,18 @@ func overrideConfig(original *params.ChainConfig, override *params.ChainConfig)
copy.DurangoBlockTimestamp = timestamp
canon = false
}
+ if timestamp := override.EtnaTimestamp; timestamp != nil {
+ copy.EtnaTimestamp = timestamp
+ canon = false
+ }
if timestamp := override.CancunTime; timestamp != nil {
copy.CancunTime = timestamp
canon = false
}
+ if timestamp := override.VerkleTime; timestamp != nil {
+ copy.VerkleTime = timestamp
+ canon = false
+ }
return copy, canon
}
diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go
index ecf4741dc6..fcaf16a85a 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.TestBanffChainConfig, // TODO: go-ethereum has not enabled Shanghai yet, so we use Banff here so tests pass.
- 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.TestCortinaChainConfig, // TODO: go-ethereum has not enabled Shanghai yet, so we use Cortina here so tests pass.
- 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 582b5979a9..87c6ff5979 100644
--- a/eth/tracers/internal/tracetest/calltrace_test.go
+++ b/eth/tracers/internal/tracetest/calltrace_test.go
@@ -134,12 +134,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,
@@ -150,19 +145,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)
@@ -234,10 +229,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,
@@ -252,8 +243,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()
@@ -262,8 +257,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)
@@ -271,7 +266,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)
}
}
@@ -380,18 +375,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.GenesisAccount{
Code: tc.code,
},
- origin: core.GenesisAccount{
+ origin: types.GenesisAccount{
Balance: big.NewInt(500000000000000),
},
}, false, rawdb.HashScheme)
- defer triedb.Close()
+ defer state.Close()
- evm := vm.NewEVM(context, txContext, statedb, config, vm.Config{Tracer: tc.tracer})
+ evm := vm.NewEVM(context, txContext, state.StateDB, config, 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 c7e06f6e58..9305bf7150 100644
--- a/eth/tracers/internal/tracetest/flat_calltrace_test.go
+++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go
@@ -95,11 +95,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,
@@ -109,20 +104,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 e84d34f5ef..36d8e35619 100644
--- a/eth/tracers/internal/tracetest/prestate_test.go
+++ b/eth/tracers/internal/tracetest/prestate_test.go
@@ -106,12 +106,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,
CanTransferMC: core.CanTransferMC,
@@ -124,19 +119,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 bb438af159..d19d3c52e2 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
@@ -85,11 +85,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 b0bc34ba0e..7e46c0aa20 100644
--- a/eth/tracers/js/tracer_test.go
+++ b/eth/tracers/js/tracer_test.go
@@ -39,6 +39,7 @@ import (
"github.com/ava-labs/coreth/eth/tracers"
"github.com/ava-labs/coreth/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 2d70cc5f8c..0a2becf947 100644
--- a/eth/tracers/logger/logger_test.go
+++ b/eth/tracers/logger/logger_test.go
@@ -27,6 +27,7 @@ import (
"github.com/ava-labs/coreth/core/vm"
"github.com/ava-labs/coreth/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 65914df1c8..ea2f0f8980 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 5c800646c7..76ed4be967 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 2fe2676d75..e5a2c92047 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.GenesisAccount{
Nonce: 1,
Code: loop,
Balance: big.NewInt(1),
}
- alloc[from] = core.GenesisAccount{
+ alloc[from] = types.GenesisAccount{
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/corethclient/corethclient.go b/ethclient/corethclient/corethclient.go
index f7a5063e75..448b10b6af 100644
--- a/ethclient/corethclient/corethclient.go
+++ b/ethclient/corethclient/corethclient.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/ethclient/ethclient.go b/ethclient/ethclient.go
index 5bcd2a2ce0..5861210aca 100644
--- a/ethclient/ethclient.go
+++ b/ethclient/ethclient.go
@@ -383,10 +383,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..c4a3cb8971
--- /dev/null
+++ b/ethclient/simulated/backend.go
@@ -0,0 +1,278 @@
+// 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/coreth/consensus/dummy"
+ "github.com/ava-labs/coreth/constants"
+ "github.com/ava-labs/coreth/core"
+ "github.com/ava-labs/coreth/core/rawdb"
+ "github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/coreth/eth"
+ "github.com/ava-labs/coreth/eth/ethconfig"
+ "github.com/ava-labs/coreth/ethclient"
+ "github.com/ava-labs/coreth/interfaces"
+ "github.com/ava-labs/coreth/node"
+ "github.com/ava-labs/coreth/params"
+ "github.com/ava-labs/coreth/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,
+ 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..34a4855cfb
--- /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/coreth/accounts/abi/bind"
+ "github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/coreth/params"
+ "github.com/ava-labs/coreth/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..9dc00ca072
--- /dev/null
+++ b/ethclient/simulated/options.go
@@ -0,0 +1,38 @@
+// 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 (
+ "github.com/ava-labs/coreth/eth/ethconfig"
+ "github.com/ava-labs/coreth/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
+ }
+}
+
+// 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..34db36c92a
--- /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/coreth/core"
+ "github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/coreth/interfaces"
+ "github.com/ava-labs/coreth/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() != params.CortinaGasLimit {
+ t.Errorf("head gas limit mismatch: have %v, want %v", head.GasLimit(), params.CortinaGasLimit)
+ }
+}
+
+// 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/go.mod b/go.mod
index d552afc582..6f242937fc 100644
--- a/go.mod
+++ b/go.mod
@@ -4,13 +4,13 @@ go 1.21.12
require (
github.com/VictoriaMetrics/fastcache v1.12.1
- github.com/ava-labs/avalanchego v1.11.11-0.20240814145500-1ac532af76df
+ github.com/ava-labs/avalanchego v1.11.11
github.com/cespare/cp v0.1.0
github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233
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/ethereum/go-ethereum v1.13.14
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
@@ -19,7 +19,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
@@ -38,10 +38,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
@@ -71,7 +71,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/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
@@ -120,8 +120,8 @@ 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/net v0.24.0 // indirect
+ golang.org/x/term v0.19.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 a6c70bd048..de26108af3 100644
--- a/go.sum
+++ b/go.sum
@@ -54,8 +54,8 @@ github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
-github.com/ava-labs/avalanchego v1.11.11-0.20240814145500-1ac532af76df h1:Yp9rCHpgEsPFzpx2MXxpb/T+/NbP2NpS1EDwFquffLQ=
-github.com/ava-labs/avalanchego v1.11.11-0.20240814145500-1ac532af76df/go.mod h1:Kw2GKwTaCkLwq2z3zSVH4V2eiAmq2FohHmN3AIDWjvY=
+github.com/ava-labs/avalanchego v1.11.11 h1:MIQq8xRavRj4ZXHA4G+aMiymig7SOScGOG1SApmMvBc=
+github.com/ava-labs/avalanchego v1.11.11/go.mod h1:yFx3V31Jy9NFa8GZlgGnwiVf8KGjeF2+Uc99l9Scd/8=
github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
@@ -166,8 +166,8 @@ 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/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
@@ -200,8 +200,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=
@@ -313,8 +314,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=
@@ -599,8 +600,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=
@@ -683,8 +684,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=
@@ -706,8 +707,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=
@@ -770,15 +771,16 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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=
diff --git a/interfaces/interfaces.go b/interfaces/interfaces.go
index 2dd3031d81..6e715808c1 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 794e08a8b7..94cffe5a0e 100644
--- a/internal/debug/flags.go
+++ b/internal/debug/flags.go
@@ -173,22 +173,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 3eee4bda66..3b72aeae33 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -56,6 +56,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"
)
@@ -63,6 +64,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
@@ -276,7 +279,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(),
@@ -441,7 +444,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
@@ -460,6 +463,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)
@@ -484,6 +490,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")
}
@@ -512,7 +521,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}
@@ -540,7 +549,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)
@@ -633,7 +642,8 @@ 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()
}
// GetAssetBalance returns the amount of [assetID] for the given address in the state of the
@@ -647,7 +657,7 @@ func (s *BlockChainAPI) GetAssetBalance(ctx context.Context, address common.Addr
return (*hexutil.Big)(state.GetBalanceMultiCoin(address, common.Hash(assetID))), state.Error()
}
-// Result structs for GetProof
+// AccountResult structs for GetProof
type AccountResult struct {
Address common.Address `json:"address"`
AccountProof []string `json:"accountProof"`
@@ -742,10 +752,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,
@@ -970,7 +981,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())
@@ -1082,14 +1094,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
@@ -1214,6 +1226,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 {
@@ -1479,7 +1492,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
}
@@ -1503,14 +1516,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
@@ -1643,51 +1651,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 {
@@ -1810,9 +1816,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
@@ -1829,8 +1838,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
@@ -1893,10 +1904,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.
@@ -1946,7 +1960,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()
@@ -2071,15 +2085,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 b74507c1be..d4dde119a3 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"
@@ -40,6 +42,7 @@ import (
"time"
"github.com/ava-labs/coreth/accounts"
+ "github.com/ava-labs/coreth/accounts/keystore"
"github.com/ava-labs/coreth/consensus"
"github.com/ava-labs/coreth/consensus/dummy"
"github.com/ava-labs/coreth/core"
@@ -51,9 +54,11 @@ import (
"github.com/ava-labs/coreth/internal/blocktest"
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/rpc"
+ "github.com/ava-labs/coreth/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"
@@ -411,9 +416,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 {
@@ -425,6 +450,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.GenesisAccount{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)
@@ -441,7 +468,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
}
@@ -452,7 +479,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 }
@@ -553,14 +580,14 @@ func (b testBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) even
func (b testBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
panic("implement me")
}
-func (b testBackend) GetTransaction(ctx context.Context, txHash common.Hash) (*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) {
@@ -606,7 +633,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)},
},
@@ -621,6 +648,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
@@ -720,6 +748,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)
@@ -744,13 +784,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)},
@@ -765,6 +808,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 {
@@ -886,6 +930,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)
@@ -912,6 +982,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":"0x68c6171400","value":"0x1","input":"0x","accessList":[],"v":"0x1","r":"0x83cf4820cfd50fdd240fea74eaf7227e2e3919507cc567c885a6e6bf27543567","s":"0x899543bbe903a76b705e03cfba217e5cb91fc7204309170c0e91f34988d5fb2","yParity":"0x1","hash":"0x09572232b67f69faab343c5b417c8964b15263c5abbbcdb1ebf7f5eda8ed3a63"}`
+ 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
@@ -1163,7 +1550,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)},
},
@@ -1401,7 +1788,6 @@ func TestRPCGetBlockOrHeader(t *testing.T) {
func setupReceiptBackend(t *testing.T, genBlocks int) (*testBackend, []common.Hash) {
config := *params.TestChainConfig
- // config.ShanghaiTime = new(uint64)
config.CancunTime = new(uint64)
var (
acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
@@ -1413,7 +1799,7 @@ func setupReceiptBackend(t *testing.T, genBlocks int) (*testBackend, []common.Ha
Config: &config,
ExcessBlobGas: new(uint64),
BlobGasUsed: new(uint64),
- Alloc: core.GenesisAlloc{
+ Alloc: types.GenesisAlloc{
acc1Addr: {Balance: big.NewInt(params.Ether)},
acc2Addr: {Balance: big.NewInt(params.Ether)},
// // SPDX-License-Identifier: GPL-3.0
@@ -1433,15 +1819,13 @@ 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
// FullFaker used to skip header verification that enforces no blobs.
backend := newTestBackend(t, genBlocks, genesis, dummy.NewFullFaker(), func(i int, b *core.BlockGen) {
var (
tx *types.Transaction
err error
)
+ // b.SetPoS()
switch i {
case 0:
// transfer 1000wei
@@ -1490,7 +1874,6 @@ func setupReceiptBackend(t *testing.T, genBlocks int) (*testBackend, []common.Ha
b.AddTx(tx)
txHashes[i] = tx.Hash()
}
- // b.SetPoS()
})
return backend, txHashes
}
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index d269db702f..accfda57aa 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -83,7 +83,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 1098fb9334..928dded8b7 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 9c7b28fb93..7e707bf092 100644
--- a/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json
+++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-1.json
@@ -8,17 +8,17 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0xe4e1c0",
"gasUsed": "0x5208",
- "hash": "0x4729ded876444349cc71f0062017ccf35068c2831d27f75c1d35bc4d3eb0c3ba",
+ "hash": "0xcf8a94110956240b890d04288d8ee308e04ca5ce12b442679afa7dfc74c3abf5",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x1",
- "parentHash": "0x1509a989ede83d85f51c9a489ef5f9a1ef15e08ec50f9d569ad41b56ecc4dffd",
+ "parentHash": "0x47f282f6a118c159de5d11a8ad6cc15f2a7bb0eee8e651c94702d9ff413d20b8",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x2df",
- "stateRoot": "0xe6a34faf8c7bd056de278fa20a0bf3236c8c157f6bbf40bced8e5961c51f3691",
+ "stateRoot": "0x060c7f976a708f443cb9b824aeb5b4bb0f3bf35d2c80ada9d329b511cf4f251f",
"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 b69afea8ff..a836bfa4f4 100644
--- a/internal/ethapi/testdata/eth_getBlockByHash-hash-genesis.json
+++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-genesis.json
@@ -6,7 +6,7 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x0",
- "hash": "0x1509a989ede83d85f51c9a489ef5f9a1ef15e08ec50f9d569ad41b56ecc4dffd",
+ "hash": "0x47f282f6a118c159de5d11a8ad6cc15f2a7bb0eee8e651c94702d9ff413d20b8",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
@@ -16,7 +16,7 @@
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x224",
- "stateRoot": "0xb5a65ee2c90afcbaccd940a768b2a719394755b7275bce8a4c0c742991e17131",
+ "stateRoot": "0xb7b3d2030ccceaa351c051a03a77862e839cf96e5eec451c4697232e703b754b",
"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 f073a2b21b..72dede0d90 100644
--- a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json
+++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest-1-fullTx.json
@@ -8,22 +8,22 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0xe4e1c0",
"gasUsed": "0x5208",
- "hash": "0x3556a043faadc4f03ba18453a084ba91d49c6baec50d7792dab1f3f552d2c973",
+ "hash": "0xc2c901ff74a31cb1ca48a2cafc01c51636cef7452aeb759ab8248f529efe7593",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x9",
- "parentHash": "0x2006bc7d2d25edaef78eb0dd7392262bd865a7a0ab4ec70c169ab02eeb3a53f9",
+ "parentHash": "0x800fb44aa072cdae6ae48192a1ce9c147540304b59904ddff593de42e9a9e7d4",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x2df",
- "stateRoot": "0x559db971f1bdcc4a39b612c81c26c3d91ca805d61eb614132352196f03d38192",
+ "stateRoot": "0x7afe30d6f703bbdf57c04120e729a4b1d248ebc81da9bc1cb96b0491a421f15a",
"timestamp": "0x5a",
"totalDifficulty": "0x9",
"transactions": [
{
- "blockHash": "0x3556a043faadc4f03ba18453a084ba91d49c6baec50d7792dab1f3f552d2c973",
+ "blockHash": "0xc2c901ff74a31cb1ca48a2cafc01c51636cef7452aeb759ab8248f529efe7593",
"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 f87d96ee4b..4218cb5ef0 100644
--- a/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json
+++ b/internal/ethapi/testdata/eth_getBlockByHash-hash-latest.json
@@ -8,17 +8,17 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0xe4e1c0",
"gasUsed": "0x5208",
- "hash": "0x9e839b3ad2ecd76f842bb6891144e073f015c785f5aad0001968222334131d02",
+ "hash": "0x27a110dbd61d8291cedc5561f462d0258f860809988bca7eec74abbc7e9c6e75",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0xa",
- "parentHash": "0x3556a043faadc4f03ba18453a084ba91d49c6baec50d7792dab1f3f552d2c973",
+ "parentHash": "0xc2c901ff74a31cb1ca48a2cafc01c51636cef7452aeb759ab8248f529efe7593",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x2df",
- "stateRoot": "0xdded7f848268f119f12c77fdf32520ac3f98d710164997b6adf038e76c5007fe",
+ "stateRoot": "0xa49730b1582776f571728ddb1858e048374f17223524d728274f57afbafd3893",
"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 b69afea8ff..a836bfa4f4 100644
--- a/internal/ethapi/testdata/eth_getBlockByNumber-number-0.json
+++ b/internal/ethapi/testdata/eth_getBlockByNumber-number-0.json
@@ -6,7 +6,7 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x0",
- "hash": "0x1509a989ede83d85f51c9a489ef5f9a1ef15e08ec50f9d569ad41b56ecc4dffd",
+ "hash": "0x47f282f6a118c159de5d11a8ad6cc15f2a7bb0eee8e651c94702d9ff413d20b8",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
@@ -16,7 +16,7 @@
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x224",
- "stateRoot": "0xb5a65ee2c90afcbaccd940a768b2a719394755b7275bce8a4c0c742991e17131",
+ "stateRoot": "0xb7b3d2030ccceaa351c051a03a77862e839cf96e5eec451c4697232e703b754b",
"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 9c7b28fb93..7e707bf092 100644
--- a/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json
+++ b/internal/ethapi/testdata/eth_getBlockByNumber-number-1.json
@@ -8,17 +8,17 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0xe4e1c0",
"gasUsed": "0x5208",
- "hash": "0x4729ded876444349cc71f0062017ccf35068c2831d27f75c1d35bc4d3eb0c3ba",
+ "hash": "0xcf8a94110956240b890d04288d8ee308e04ca5ce12b442679afa7dfc74c3abf5",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x1",
- "parentHash": "0x1509a989ede83d85f51c9a489ef5f9a1ef15e08ec50f9d569ad41b56ecc4dffd",
+ "parentHash": "0x47f282f6a118c159de5d11a8ad6cc15f2a7bb0eee8e651c94702d9ff413d20b8",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x2df",
- "stateRoot": "0xe6a34faf8c7bd056de278fa20a0bf3236c8c157f6bbf40bced8e5961c51f3691",
+ "stateRoot": "0x060c7f976a708f443cb9b824aeb5b4bb0f3bf35d2c80ada9d329b511cf4f251f",
"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 f073a2b21b..72dede0d90 100644
--- a/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json
+++ b/internal/ethapi/testdata/eth_getBlockByNumber-number-latest-1.json
@@ -8,22 +8,22 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0xe4e1c0",
"gasUsed": "0x5208",
- "hash": "0x3556a043faadc4f03ba18453a084ba91d49c6baec50d7792dab1f3f552d2c973",
+ "hash": "0xc2c901ff74a31cb1ca48a2cafc01c51636cef7452aeb759ab8248f529efe7593",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x9",
- "parentHash": "0x2006bc7d2d25edaef78eb0dd7392262bd865a7a0ab4ec70c169ab02eeb3a53f9",
+ "parentHash": "0x800fb44aa072cdae6ae48192a1ce9c147540304b59904ddff593de42e9a9e7d4",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x2df",
- "stateRoot": "0x559db971f1bdcc4a39b612c81c26c3d91ca805d61eb614132352196f03d38192",
+ "stateRoot": "0x7afe30d6f703bbdf57c04120e729a4b1d248ebc81da9bc1cb96b0491a421f15a",
"timestamp": "0x5a",
"totalDifficulty": "0x9",
"transactions": [
{
- "blockHash": "0x3556a043faadc4f03ba18453a084ba91d49c6baec50d7792dab1f3f552d2c973",
+ "blockHash": "0xc2c901ff74a31cb1ca48a2cafc01c51636cef7452aeb759ab8248f529efe7593",
"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 f87d96ee4b..4218cb5ef0 100644
--- a/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json
+++ b/internal/ethapi/testdata/eth_getBlockByNumber-tag-latest.json
@@ -8,17 +8,17 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0xe4e1c0",
"gasUsed": "0x5208",
- "hash": "0x9e839b3ad2ecd76f842bb6891144e073f015c785f5aad0001968222334131d02",
+ "hash": "0x27a110dbd61d8291cedc5561f462d0258f860809988bca7eec74abbc7e9c6e75",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0xa",
- "parentHash": "0x3556a043faadc4f03ba18453a084ba91d49c6baec50d7792dab1f3f552d2c973",
+ "parentHash": "0xc2c901ff74a31cb1ca48a2cafc01c51636cef7452aeb759ab8248f529efe7593",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x2df",
- "stateRoot": "0xdded7f848268f119f12c77fdf32520ac3f98d710164997b6adf038e76c5007fe",
+ "stateRoot": "0xa49730b1582776f571728ddb1858e048374f17223524d728274f57afbafd3893",
"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 4662ebc06c..f4da042d07 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": "0x1aa2bdf0b0357f6932543b62d8a4528e525bba89e9f250f6856a446b0f28ac8b",
+ "blockHash": "0x0415a36cd8c5f7531e835ebd048b31a2498307ff628a5360f1ea32cd505eca3c",
"blockNumber": "0x6",
"contractAddress": null,
"cumulativeGasUsed": "0x5208",
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 43b480eb89..a2a0e2dc97 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": "0xe94440d64ef293387fd5245d15240c1b72789db4005502c62f03c5e29d92ed60",
+ "blockHash": "0x536da302c391777506cb8eec39a41cef7425049c0de0e0def83f0932435c0f15",
"blockNumber": "0x2",
"contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592",
"cumulativeGasUsed": "0xcf50",
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 8dc9346333..dbe0a314be 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": "0x1834358f0a607f09a35cf1448b968a9e0255fcd661f4f1f8d5b12016ba9a1069",
+ "blockHash": "0x7efe824a4276eab14dfb5bbb617954582efad47350a962c795bb156ad61a2aea",
"blockNumber": "0x4",
"contractAddress": null,
"cumulativeGasUsed": "0x538d",
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 90fa760aa6..4f8b9567c7 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": "0x69086f9c85b303fc69154df16f34513f093f4937c6e3f6d8cbcf27a7617f8ba4",
+ "blockHash": "0xe9a180ae60cf1ecd8e7495c14539cd68f24903cf66cec6708b81f58a57597a96",
"blockNumber": "0x3",
"contractAddress": null,
"cumulativeGasUsed": "0x5e28",
@@ -19,7 +19,7 @@
"blockNumber": "0x3",
"transactionHash": "0x0e9c460065fee166157eaadf702a01fb6ac1ce27b651e32850a8b09f71f93937",
"transactionIndex": "0x0",
- "blockHash": "0x69086f9c85b303fc69154df16f34513f093f4937c6e3f6d8cbcf27a7617f8ba4",
+ "blockHash": "0xe9a180ae60cf1ecd8e7495c14539cd68f24903cf66cec6708b81f58a57597a96",
"logIndex": "0x0",
"removed": false
}
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 39c5e5727e..aee6d37182 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": "0xb3839c504dc43f5cf0839a1a26f01c1a0e5da1a6497d3f280467931690274467",
+ "blockHash": "0x7f8cb96ca1d10b696b2a6c81765073c0f1a285583eb3257723315d59fbe99943",
"blockNumber": "0x1",
"contractAddress": null,
"cumulativeGasUsed": "0x5208",
diff --git a/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json b/internal/ethapi/testdata/eth_getBlockReceipts-tag-latest.json
index 4662ebc06c..f4da042d07 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": "0x1aa2bdf0b0357f6932543b62d8a4528e525bba89e9f250f6856a446b0f28ac8b",
+ "blockHash": "0x0415a36cd8c5f7531e835ebd048b31a2498307ff628a5360f1ea32cd505eca3c",
"blockNumber": "0x6",
"contractAddress": null,
"cumulativeGasUsed": "0x5208",
diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-0.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-0.json
index 272f5135e6..bc7d26c3be 100644
--- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-0.json
+++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-0.json
@@ -5,7 +5,7 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x0",
- "hash": "0x1509a989ede83d85f51c9a489ef5f9a1ef15e08ec50f9d569ad41b56ecc4dffd",
+ "hash": "0x47f282f6a118c159de5d11a8ad6cc15f2a7bb0eee8e651c94702d9ff413d20b8",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
@@ -14,7 +14,7 @@
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0xb5a65ee2c90afcbaccd940a768b2a719394755b7275bce8a4c0c742991e17131",
+ "stateRoot": "0xb7b3d2030ccceaa351c051a03a77862e839cf96e5eec451c4697232e703b754b",
"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 ca2df164d2..4423eb7ee6 100644
--- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json
+++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-1.json
@@ -7,16 +7,16 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0xe4e1c0",
"gasUsed": "0x5208",
- "hash": "0x4729ded876444349cc71f0062017ccf35068c2831d27f75c1d35bc4d3eb0c3ba",
+ "hash": "0xcf8a94110956240b890d04288d8ee308e04ca5ce12b442679afa7dfc74c3abf5",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x1",
- "parentHash": "0x1509a989ede83d85f51c9a489ef5f9a1ef15e08ec50f9d569ad41b56ecc4dffd",
+ "parentHash": "0x47f282f6a118c159de5d11a8ad6cc15f2a7bb0eee8e651c94702d9ff413d20b8",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0xe6a34faf8c7bd056de278fa20a0bf3236c8c157f6bbf40bced8e5961c51f3691",
+ "stateRoot": "0x060c7f976a708f443cb9b824aeb5b4bb0f3bf35d2c80ada9d329b511cf4f251f",
"timestamp": "0xa",
"totalDifficulty": "0x1",
"transactionsRoot": "0x272d13afea9f2f2c9b9ab3d8bbdb492ce5f7b215c493adaac5d98abc9ad62352"
diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json
index 8f6f248043..091cb98bbd 100644
--- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json
+++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest-1.json
@@ -7,16 +7,16 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0xe4e1c0",
"gasUsed": "0x5208",
- "hash": "0x3556a043faadc4f03ba18453a084ba91d49c6baec50d7792dab1f3f552d2c973",
+ "hash": "0xc2c901ff74a31cb1ca48a2cafc01c51636cef7452aeb759ab8248f529efe7593",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x9",
- "parentHash": "0x2006bc7d2d25edaef78eb0dd7392262bd865a7a0ab4ec70c169ab02eeb3a53f9",
+ "parentHash": "0x800fb44aa072cdae6ae48192a1ce9c147540304b59904ddff593de42e9a9e7d4",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0x559db971f1bdcc4a39b612c81c26c3d91ca805d61eb614132352196f03d38192",
+ "stateRoot": "0x7afe30d6f703bbdf57c04120e729a4b1d248ebc81da9bc1cb96b0491a421f15a",
"timestamp": "0x5a",
"totalDifficulty": "0x9",
"transactionsRoot": "0x636f1c9b3e00b425fbab75c79989315b6f30c48f5a55ee636b1fc3805538c5f7"
diff --git a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json
index a7da76acf3..62dcf2a711 100644
--- a/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json
+++ b/internal/ethapi/testdata/eth_getHeaderByHash-hash-latest.json
@@ -7,16 +7,16 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0xe4e1c0",
"gasUsed": "0x5208",
- "hash": "0x9e839b3ad2ecd76f842bb6891144e073f015c785f5aad0001968222334131d02",
+ "hash": "0x27a110dbd61d8291cedc5561f462d0258f860809988bca7eec74abbc7e9c6e75",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0xa",
- "parentHash": "0x3556a043faadc4f03ba18453a084ba91d49c6baec50d7792dab1f3f552d2c973",
+ "parentHash": "0xc2c901ff74a31cb1ca48a2cafc01c51636cef7452aeb759ab8248f529efe7593",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0xdded7f848268f119f12c77fdf32520ac3f98d710164997b6adf038e76c5007fe",
+ "stateRoot": "0xa49730b1582776f571728ddb1858e048374f17223524d728274f57afbafd3893",
"timestamp": "0x64",
"totalDifficulty": "0xa",
"transactionsRoot": "0xf578b0855e1c4509f5248387fcc5d3144552ade53be98825df0884f36fbc3ab9"
diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-number-0.json b/internal/ethapi/testdata/eth_getHeaderByNumber-number-0.json
index 272f5135e6..bc7d26c3be 100644
--- a/internal/ethapi/testdata/eth_getHeaderByNumber-number-0.json
+++ b/internal/ethapi/testdata/eth_getHeaderByNumber-number-0.json
@@ -5,7 +5,7 @@
"extraData": "0x",
"gasLimit": "0x47e7c4",
"gasUsed": "0x0",
- "hash": "0x1509a989ede83d85f51c9a489ef5f9a1ef15e08ec50f9d569ad41b56ecc4dffd",
+ "hash": "0x47f282f6a118c159de5d11a8ad6cc15f2a7bb0eee8e651c94702d9ff413d20b8",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
@@ -14,7 +14,7 @@
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptsRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0xb5a65ee2c90afcbaccd940a768b2a719394755b7275bce8a4c0c742991e17131",
+ "stateRoot": "0xb7b3d2030ccceaa351c051a03a77862e839cf96e5eec451c4697232e703b754b",
"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 ca2df164d2..4423eb7ee6 100644
--- a/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json
+++ b/internal/ethapi/testdata/eth_getHeaderByNumber-number-1.json
@@ -7,16 +7,16 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0xe4e1c0",
"gasUsed": "0x5208",
- "hash": "0x4729ded876444349cc71f0062017ccf35068c2831d27f75c1d35bc4d3eb0c3ba",
+ "hash": "0xcf8a94110956240b890d04288d8ee308e04ca5ce12b442679afa7dfc74c3abf5",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x1",
- "parentHash": "0x1509a989ede83d85f51c9a489ef5f9a1ef15e08ec50f9d569ad41b56ecc4dffd",
+ "parentHash": "0x47f282f6a118c159de5d11a8ad6cc15f2a7bb0eee8e651c94702d9ff413d20b8",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0xe6a34faf8c7bd056de278fa20a0bf3236c8c157f6bbf40bced8e5961c51f3691",
+ "stateRoot": "0x060c7f976a708f443cb9b824aeb5b4bb0f3bf35d2c80ada9d329b511cf4f251f",
"timestamp": "0xa",
"totalDifficulty": "0x1",
"transactionsRoot": "0x272d13afea9f2f2c9b9ab3d8bbdb492ce5f7b215c493adaac5d98abc9ad62352"
diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json b/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json
index 8f6f248043..091cb98bbd 100644
--- a/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json
+++ b/internal/ethapi/testdata/eth_getHeaderByNumber-number-latest-1.json
@@ -7,16 +7,16 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0xe4e1c0",
"gasUsed": "0x5208",
- "hash": "0x3556a043faadc4f03ba18453a084ba91d49c6baec50d7792dab1f3f552d2c973",
+ "hash": "0xc2c901ff74a31cb1ca48a2cafc01c51636cef7452aeb759ab8248f529efe7593",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0x9",
- "parentHash": "0x2006bc7d2d25edaef78eb0dd7392262bd865a7a0ab4ec70c169ab02eeb3a53f9",
+ "parentHash": "0x800fb44aa072cdae6ae48192a1ce9c147540304b59904ddff593de42e9a9e7d4",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0x559db971f1bdcc4a39b612c81c26c3d91ca805d61eb614132352196f03d38192",
+ "stateRoot": "0x7afe30d6f703bbdf57c04120e729a4b1d248ebc81da9bc1cb96b0491a421f15a",
"timestamp": "0x5a",
"totalDifficulty": "0x9",
"transactionsRoot": "0x636f1c9b3e00b425fbab75c79989315b6f30c48f5a55ee636b1fc3805538c5f7"
diff --git a/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json b/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json
index a7da76acf3..62dcf2a711 100644
--- a/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json
+++ b/internal/ethapi/testdata/eth_getHeaderByNumber-tag-latest.json
@@ -7,16 +7,16 @@
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0xe4e1c0",
"gasUsed": "0x5208",
- "hash": "0x9e839b3ad2ecd76f842bb6891144e073f015c785f5aad0001968222334131d02",
+ "hash": "0x27a110dbd61d8291cedc5561f462d0258f860809988bca7eec74abbc7e9c6e75",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"miner": "0x0000000000000000000000000000000000000000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce": "0x0000000000000000",
"number": "0xa",
- "parentHash": "0x3556a043faadc4f03ba18453a084ba91d49c6baec50d7792dab1f3f552d2c973",
+ "parentHash": "0xc2c901ff74a31cb1ca48a2cafc01c51636cef7452aeb759ab8248f529efe7593",
"receiptsRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
- "stateRoot": "0xdded7f848268f119f12c77fdf32520ac3f98d710164997b6adf038e76c5007fe",
+ "stateRoot": "0xa49730b1582776f571728ddb1858e048374f17223524d728274f57afbafd3893",
"timestamp": "0x64",
"totalDifficulty": "0xa",
"transactionsRoot": "0xf578b0855e1c4509f5248387fcc5d3144552ade53be98825df0884f36fbc3ab9"
diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-blob-tx.json
index 2a0bbf49aa..98a5bc0cee 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": "0x1aa2bdf0b0357f6932543b62d8a4528e525bba89e9f250f6856a446b0f28ac8b",
+ "blockHash": "0x0415a36cd8c5f7531e835ebd048b31a2498307ff628a5360f1ea32cd505eca3c",
"blockNumber": "0x6",
"contractAddress": null,
"cumulativeGasUsed": "0x5208",
diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-create-contract-tx.json
index 7d8c23adc3..60924bb834 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": "0xe94440d64ef293387fd5245d15240c1b72789db4005502c62f03c5e29d92ed60",
+ "blockHash": "0x536da302c391777506cb8eec39a41cef7425049c0de0e0def83f0932435c0f15",
"blockNumber": "0x2",
"contractAddress": "0xae9bea628c4ce503dcfd7e305cab4e29e7476592",
"cumulativeGasUsed": "0xcf50",
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 b536c819a3..3c7cc536ce 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": "0xd8c23617a84076fe296ee742a3abdadc3d6e93c496bf95751b687ffeb54d09c1",
+ "blockHash": "0xc9352f0417114d8858cda8c8f2dff3796188519302d619df010fd683ea39594b",
"blockNumber": "0x5",
"contractAddress": "0xfdaa97661a584d977b4d3abb5370766ff5b86a18",
"cumulativeGasUsed": "0xe01c",
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 00d14795b6..fc09e7aee9 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": "0x1834358f0a607f09a35cf1448b968a9e0255fcd661f4f1f8d5b12016ba9a1069",
+ "blockHash": "0x7efe824a4276eab14dfb5bbb617954582efad47350a962c795bb156ad61a2aea",
"blockNumber": "0x4",
"contractAddress": null,
"cumulativeGasUsed": "0x538d",
diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json b/internal/ethapi/testdata/eth_getTransactionReceipt-normal-transfer-tx.json
index 1f676d4280..9bab6ca803 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": "0xb3839c504dc43f5cf0839a1a26f01c1a0e5da1a6497d3f280467931690274467",
+ "blockHash": "0x7f8cb96ca1d10b696b2a6c81765073c0f1a285583eb3257723315d59fbe99943",
"blockNumber": "0x1",
"contractAddress": null,
"cumulativeGasUsed": "0x5208",
diff --git a/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json b/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json
index d3a6b70f46..2f9b4c152f 100644
--- a/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json
+++ b/internal/ethapi/testdata/eth_getTransactionReceipt-with-logs.json
@@ -1,5 +1,5 @@
{
- "blockHash": "0x69086f9c85b303fc69154df16f34513f093f4937c6e3f6d8cbcf27a7617f8ba4",
+ "blockHash": "0xe9a180ae60cf1ecd8e7495c14539cd68f24903cf66cec6708b81f58a57597a96",
"blockNumber": "0x3",
"contractAddress": null,
"cumulativeGasUsed": "0x5e28",
@@ -18,7 +18,7 @@
"blockNumber": "0x3",
"transactionHash": "0x0e9c460065fee166157eaadf702a01fb6ac1ce27b651e32850a8b09f71f93937",
"transactionIndex": "0x0",
- "blockHash": "0x69086f9c85b303fc69154df16f34513f093f4937c6e3f6d8cbcf27a7617f8ba4",
+ "blockHash": "0xe9a180ae60cf1ecd8e7495c14539cd68f24903cf66cec6708b81f58a57597a96",
"logIndex": "0x0",
"removed": false
}
diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go
index 77d4716575..f253a41bdf 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/coreth/consensus/misc/eip4844"
"github.com/ava-labs/coreth/core"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/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`)
+ }
+ }
+
+ 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)
}
- 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().IsApricotPhase3(head.Time)
if args.GasPrice != nil && !eip1559ParamsSet {
// Zero gas-price is not allowed after London fork
@@ -191,13 +248,31 @@ func (args *TransactionArgs) setFeeDefaults(ctx context.Context, b feeBackend) e
if args.MaxFeePerGas != nil || args.MaxPriorityFeePerGas != nil {
return errors.New("maxFeePerGas and maxPriorityFeePerGas are not valid before London is active")
}
- if args.GasPrice == nil {
- price, err := b.SuggestGasTipCap(ctx)
- if err != nil {
- return err
- }
- args.GasPrice = (*hexutil.Big)(price)
+ // London not active, set gas price.
+ price, err := b.SuggestGasTipCap(ctx)
+ if err != nil {
+ return err
}
+ args.GasPrice = (*hexutil.Big)(price)
+ }
+ return nil
+}
+
+// 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
}
@@ -217,11 +292,11 @@ func (args *TransactionArgs) setApricotPhase3FeeDefault(ctx context.Context, hea
// Set the max fee to be 2 times larger than the previous block's base fee.
// The additional slack allows the tx to not become invalidated if the base
// fee is rising.
- gasFeeCap := new(big.Int).Add(
- (*big.Int)(args.MaxPriorityFeePerGas),
+ val := new(big.Int).Add(
+ args.MaxPriorityFeePerGas.ToInt(),
new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
)
- args.MaxFeePerGas = (*hexutil.Big)(gasFeeCap)
+ args.MaxFeePerGas = (*hexutil.Big)(val)
}
// Both EIP-1559 fee parameters are now set; sanity check them.
if args.MaxFeePerGas.ToInt().Cmp(args.MaxPriorityFeePerGas.ToInt()) < 0 {
@@ -230,6 +305,81 @@ func (args *TransactionArgs) setApricotPhase3FeeDefault(ctx context.Context, hea
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.
@@ -254,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
@@ -288,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()
@@ -307,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
@@ -317,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 {
@@ -333,6 +517,7 @@ func (args *TransactionArgs) toTransaction() *types.Transaction {
Data: args.data(),
AccessList: al,
}
+
case args.AccessList != nil:
data = &types.AccessListTx{
To: args.To,
@@ -344,6 +529,7 @@ func (args *TransactionArgs) toTransaction() *types.Transaction {
Data: args.data(),
AccessList: *args.AccessList,
}
+
default:
data = &types.LegacyTx{
To: args.To,
@@ -357,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 97d1afc77a..7f2b0661fd 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),
@@ -250,8 +276,9 @@ func newBackendMock() *backendMock {
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
MuirGlacierBlock: big.NewInt(0),
+ CancunTime: &cancunTime,
NetworkUpgrades: params.NetworkUpgrades{
- ApricotPhase3BlockTimestamp: utils.NewUint64(1000),
+ ApricotPhase3BlockTimestamp: utils.NewUint64(100),
},
}
return &backendMock{
@@ -268,13 +295,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 ApricotPhase3BlockTimestamp
+ } else if fork == "london" {
+ b.current.Time = uint64(110) // After ApricotPhase3BlockTimestamp
+ } 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/helpers.go b/internal/flags/helpers.go
index 0839450db8..9a520e739a 100644
--- a/internal/flags/helpers.go
+++ b/internal/flags/helpers.go
@@ -49,7 +49,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
@@ -123,7 +123,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/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/metrics/sample_test.go b/metrics/sample_test.go
index 5122a132c1..7967357055 100644
--- a/metrics/sample_test.go
+++ b/metrics/sample_test.go
@@ -3,7 +3,6 @@ package metrics
import (
"math"
"math/rand"
- "os"
"runtime"
"testing"
"time"
@@ -133,9 +132,6 @@ func TestExpDecaySample(t *testing.T) {
// The priority becomes +Inf quickly after starting if this is done,
// effectively freezing the set of samples until a rescale step happens.
func TestExpDecaySampleNanosecondRegression(t *testing.T) {
- if os.Getenv("RUN_FLAKY_TESTS") != "true" {
- t.Skip("FLAKY")
- }
sw := NewExpDecaySample(100, 0.99)
for i := 0; i < 100; i++ {
sw.Update(10)
diff --git a/miner/miner.go b/miner/miner.go
index b6d02fedf4..512b99c1bb 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 e9c80bca9e..07fa390e4b 100644
--- a/miner/ordering.go
+++ b/miner/ordering.go
@@ -33,26 +33,29 @@ import (
"github.com/ava-labs/coreth/core/txpool"
"github.com/ava-labs/coreth/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 3ff34418a3..27d6b96761 100644
--- a/miner/ordering_test.go
+++ b/miner/ordering_test.go
@@ -37,6 +37,7 @@ import (
"github.com/ava-labs/coreth/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 9f617a454f..5bd709ebb9 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 (
@@ -216,25 +217,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)
@@ -327,15 +350,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
}
@@ -422,7 +477,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 da47cf8282..aa6e3406f4 100644
--- a/params/config.go
+++ b/params/config.go
@@ -502,8 +502,8 @@ type ChainConfig struct {
CancunTime *uint64 `json:"cancunTime,omitempty"` // Cancun switch time (nil = no fork, 0 = already activated)
VerkleTime *uint64 `json:"verkleTime,omitempty"` // Verkle switch time (nil = no fork, 0 = already on verkle)
- // Avalanche Network Upgrades
- NetworkUpgrades
+
+ NetworkUpgrades // Config for timestamps that enable network upgrades. Skip encoding/decoding directly into ChainConfig.
AvalancheContext `json:"-"` // Avalanche specific context set during VM initialization. Not serialized.
@@ -537,7 +537,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 += "\n"
banner += "Avalanche Upgrades (timestamp based):\n"
@@ -913,6 +914,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
diff --git a/params/protocol_params.go b/params/protocol_params.go
index 4876b573e1..a09de7025c 100644
--- a/params/protocol_params.go
+++ b/params/protocol_params.go
@@ -174,7 +174,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 13962017fc..32c2b63bf6 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/atomic_syncer_test.go b/plugin/evm/atomic_syncer_test.go
index d96d7027a3..7540be1a32 100644
--- a/plugin/evm/atomic_syncer_test.go
+++ b/plugin/evm/atomic_syncer_test.go
@@ -23,6 +23,7 @@ import (
handlerstats "github.com/ava-labs/coreth/sync/handlers/stats"
"github.com/ava-labs/coreth/sync/syncutils"
"github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/triedb"
"github.com/ethereum/go-ethereum/common"
)
@@ -37,7 +38,7 @@ type atomicSyncTestCheckpoint struct {
// testAtomicSyncer creates a leaf handler with [serverTrieDB] and tests to ensure that the atomic syncer can sync correctly
// starting at [targetRoot], and stopping and resuming at each of the [checkpoints].
-func testAtomicSyncer(t *testing.T, serverTrieDB *trie.Database, targetHeight uint64, targetRoot common.Hash, checkpoints []atomicSyncTestCheckpoint, finalExpectedNumLeaves int64) {
+func testAtomicSyncer(t *testing.T, serverTrieDB *triedb.Database, targetHeight uint64, targetRoot common.Hash, checkpoints []atomicSyncTestCheckpoint, finalExpectedNumLeaves int64) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@@ -116,7 +117,7 @@ func testAtomicSyncer(t *testing.T, serverTrieDB *trie.Database, targetHeight ui
syncutils.AssertTrieConsistency(t, targetRoot, serverTrieDB, clientTrieDB, nil)
// check all commit heights are created correctly
- hasher := trie.NewEmpty(trie.NewDatabase(rawdb.NewMemoryDatabase(), nil))
+ hasher := trie.NewEmpty(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil))
assert.NoError(t, err)
serverTrie, err := trie.New(trie.TrieID(targetRoot), serverTrieDB)
@@ -153,7 +154,7 @@ func testAtomicSyncer(t *testing.T, serverTrieDB *trie.Database, targetHeight ui
func TestAtomicSyncer(t *testing.T) {
rand.Seed(1)
targetHeight := 10 * uint64(commitInterval)
- serverTrieDB := trie.NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ serverTrieDB := triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)
root, _, _ := syncutils.GenerateTrie(t, serverTrieDB, int(targetHeight), atomicKeyLength)
testAtomicSyncer(t, serverTrieDB, targetHeight, root, nil, int64(targetHeight))
@@ -162,7 +163,7 @@ func TestAtomicSyncer(t *testing.T) {
func TestAtomicSyncerResume(t *testing.T) {
rand.Seed(1)
targetHeight := 10 * uint64(commitInterval)
- serverTrieDB := trie.NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ serverTrieDB := triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)
numTrieKeys := int(targetHeight) - 1 // no atomic ops for genesis
root, _, _ := syncutils.GenerateTrie(t, serverTrieDB, numTrieKeys, atomicKeyLength)
@@ -179,7 +180,7 @@ func TestAtomicSyncerResume(t *testing.T) {
func TestAtomicSyncerResumeNewRootCheckpoint(t *testing.T) {
rand.Seed(1)
targetHeight1 := 10 * uint64(commitInterval)
- serverTrieDB := trie.NewDatabase(rawdb.NewMemoryDatabase(), nil)
+ serverTrieDB := triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil)
numTrieKeys1 := int(targetHeight1) - 1 // no atomic ops for genesis
root1, _, _ := syncutils.GenerateTrie(t, serverTrieDB, numTrieKeys1, atomicKeyLength)
diff --git a/plugin/evm/atomic_trie.go b/plugin/evm/atomic_trie.go
index 2e03a05ede..2760850d18 100644
--- a/plugin/evm/atomic_trie.go
+++ b/plugin/evm/atomic_trie.go
@@ -18,8 +18,9 @@ import (
"github.com/ava-labs/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/trie"
- "github.com/ava-labs/coreth/trie/triedb/hashdb"
"github.com/ava-labs/coreth/trie/trienode"
+ "github.com/ava-labs/coreth/triedb"
+ "github.com/ava-labs/coreth/triedb/hashdb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
@@ -61,7 +62,7 @@ type AtomicTrie interface {
LastCommitted() (common.Hash, uint64)
// TrieDB returns the underlying trie database
- TrieDB() *trie.Database
+ TrieDB() *triedb.Database
// Root returns hash if it exists at specified height
// if trie was not committed at provided height, it returns
@@ -117,7 +118,7 @@ type AtomicTrieIterator interface {
type atomicTrie struct {
commitInterval uint64 // commit interval, same as commitHeightInterval by default
metadataDB database.Database // Underlying database containing the atomic trie metadata
- trieDB *trie.Database // Trie database
+ trieDB *triedb.Database // Trie database
lastCommittedRoot common.Hash // trie root of the most recent commit
lastCommittedHeight uint64 // index height of the most recent commit
lastAcceptedRoot common.Hash // most recent trie root passed to accept trie or the root of the atomic trie on intialization.
@@ -150,9 +151,9 @@ func newAtomicTrie(
}
}
- trieDB := trie.NewDatabase(
+ trieDB := triedb.NewDatabase(
rawdb.NewDatabase(Database{atomicTrieDB}),
- &trie.Config{
+ &triedb.Config{
HashDB: &hashdb.Config{
CleanCacheSize: 64 * units.MiB, // Allocate 64MB of memory for clean cache
},
@@ -282,7 +283,7 @@ func (a *atomicTrie) Iterator(root common.Hash, cursor []byte) (AtomicTrieIterat
return NewAtomicTrieIterator(iter, a.codec), iter.Err
}
-func (a *atomicTrie) TrieDB() *trie.Database {
+func (a *atomicTrie) TrieDB() *triedb.Database {
return a.trieDB
}
diff --git a/plugin/evm/block_builder.go b/plugin/evm/block_builder.go
index d59015cffb..d8a1d07024 100644
--- a/plugin/evm/block_builder.go
+++ b/plugin/evm/block_builder.go
@@ -11,6 +11,7 @@ import (
"github.com/ava-labs/coreth/core"
"github.com/ava-labs/coreth/core/txpool"
"github.com/ava-labs/coreth/params"
+ "github.com/holiman/uint256"
"github.com/ava-labs/avalanchego/snow"
commonEng "github.com/ava-labs/avalanchego/snow/engine/common"
@@ -100,7 +101,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 || b.mempool.Len() > 0
}
diff --git a/plugin/evm/config.go b/plugin/evm/config.go
index 9aad072f39..c0fa3b0386 100644
--- a/plugin/evm/config.go
+++ b/plugin/evm/config.go
@@ -218,6 +218,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/config_test.go b/plugin/evm/config_test.go
index e6f17283b4..9a8384bf5d 100644
--- a/plugin/evm/config_test.go
+++ b/plugin/evm/config_test.go
@@ -78,33 +78,39 @@ func TestUnmarshalConfig(t *testing.T) {
false,
},
{
- "empty tx lookup limit",
+ "empty transaction history ",
[]byte(`{}`),
- Config{TxLookupLimit: 0},
+ Config{TransactionHistory: 0},
false,
},
{
- "zero tx lookup limit",
- []byte(`{"tx-lookup-limit": 0}`),
+ "zero transaction history",
+ []byte(`{"transaction-history": 0}`),
func() Config {
- return Config{TxLookupLimit: 0}
+ return Config{TransactionHistory: 0}
}(),
false,
},
{
- "1 tx lookup limit",
- []byte(`{"tx-lookup-limit": 1}`),
+ "1 transaction history",
+ []byte(`{"transaction-history": 1}`),
func() Config {
- return Config{TxLookupLimit: 1}
+ return Config{TransactionHistory: 1}
}(),
false,
},
{
- "-1 tx lookup limit",
- []byte(`{"tx-lookup-limit": -1}`),
+ "-1 transaction history",
+ []byte(`{"transaction-history": -1}`),
Config{},
true,
},
+ {
+ "deprecated tx lookup limit",
+ []byte(`{"tx-lookup-limit": 1}`),
+ Config{TransactionHistory: 1, TxLookupLimit: 1},
+ false,
+ },
{
"allow unprotected tx hashes",
[]byte(`{"allow-unprotected-tx-hashes": ["0x803351deb6d745e91545a6a3e1c0ea3e9a6a02a1a4193b70edfcd2f40f71a01c"]}`),
@@ -121,6 +127,7 @@ func TestUnmarshalConfig(t *testing.T) {
assert.Error(t, err)
} else {
assert.NoError(t, err)
+ tmp.Deprecate()
assert.Equal(t, tt.expected, tmp)
}
})
diff --git a/plugin/evm/export_tx.go b/plugin/evm/export_tx.go
index 9178849b26..a187007046 100644
--- a/plugin/evm/export_tx.go
+++ b/plugin/evm/export_tx.go
@@ -11,6 +11,7 @@ import (
"github.com/ava-labs/coreth/core/state"
"github.com/ava-labs/coreth/params"
+ "github.com/holiman/uint256"
"github.com/ava-labs/avalanchego/chains/atomic"
"github.com/ava-labs/avalanchego/ids"
@@ -376,8 +377,10 @@ func (utx *UnsignedExportTx) EVMStateTransfer(ctx *snow.Context, state *state.St
log.Debug("export_tx", "dest", utx.DestinationChain, "addr", from.Address, "amount", from.Amount, "assetID", "AVAX")
// We multiply the input amount by x2cRate to convert AVAX back to the appropriate
// denomination before export.
- amount := new(big.Int).Mul(
- new(big.Int).SetUint64(from.Amount), x2cRate)
+ amount := new(uint256.Int).Mul(
+ uint256.NewInt(from.Amount),
+ uint256.NewInt(x2cRate.Uint64()),
+ )
if state.GetBalance(from.Address).Cmp(amount) < 0 {
return errInsufficientFunds
}
diff --git a/plugin/evm/export_tx_test.go b/plugin/evm/export_tx_test.go
index f7c0e92cc6..d8a7fed80f 100644
--- a/plugin/evm/export_tx_test.go
+++ b/plugin/evm/export_tx_test.go
@@ -19,6 +19,7 @@ import (
"github.com/ava-labs/avalanchego/vms/secp256k1fx"
"github.com/ava-labs/coreth/params"
"github.com/ethereum/go-ethereum/common"
+ "github.com/holiman/uint256"
)
// createExportTxOptions adds funds to shared memory, imports them, and returns a list of export transactions
@@ -128,7 +129,7 @@ func TestExportTxEVMStateTransfer(t *testing.T) {
tests := []struct {
name string
tx []EVMInput
- avaxBalance *big.Int
+ avaxBalance *uint256.Int
balances map[ids.ID]*big.Int
expectedNonce uint64
shouldErr bool
@@ -136,7 +137,7 @@ func TestExportTxEVMStateTransfer(t *testing.T) {
{
name: "no transfers",
tx: nil,
- avaxBalance: big.NewInt(int64(avaxAmount) * x2cRateInt64),
+ avaxBalance: uint256.NewInt(avaxAmount * x2cRateUint64),
balances: map[ids.ID]*big.Int{
customAssetID: big.NewInt(int64(customAmount)),
},
@@ -153,7 +154,7 @@ func TestExportTxEVMStateTransfer(t *testing.T) {
Nonce: 0,
},
},
- avaxBalance: big.NewInt(int64(avaxAmount/2) * x2cRateInt64),
+ avaxBalance: uint256.NewInt(avaxAmount / 2 * x2cRateUint64),
balances: map[ids.ID]*big.Int{
customAssetID: big.NewInt(int64(customAmount)),
},
@@ -170,7 +171,7 @@ func TestExportTxEVMStateTransfer(t *testing.T) {
Nonce: 0,
},
},
- avaxBalance: big.NewInt(0),
+ avaxBalance: uint256.NewInt(0),
balances: map[ids.ID]*big.Int{
customAssetID: big.NewInt(int64(customAmount)),
},
@@ -187,7 +188,7 @@ func TestExportTxEVMStateTransfer(t *testing.T) {
Nonce: 0,
},
},
- avaxBalance: big.NewInt(0),
+ avaxBalance: uint256.NewInt(0),
balances: map[ids.ID]*big.Int{
customAssetID: big.NewInt(int64(customAmount)),
},
@@ -204,7 +205,7 @@ func TestExportTxEVMStateTransfer(t *testing.T) {
Nonce: 0,
},
},
- avaxBalance: big.NewInt(int64(avaxAmount) * x2cRateInt64),
+ avaxBalance: uint256.NewInt(avaxAmount * x2cRateUint64),
balances: map[ids.ID]*big.Int{
customAssetID: big.NewInt(int64(customAmount / 2)),
},
@@ -221,7 +222,7 @@ func TestExportTxEVMStateTransfer(t *testing.T) {
Nonce: 0,
},
},
- avaxBalance: big.NewInt(int64(avaxAmount) * x2cRateInt64),
+ avaxBalance: uint256.NewInt(avaxAmount * x2cRateUint64),
balances: map[ids.ID]*big.Int{
customAssetID: big.NewInt(0),
},
@@ -238,7 +239,7 @@ func TestExportTxEVMStateTransfer(t *testing.T) {
Nonce: 0,
},
},
- avaxBalance: big.NewInt(int64(avaxAmount) * x2cRateInt64),
+ avaxBalance: uint256.NewInt(avaxAmount * x2cRateUint64),
balances: map[ids.ID]*big.Int{
customAssetID: big.NewInt(0),
},
@@ -261,7 +262,7 @@ func TestExportTxEVMStateTransfer(t *testing.T) {
Nonce: 0,
},
},
- avaxBalance: big.NewInt(0),
+ avaxBalance: uint256.NewInt(0),
balances: map[ids.ID]*big.Int{
customAssetID: big.NewInt(0),
},
@@ -284,7 +285,7 @@ func TestExportTxEVMStateTransfer(t *testing.T) {
Nonce: 1,
},
},
- avaxBalance: big.NewInt(0),
+ avaxBalance: uint256.NewInt(0),
balances: map[ids.ID]*big.Int{
customAssetID: big.NewInt(0),
},
@@ -307,7 +308,7 @@ func TestExportTxEVMStateTransfer(t *testing.T) {
Nonce: 1,
},
},
- avaxBalance: big.NewInt(0),
+ avaxBalance: uint256.NewInt(0),
balances: map[ids.ID]*big.Int{
customAssetID: big.NewInt(0),
},
@@ -1794,7 +1795,7 @@ func TestNewExportTx(t *testing.T) {
}
addr := GetEthAddress(testKeys[0])
- if sdb.GetBalance(addr).Cmp(new(big.Int).SetUint64(test.bal*units.Avax)) != 0 {
+ if sdb.GetBalance(addr).Cmp(uint256.NewInt(test.bal*units.Avax)) != 0 {
t.Fatalf("address balance %s equal %s not %s", addr.String(), sdb.GetBalance(addr), new(big.Int).SetUint64(test.bal*units.Avax))
}
})
@@ -1981,7 +1982,7 @@ func TestNewExportTxMulticoin(t *testing.T) {
}
addr := GetEthAddress(testKeys[0])
- if stdb.GetBalance(addr).Cmp(new(big.Int).SetUint64(test.bal*units.Avax)) != 0 {
+ if stdb.GetBalance(addr).Cmp(uint256.NewInt(test.bal*units.Avax)) != 0 {
t.Fatalf("address balance %s equal %s not %s", addr.String(), stdb.GetBalance(addr), new(big.Int).SetUint64(test.bal*units.Avax))
}
if stdb.GetBalanceMultiCoin(addr, common.BytesToHash(tid[:])).Cmp(new(big.Int).SetUint64(test.balmc)) != 0 {
diff --git a/plugin/evm/gossip.go b/plugin/evm/gossip.go
index 139fbbe454..a760936021 100644
--- a/plugin/evm/gossip.go
+++ b/plugin/evm/gossip.go
@@ -160,7 +160,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 47ca011836..2db67a6f4b 100644
--- a/plugin/evm/gossip_test.go
+++ b/plugin/evm/gossip_test.go
@@ -204,14 +204,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_atomic_gossiping_test.go b/plugin/evm/gossiper_atomic_gossiping_test.go
index 6ab4b429b9..0974b50638 100644
--- a/plugin/evm/gossiper_atomic_gossiping_test.go
+++ b/plugin/evm/gossiper_atomic_gossiping_test.go
@@ -5,7 +5,6 @@ package evm
import (
"context"
- "os"
"sync"
"testing"
"time"
@@ -72,7 +71,7 @@ func TestMempoolAtmTxsAppGossipHandling(t *testing.T) {
txGossipedLock.Lock()
assert.Equal(0, txGossiped, "tx should not have been gossiped")
txGossipedLock.Unlock()
- assert.True(vm.mempool.has(tx.ID()))
+ assert.True(vm.mempool.Has(tx.ID()))
vm.ctx.Lock.Unlock()
@@ -102,14 +101,11 @@ func TestMempoolAtmTxsAppGossipHandling(t *testing.T) {
txGossipedLock.Lock()
assert.Equal(0, txGossiped, "tx should not have been gossiped")
txGossipedLock.Unlock()
- assert.False(vm.mempool.has(conflictingTx.ID()), "conflicting tx should not be in the atomic mempool")
+ assert.False(vm.mempool.Has(conflictingTx.ID()), "conflicting tx should not be in the atomic mempool")
}
// show that txs already marked as invalid are not re-requested on gossiping
func TestMempoolAtmTxsAppGossipHandlingDiscardedTx(t *testing.T) {
- if os.Getenv("RUN_FLAKY_TESTS") != "true" {
- t.Skip("FLAKY")
- }
assert := assert.New(t)
_, vm, _, sharedMemory, sender := GenesisVM(t, true, "", "", "")
@@ -146,7 +142,7 @@ func TestMempoolAtmTxsAppGossipHandlingDiscardedTx(t *testing.T) {
mempool.DiscardCurrentTx(txID)
// Check the mempool does not contain the discarded transaction
- assert.False(mempool.has(txID))
+ assert.False(mempool.Has(txID))
// Gossip the transaction to the VM and ensure that it is not added to the mempool
// and is not re-gossipped.
@@ -168,7 +164,7 @@ func TestMempoolAtmTxsAppGossipHandlingDiscardedTx(t *testing.T) {
assert.Zero(txGossiped, "tx should not have been gossiped")
txGossipedLock.Unlock()
- assert.False(mempool.has(txID))
+ assert.False(mempool.Has(txID))
vm.ctx.Lock.Unlock()
@@ -186,6 +182,6 @@ func TestMempoolAtmTxsAppGossipHandlingDiscardedTx(t *testing.T) {
assert.Equal(1, txGossiped, "conflicting tx should have been gossiped")
txGossipedLock.Unlock()
- assert.False(mempool.has(txID))
- assert.True(mempool.has(conflictingTx.ID()))
+ assert.False(mempool.Has(txID))
+ assert.True(mempool.Has(conflictingTx.ID()))
}
diff --git a/plugin/evm/gossiper_eth_gossiping_test.go b/plugin/evm/gossiper_eth_gossiping_test.go
index dfcf80666f..47416f0bf1 100644
--- a/plugin/evm/gossiper_eth_gossiping_test.go
+++ b/plugin/evm/gossiper_eth_gossiping_test.go
@@ -8,7 +8,6 @@ import (
"crypto/ecdsa"
"encoding/json"
"math/big"
- "os"
"strings"
"sync"
"testing"
@@ -35,9 +34,9 @@ func fundAddressByGenesis(addrs []common.Address) (string, error) {
Difficulty: common.Big0,
GasLimit: uint64(5000000),
}
- funds := make(map[common.Address]core.GenesisAccount)
+ funds := make(map[common.Address]types.GenesisAccount)
for _, addr := range addrs {
- funds[addr] = core.GenesisAccount{
+ funds[addr] = types.GenesisAccount{
Balance: balance,
}
}
@@ -74,9 +73,6 @@ func getValidEthTxs(key *ecdsa.PrivateKey, count int, gasPrice *big.Int) []*type
// show that a geth tx discovered from gossip is requested to the same node that
// gossiped it
func TestMempoolEthTxsAppGossipHandling(t *testing.T) {
- if os.Getenv("RUN_FLAKY_TESTS") != "true" {
- t.Skip("FLAKY")
- }
assert := assert.New(t)
key, err := crypto.GenerateKey()
diff --git a/plugin/evm/import_tx.go b/plugin/evm/import_tx.go
index 843c108d50..b67b834673 100644
--- a/plugin/evm/import_tx.go
+++ b/plugin/evm/import_tx.go
@@ -12,6 +12,7 @@ import (
"github.com/ava-labs/coreth/core/state"
"github.com/ava-labs/coreth/params"
+ "github.com/holiman/uint256"
"github.com/ava-labs/avalanchego/chains/atomic"
"github.com/ava-labs/avalanchego/ids"
@@ -434,8 +435,7 @@ func (utx *UnsignedImportTx) EVMStateTransfer(ctx *snow.Context, state *state.St
log.Debug("import_tx", "src", utx.SourceChain, "addr", to.Address, "amount", to.Amount, "assetID", "AVAX")
// If the asset is AVAX, convert the input amount in nAVAX to gWei by
// multiplying by the x2c rate.
- amount := new(big.Int).Mul(
- new(big.Int).SetUint64(to.Amount), x2cRate)
+ amount := new(uint256.Int).Mul(uint256.NewInt(to.Amount), x2cRate)
state.AddBalance(to.Address, amount)
} else {
log.Debug("import_tx", "src", utx.SourceChain, "addr", to.Address, "amount", to.Amount, "assetID", to.AssetID)
diff --git a/plugin/evm/import_tx_test.go b/plugin/evm/import_tx_test.go
index ddcde4a879..ec8b2b2fb4 100644
--- a/plugin/evm/import_tx_test.go
+++ b/plugin/evm/import_tx_test.go
@@ -9,6 +9,7 @@ import (
"github.com/ava-labs/coreth/params"
"github.com/ethereum/go-ethereum/common"
+ "github.com/holiman/uint256"
"github.com/ava-labs/avalanchego/chains/atomic"
"github.com/ava-labs/avalanchego/ids"
@@ -494,7 +495,8 @@ func TestNewImportTx(t *testing.T) {
t.Fatal(err)
}
- expectedRemainingBalance := new(big.Int).Mul(new(big.Int).SetUint64(importAmount-actualAVAXBurned), x2cRate)
+ expectedRemainingBalance := new(uint256.Int).Mul(
+ uint256.NewInt(importAmount-actualAVAXBurned), x2cRate)
addr := GetEthAddress(testKeys[0])
if actualBalance := sdb.GetBalance(addr); actualBalance.Cmp(expectedRemainingBalance) != 0 {
t.Fatalf("address remaining balance %s equal %s not %s", addr.String(), actualBalance, expectedRemainingBalance)
@@ -1274,7 +1276,7 @@ func TestImportTxEVMStateTransfer(t *testing.T) {
t.Fatalf("Expected asset balance to be %d, found balance: %d", common.Big1, assetBalance)
}
avaxBalance := sdb.GetBalance(testEthAddrs[0])
- if avaxBalance.Cmp(common.Big0) != 0 {
+ if avaxBalance.Cmp(common.U2560) != 0 {
t.Fatalf("Expected AVAX balance to be 0, found balance: %d", avaxBalance)
}
},
diff --git a/plugin/evm/log_test.go b/plugin/evm/log_test.go
index b0ef478795..b39fa8cc3c 100644
--- a/plugin/evm/log_test.go
+++ b/plugin/evm/log_test.go
@@ -4,8 +4,10 @@
package evm
import (
+ "os"
"testing"
+ "github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require"
)
@@ -22,3 +24,10 @@ func TestTrimPrefixes(t *testing.T) {
require.Equal(t, test.after, trimPrefixes(test.before))
}
}
+
+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/mempool.go b/plugin/evm/mempool.go
index 25b787754c..69a832cd2e 100644
--- a/plugin/evm/mempool.go
+++ b/plugin/evm/mempool.go
@@ -115,13 +115,6 @@ func (m *Mempool) length() int {
return m.txHeap.Len() + len(m.issuedTxs)
}
-// has indicates if a given [txID] is in the mempool and has not been
-// discarded.
-func (m *Mempool) has(txID ids.ID) bool {
- _, dropped, found := m.GetTx(txID)
- return found && !dropped
-}
-
// atomicTxGasPrice is the [gasPrice] paid by a transaction to burn a given
// amount of [AVAXAssetID] given the value of [gasUsed].
func (m *Mempool) atomicTxGasPrice(tx *Tx) (uint64, error) {
@@ -430,10 +423,8 @@ func (m *Mempool) GetTx(txID ids.ID) (*Tx, bool, bool) {
// Has returns true if the mempool contains [txID] or it was issued.
func (m *Mempool) Has(txID ids.ID) bool {
- m.lock.RLock()
- defer m.lock.RUnlock()
-
- return m.has(txID)
+ _, dropped, found := m.GetTx(txID)
+ return found && !dropped
}
// IssueCurrentTx marks [currentTx] as issued if there is one
diff --git a/plugin/evm/mempool_atomic_gossiping_test.go b/plugin/evm/mempool_atomic_gossiping_test.go
index 741c177b90..b44f2097b4 100644
--- a/plugin/evm/mempool_atomic_gossiping_test.go
+++ b/plugin/evm/mempool_atomic_gossiping_test.go
@@ -47,18 +47,18 @@ func TestMempoolAddLocallyCreateAtomicTx(t *testing.T) {
// add a tx to the mempool
err := vm.mempool.AddLocalTx(tx)
assert.NoError(err)
- has := mempool.has(txID)
+ has := mempool.Has(txID)
assert.True(has, "valid tx not recorded into mempool")
// try to add a conflicting tx
err = vm.mempool.AddLocalTx(conflictingTx)
assert.ErrorIs(err, errConflictingAtomicTx)
- has = mempool.has(conflictingTxID)
+ has = mempool.Has(conflictingTxID)
assert.False(has, "conflicting tx in mempool")
<-issuer
- has = mempool.has(txID)
+ has = mempool.Has(txID)
assert.True(has, "valid tx not recorded into mempool")
// Show that BuildBlock generates a block containing [txID] and that it is
@@ -71,7 +71,7 @@ func TestMempoolAddLocallyCreateAtomicTx(t *testing.T) {
assert.Equal(txID, evmBlk.atomicTxs[0].ID(), "block does not include expected transaction")
- has = mempool.has(txID)
+ has = mempool.Has(txID)
assert.True(has, "tx should stay in mempool until block is accepted")
err = blk.Verify(context.Background())
@@ -80,7 +80,7 @@ func TestMempoolAddLocallyCreateAtomicTx(t *testing.T) {
err = blk.Accept(context.Background())
assert.NoError(err)
- has = mempool.has(txID)
+ has = mempool.Has(txID)
assert.False(has, "tx shouldn't be in mempool after block is accepted")
})
}
@@ -105,13 +105,13 @@ func TestMempoolMaxMempoolSizeHandling(t *testing.T) {
mempool.maxSize = 0
assert.ErrorIs(mempool.AddTx(tx), errTooManyAtomicTx)
- assert.False(mempool.has(tx.ID()))
+ assert.False(mempool.Has(tx.ID()))
// shortcut to simulated empty mempool
mempool.maxSize = defaultMempoolSize
assert.NoError(mempool.AddTx(tx))
- assert.True(mempool.has(tx.ID()))
+ assert.True(mempool.Has(tx.ID()))
}
// mempool will drop transaction with the lowest fee
@@ -136,22 +136,22 @@ func TestMempoolPriorityDrop(t *testing.T) {
t.Fatal(err)
}
assert.NoError(mempool.AddTx(tx1))
- assert.True(mempool.has(tx1.ID()))
+ assert.True(mempool.Has(tx1.ID()))
tx2, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[1], initialBaseFee, []*secp256k1.PrivateKey{testKeys[1]})
if err != nil {
t.Fatal(err)
}
assert.ErrorIs(mempool.AddTx(tx2), errInsufficientAtomicTxFee)
- assert.True(mempool.has(tx1.ID()))
- assert.False(mempool.has(tx2.ID()))
+ assert.True(mempool.Has(tx1.ID()))
+ assert.False(mempool.Has(tx2.ID()))
tx3, err := vm.newImportTx(vm.ctx.XChainID, testEthAddrs[1], new(big.Int).Mul(initialBaseFee, big.NewInt(2)), []*secp256k1.PrivateKey{testKeys[1]})
if err != nil {
t.Fatal(err)
}
assert.NoError(mempool.AddTx(tx3))
- assert.False(mempool.has(tx1.ID()))
- assert.False(mempool.has(tx2.ID()))
- assert.True(mempool.has(tx3.ID()))
+ assert.False(mempool.Has(tx1.ID()))
+ assert.False(mempool.Has(tx2.ID()))
+ assert.True(mempool.Has(tx3.ID()))
}
diff --git a/plugin/evm/network_handler.go b/plugin/evm/network_handler.go
index 33670ed4cf..aa4b728ed5 100644
--- a/plugin/evm/network_handler.go
+++ b/plugin/evm/network_handler.go
@@ -12,7 +12,7 @@ import (
"github.com/ava-labs/coreth/plugin/evm/message"
syncHandlers "github.com/ava-labs/coreth/sync/handlers"
syncStats "github.com/ava-labs/coreth/sync/handlers/stats"
- "github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/triedb"
"github.com/ava-labs/coreth/warp"
warpHandlers "github.com/ava-labs/coreth/warp/handlers"
"github.com/ethereum/go-ethereum/ethdb"
@@ -32,8 +32,8 @@ type networkHandler struct {
func newNetworkHandler(
provider syncHandlers.SyncDataProvider,
diskDB ethdb.KeyValueReader,
- evmTrieDB *trie.Database,
- atomicTrieDB *trie.Database,
+ evmTrieDB *triedb.Database,
+ atomicTrieDB *triedb.Database,
warpBackend warp.Backend,
networkCodec codec.Manager,
) message.RequestHandler {
diff --git a/plugin/evm/syncervm_test.go b/plugin/evm/syncervm_test.go
index 11b66802a5..3ecfcb517c 100644
--- a/plugin/evm/syncervm_test.go
+++ b/plugin/evm/syncervm_test.go
@@ -39,6 +39,7 @@ import (
statesyncclient "github.com/ava-labs/coreth/sync/client"
"github.com/ava-labs/coreth/sync/statesync"
"github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/triedb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
@@ -339,7 +340,7 @@ func createSyncServerAndClientVMs(t *testing.T, test syncTest, numBlocks int) *s
serverSharedMemories.assertOpsApplied(t, exportTx.mustAtomicOps())
// 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
@@ -512,7 +513,7 @@ func testSyncerVM(t *testing.T, vmSetup *syncVMSetup, test syncTest) {
}
// tail should be the last block synced
- if syncerVM.ethConfig.TxLookupLimit != 0 {
+ if syncerVM.ethConfig.TransactionHistory != 0 {
tail := lastSyncedBlock.NumberU64()
core.CheckTxIndices(t, &tail, tail, syncerVM.chaindb, true)
@@ -540,8 +541,8 @@ func testSyncerVM(t *testing.T, vmSetup *syncVMSetup, test syncTest) {
}
},
func(block *types.Block) {
- if syncerVM.ethConfig.TxLookupLimit != 0 {
- tail := block.NumberU64() - syncerVM.ethConfig.TxLookupLimit + 1
+ if syncerVM.ethConfig.TransactionHistory != 0 {
+ tail := block.NumberU64() - syncerVM.ethConfig.TransactionHistory + 1
// tail should be the minimum last synced block, since we skipped it to the last block
if tail < lastSyncedBlock.NumberU64() {
tail = lastSyncedBlock.NumberU64()
@@ -582,8 +583,8 @@ func testSyncerVM(t *testing.T, vmSetup *syncVMSetup, test syncTest) {
}
},
func(block *types.Block) {
- if syncerVM.ethConfig.TxLookupLimit != 0 {
- tail := block.NumberU64() - syncerVM.ethConfig.TxLookupLimit + 1
+ if syncerVM.ethConfig.TransactionHistory != 0 {
+ tail := block.NumberU64() - syncerVM.ethConfig.TransactionHistory + 1
// tail should be the minimum last synced block, since we skipped it to the last block
if tail < lastSyncedBlock.NumberU64() {
tail = lastSyncedBlock.NumberU64()
diff --git a/plugin/evm/tx.go b/plugin/evm/tx.go
index 5c8497a3a3..00f7de4bce 100644
--- a/plugin/evm/tx.go
+++ b/plugin/evm/tx.go
@@ -216,7 +216,7 @@ func (tx *Tx) BlockFeeContribution(fixedFee bool, avaxAssetID ids.ID, baseFee *b
// Calculate the amount of AVAX that has been burned above the required fee denominated
// in C-Chain native 18 decimal places
- blockFeeContribution := new(big.Int).Mul(new(big.Int).SetUint64(excessBurned), x2cRate)
+ blockFeeContribution := new(big.Int).Mul(new(big.Int).SetUint64(excessBurned), x2cRate.ToBig())
return blockFeeContribution, new(big.Int).SetUint64(gasUsed), nil
}
@@ -254,8 +254,8 @@ func CalculateDynamicFee(cost uint64, baseFee *big.Int) (uint64, error) {
}
bigCost := new(big.Int).SetUint64(cost)
fee := new(big.Int).Mul(bigCost, baseFee)
- feeToRoundUp := new(big.Int).Add(fee, x2cRateMinus1)
- feeInNAVAX := new(big.Int).Div(feeToRoundUp, x2cRate)
+ feeToRoundUp := new(big.Int).Add(fee, x2cRateMinus1.ToBig())
+ feeInNAVAX := new(big.Int).Div(feeToRoundUp, x2cRate.ToBig())
if !feeInNAVAX.IsUint64() {
// the fee is more than can fit in a uint64
return 0, errFeeOverflow
diff --git a/plugin/evm/tx_gossip_test.go b/plugin/evm/tx_gossip_test.go
index 4da9713d68..6024747180 100644
--- a/plugin/evm/tx_gossip_test.go
+++ b/plugin/evm/tx_gossip_test.go
@@ -86,7 +86,7 @@ func TestEthTxGossip(t *testing.T) {
network, err := p2p.NewNetwork(logging.NoLog{}, peerSender, prometheus.NewRegistry(), "")
require.NoError(err)
- client := network.NewClient(ethTxGossipProtocol)
+ client := network.NewClient(p2p.TxGossipHandlerID)
// we only accept gossip requests from validators
requestingNodeID := ids.GenerateTestNodeID()
@@ -218,7 +218,7 @@ func TestAtomicTxGossip(t *testing.T) {
}
network, err := p2p.NewNetwork(logging.NoLog{}, peerSender, prometheus.NewRegistry(), "")
require.NoError(err)
- client := network.NewClient(atomicTxGossipProtocol)
+ client := network.NewClient(p2p.AtomicTxGossipHandlerID)
// we only accept gossip requests from validators
requestingNodeID := ids.GenerateTestNodeID()
@@ -363,7 +363,7 @@ func TestEthTxPushGossipOutbound(t *testing.T) {
// we should get a message that has the protocol prefix and the gossip
// message
- require.Equal(byte(ethTxGossipProtocol), sent[0])
+ require.Equal(byte(p2p.TxGossipHandlerID), sent[0])
require.NoError(proto.Unmarshal(sent[1:], got))
marshaller := GossipEthTxMarshaller{}
@@ -428,7 +428,7 @@ func TestEthTxPushGossipInbound(t *testing.T) {
inboundGossipBytes, err := proto.Marshal(inboundGossip)
require.NoError(err)
- inboundGossipMsg := append(binary.AppendUvarint(nil, ethTxGossipProtocol), inboundGossipBytes...)
+ inboundGossipMsg := append(binary.AppendUvarint(nil, p2p.TxGossipHandlerID), inboundGossipBytes...)
require.NoError(vm.AppGossip(ctx, ids.EmptyNodeID, inboundGossipMsg))
require.True(vm.txPool.Has(signedTx.Hash()))
@@ -500,7 +500,7 @@ func TestAtomicTxPushGossipOutbound(t *testing.T) {
vm.atomicTxPushGossiper.Add(&GossipAtomicTx{tx})
gossipedBytes := <-sender.SentAppGossip
- require.Equal(byte(atomicTxGossipProtocol), gossipedBytes[0])
+ require.Equal(byte(p2p.AtomicTxGossipHandlerID), gossipedBytes[0])
outboundGossipMsg := &sdk.PushGossip{}
require.NoError(proto.Unmarshal(gossipedBytes[1:], outboundGossipMsg))
@@ -587,8 +587,8 @@ func TestAtomicTxPushGossipInbound(t *testing.T) {
inboundGossipBytes, err := proto.Marshal(inboundGossip)
require.NoError(err)
- inboundGossipMsg := append(binary.AppendUvarint(nil, atomicTxGossipProtocol), inboundGossipBytes...)
+ inboundGossipMsg := append(binary.AppendUvarint(nil, p2p.AtomicTxGossipHandlerID), inboundGossipBytes...)
require.NoError(vm.AppGossip(ctx, ids.EmptyNodeID, inboundGossipMsg))
- require.True(vm.mempool.has(tx.ID()))
+ require.True(vm.mempool.Has(tx.ID()))
}
diff --git a/plugin/evm/tx_test.go b/plugin/evm/tx_test.go
index 1c72120065..d99ee70309 100644
--- a/plugin/evm/tx_test.go
+++ b/plugin/evm/tx_test.go
@@ -30,7 +30,7 @@ func TestCalculateDynamicFee(t *testing.T) {
var tests []test = []test{
{
gas: 1,
- baseFee: new(big.Int).Set(x2cRate),
+ baseFee: new(big.Int).Set(x2cRate.ToBig()),
expectedValue: 1,
},
{
diff --git a/plugin/evm/vm.go b/plugin/evm/vm.go
index cb7cd9f1d6..e113252e05 100644
--- a/plugin/evm/vm.go
+++ b/plugin/evm/vm.go
@@ -21,6 +21,7 @@ import (
"github.com/ava-labs/avalanchego/network/p2p/gossip"
"github.com/ava-labs/avalanchego/upgrade"
avalanchegoConstants "github.com/ava-labs/avalanchego/utils/constants"
+ "github.com/holiman/uint256"
"github.com/prometheus/client_golang/prometheus"
"github.com/ava-labs/coreth/consensus/dummy"
@@ -39,17 +40,16 @@ import (
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/peer"
"github.com/ava-labs/coreth/plugin/evm/message"
- "github.com/ava-labs/coreth/trie/triedb/hashdb"
+ "github.com/ava-labs/coreth/triedb"
+ "github.com/ava-labs/coreth/triedb/hashdb"
+ "github.com/ava-labs/coreth/utils"
- warpPrecompile "github.com/ava-labs/coreth/precompile/contracts/warp"
+ warpcontract "github.com/ava-labs/coreth/precompile/contracts/warp"
"github.com/ava-labs/coreth/rpc"
statesyncclient "github.com/ava-labs/coreth/sync/client"
"github.com/ava-labs/coreth/sync/client/stats"
- "github.com/ava-labs/coreth/trie"
- "github.com/ava-labs/coreth/utils"
"github.com/ava-labs/coreth/warp"
"github.com/ava-labs/coreth/warp/handlers"
- warpValidators "github.com/ava-labs/coreth/warp/validators"
// Force-load tracer engine to trigger registration
//
@@ -108,8 +108,8 @@ var (
)
const (
- x2cRateInt64 int64 = 1_000_000_000
- x2cRateMinus1Int64 int64 = x2cRateInt64 - 1
+ x2cRateUint64 uint64 = 1_000_000_000
+ x2cRateMinus1Uint64 uint64 = x2cRateUint64 - 1
)
var (
@@ -117,8 +117,8 @@ var (
// 1 nAVAX and the smallest denomination on the C-Chain 1 wei. Where 1 nAVAX = 1 gWei.
// This is only required for AVAX because the denomination of 1 AVAX is 9 decimal
// places on the X and P chains, but is 18 decimal places within the EVM.
- x2cRate = big.NewInt(x2cRateInt64)
- x2cRateMinus1 = big.NewInt(x2cRateMinus1Int64)
+ x2cRate = uint256.NewInt(x2cRateUint64)
+ x2cRateMinus1 = uint256.NewInt(x2cRateMinus1Uint64)
)
const (
@@ -143,10 +143,6 @@ const (
targetAtomicTxsSize = 40 * units.KiB
- // p2p app protocols
- ethTxGossipProtocol = 0x0
- atomicTxGossipProtocol = 0x1
-
// gossip constants
pushGossipDiscardedElements = 16_384
txGossipBloomMinTargetElements = 8 * 1024
@@ -473,7 +469,7 @@ func (vm *VM) Initialize(
// If the Durango is activated, activate the Warp Precompile at the same time
if g.Config.DurangoBlockTimestamp != nil {
g.Config.PrecompileUpgrades = append(g.Config.PrecompileUpgrades, params.PrecompileUpgrade{
- Config: warpPrecompile.NewDefaultConfig(g.Config.DurangoBlockTimestamp),
+ Config: warpcontract.NewDefaultConfig(g.Config.DurangoBlockTimestamp),
})
}
@@ -550,7 +546,7 @@ func (vm *VM) Initialize(
vm.ethConfig.CommitInterval = vm.config.CommitInterval
vm.ethConfig.SkipUpgradeCheck = vm.config.SkipUpgradeCheck
vm.ethConfig.AcceptedCacheSize = vm.config.AcceptedCacheSize
- vm.ethConfig.TxLookupLimit = vm.config.TxLookupLimit
+ vm.ethConfig.TransactionHistory = vm.config.TransactionHistory
vm.ethConfig.SkipTxIndexing = vm.config.SkipTxIndexing
// Create directory for offline pruning
@@ -693,14 +689,15 @@ func (vm *VM) initializeChain(lastAcceptedHash common.Hash) error {
if err != nil {
return err
}
+ callbacks := vm.createConsensusCallbacks()
vm.eth, err = eth.New(
node,
&vm.ethConfig,
- vm.createConsensusCallbacks(),
&EthPushGossiper{vm: vm},
vm.chaindb,
vm.config.EthBackendSettings(),
lastAcceptedHash,
+ dummy.NewFakerWithClock(callbacks, &vm.clock),
&vm.clock,
)
if err != nil {
@@ -1112,7 +1109,7 @@ func (vm *VM) initBlockBuilding() error {
vm.cancel = cancel
ethTxGossipMarshaller := GossipEthTxMarshaller{}
- ethTxGossipClient := vm.Network.NewClient(ethTxGossipProtocol, p2p.WithValidatorSampling(vm.validators))
+ ethTxGossipClient := vm.Network.NewClient(p2p.TxGossipHandlerID, p2p.WithValidatorSampling(vm.validators))
ethTxGossipMetrics, err := gossip.NewMetrics(vm.sdkMetrics, ethTxGossipNamespace)
if err != nil {
return fmt.Errorf("failed to initialize eth tx gossip metrics: %w", err)
@@ -1128,7 +1125,7 @@ func (vm *VM) initBlockBuilding() error {
}()
atomicTxGossipMarshaller := GossipAtomicTxMarshaller{}
- atomicTxGossipClient := vm.Network.NewClient(atomicTxGossipProtocol, p2p.WithValidatorSampling(vm.validators))
+ atomicTxGossipClient := vm.Network.NewClient(p2p.AtomicTxGossipHandlerID, p2p.WithValidatorSampling(vm.validators))
atomicTxGossipMetrics, err := gossip.NewMetrics(vm.sdkMetrics, atomicTxGossipNamespace)
if err != nil {
return fmt.Errorf("failed to initialize atomic tx gossip metrics: %w", err)
@@ -1201,7 +1198,7 @@ func (vm *VM) initBlockBuilding() error {
)
}
- if err := vm.Network.AddHandler(ethTxGossipProtocol, vm.ethTxGossipHandler); err != nil {
+ if err := vm.Network.AddHandler(p2p.TxGossipHandlerID, vm.ethTxGossipHandler); err != nil {
return err
}
@@ -1218,7 +1215,7 @@ func (vm *VM) initBlockBuilding() error {
)
}
- if err := vm.Network.AddHandler(atomicTxGossipProtocol, vm.atomicTxGossipHandler); err != nil {
+ if err := vm.Network.AddHandler(p2p.AtomicTxGossipHandlerID, vm.atomicTxGossipHandler); err != nil {
return err
}
@@ -1285,9 +1282,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,
},
@@ -1459,7 +1456,7 @@ func (vm *VM) VerifyHeightIndex(context.Context) error {
return nil
}
-// GetBlockAtHeight returns the canonical block at [height].
+// GetBlockIDAtHeight returns the canonical block at [height].
// Note: the engine assumes that if a block is not found at [height], then
// [database.ErrNotFound] will be returned. This indicates that the VM has state
// synced and does not have all historical blocks available.
@@ -1494,6 +1491,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
@@ -1528,8 +1529,7 @@ func (vm *VM) CreateHandlers(context.Context) (map[string]http.Handler, error) {
}
if vm.config.WarpAPIEnabled {
- validatorsState := warpValidators.NewState(vm.ctx)
- if err := handler.RegisterName("warp", warp.NewAPI(vm.ctx.NetworkID, vm.ctx.SubnetID, vm.ctx.ChainID, validatorsState, vm.warpBackend, vm.client)); err != nil {
+ if err := handler.RegisterName("warp", warp.NewAPI(vm.ctx.NetworkID, vm.ctx.SubnetID, vm.ctx.ChainID, vm.ctx.ValidatorState, vm.warpBackend, vm.client, vm.requirePrimaryNetworkSigners)); err != nil {
return nil, err
}
enabledAPIs = append(enabledAPIs, "warp")
@@ -1550,6 +1550,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
}
@@ -1797,7 +1800,7 @@ func (vm *VM) GetSpendableFunds(
if assetID == vm.ctx.AVAXAssetID {
// If the asset is AVAX, we divide by the x2cRate to convert back to the correct
// denomination of AVAX that can be exported.
- balance = new(big.Int).Div(state.GetBalance(addr), x2cRate).Uint64()
+ balance = new(uint256.Int).Div(state.GetBalance(addr), x2cRate).Uint64()
} else {
balance = state.GetBalanceMultiCoin(addr, common.Hash(assetID)).Uint64()
}
@@ -1884,7 +1887,7 @@ func (vm *VM) GetSpendableAVAXWithFee(
addr := GetEthAddress(key)
// Since the asset is AVAX, we divide by the x2cRate to convert back to
// the correct denomination of AVAX that can be exported.
- balance := new(big.Int).Div(state.GetBalance(addr), x2cRate).Uint64()
+ balance := new(uint256.Int).Div(state.GetBalance(addr), x2cRate).Uint64()
// If the balance for [addr] is insufficient to cover the additional cost
// of adding an input to the transaction, skip adding the input altogether
if balance <= additionalFee {
@@ -1945,6 +1948,18 @@ func (vm *VM) currentRules() params.Rules {
return vm.chainConfig.Rules(header.Number, header.Time)
}
+// requirePrimaryNetworkSigners returns true if warp messages from the primary
+// network must be signed by the primary network validators.
+// This is necessary when the subnet is not validating the primary network.
+func (vm *VM) requirePrimaryNetworkSigners() bool {
+ switch c := vm.currentRules().ActivePrecompiles[warpcontract.ContractAddress].(type) {
+ case *warpcontract.Config:
+ return c.RequirePrimaryNetworkSigners
+ default: // includes nil due to non-presence
+ return false
+ }
+}
+
func (vm *VM) startContinuousProfiler() {
// If the profiler directory is empty, return immediately
// without creating or starting a continuous profiler.
diff --git a/plugin/evm/vm_test.go b/plugin/evm/vm_test.go
index c0de677fe4..bcc1caa168 100644
--- a/plugin/evm/vm_test.go
+++ b/plugin/evm/vm_test.go
@@ -38,6 +38,7 @@ import (
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/snow"
"github.com/ava-labs/avalanchego/snow/validators/validatorstest"
+ "github.com/ava-labs/avalanchego/upgrade"
"github.com/ava-labs/avalanchego/utils/cb58"
"github.com/ava-labs/avalanchego/utils/crypto/bls"
"github.com/ava-labs/avalanchego/utils/crypto/secp256k1"
@@ -82,6 +83,7 @@ var (
g := new(core.Genesis)
g.Difficulty = big.NewInt(0)
g.GasLimit = 0x5f5e100
+ g.Timestamp = uint64(upgrade.InitiallyActiveTime.Unix())
// Use chainId: 43111, so that it does not overlap with any Avalanche ChainIDs, which may have their
// config overridden in vm.Initialize.
@@ -96,7 +98,7 @@ var (
if cfg.IsDurango(0) {
addr := common.HexToAddress("0x99b9DEA54C48Dfea6aA9A4Ca4623633EE04ddbB5")
balance := new(big.Int).Mul(big.NewInt(params.Ether), big.NewInt(10))
- g.Alloc[addr] = core.GenesisAccount{Balance: balance}
+ g.Alloc[addr] = types.GenesisAccount{Balance: balance}
}
b, err := json.Marshal(g)
@@ -166,9 +168,9 @@ func newPrefundedGenesis(
balance int,
addresses ...common.Address,
) *core.Genesis {
- alloc := core.GenesisAlloc{}
+ alloc := types.GenesisAlloc{}
for _, address := range addresses {
- alloc[address] = core.GenesisAccount{
+ alloc[address] = types.GenesisAccount{
Balance: big.NewInt(int64(balance)),
}
}
@@ -283,7 +285,8 @@ func GenesisVM(t *testing.T,
upgradeJSON string,
) (
chan commonEng.Message,
- *VM, database.Database,
+ *VM,
+ database.Database,
*atomic.Memory,
*enginetest.Sender,
) {
@@ -301,7 +304,8 @@ func GenesisVMWithClock(
clock mockable.Clock,
) (
chan commonEng.Message,
- *VM, database.Database,
+ *VM,
+ database.Database,
*atomic.Memory,
*enginetest.Sender,
) {
@@ -1098,9 +1102,9 @@ func TestReissueAtomicTxHigherGasPrice(t *testing.T) {
t.Fatalf("Expected to fail with err: %s, but found err: %s", errConflictingAtomicTx, err)
}
- assert.True(t, vm.mempool.has(importTx1.ID()))
- assert.True(t, vm.mempool.has(importTx2.ID()))
- assert.False(t, vm.mempool.has(reissuanceTx1.ID()))
+ assert.True(t, vm.mempool.Has(importTx1.ID()))
+ assert.True(t, vm.mempool.Has(importTx2.ID()))
+ assert.False(t, vm.mempool.Has(reissuanceTx1.ID()))
reissuanceTx2, err := vm.newImportTxWithUTXOs(vm.ctx.XChainID, testEthAddrs[0], new(big.Int).Mul(big.NewInt(4), initialBaseFee), kc, []*avax.UTXO{utxo1, utxo2})
if err != nil {
@@ -3850,21 +3854,21 @@ func TestParentBeaconRootBlock(t *testing.T) {
expectedError: false,
},
{
- name: "non-empty parent beacon root in Cancun",
- genesisJSON: genesisJSONCancun,
+ name: "non-empty parent beacon root in E-Upgrade (Cancun)",
+ genesisJSON: genesisJSONEtna,
beaconRoot: &common.Hash{0x01},
expectedError: true,
errString: "expected empty hash",
},
{
- name: "empty parent beacon root in Cancun",
- genesisJSON: genesisJSONCancun,
+ name: "empty parent beacon root in E-Upgrade (Cancun)",
+ genesisJSON: genesisJSONEtna,
beaconRoot: &common.Hash{},
expectedError: false,
},
{
- name: "nil parent beacon root in Cancun",
- genesisJSON: genesisJSONCancun,
+ name: "nil parent beacon root in E-Upgrade (Cancun)",
+ genesisJSON: genesisJSONEtna,
beaconRoot: nil,
expectedError: true,
errString: "header is missing parentBeaconRoot",
diff --git a/plugin/evm/vm_warp_test.go b/plugin/evm/vm_warp_test.go
index a447950d07..ec333e4132 100644
--- a/plugin/evm/vm_warp_test.go
+++ b/plugin/evm/vm_warp_test.go
@@ -13,10 +13,13 @@ import (
_ "embed"
"github.com/ava-labs/avalanchego/ids"
+ commonEng "github.com/ava-labs/avalanchego/snow/engine/common"
"github.com/ava-labs/avalanchego/snow/engine/snowman/block"
"github.com/ava-labs/avalanchego/snow/validators"
"github.com/ava-labs/avalanchego/snow/validators/validatorstest"
- "github.com/ava-labs/avalanchego/utils"
+ "github.com/ava-labs/avalanchego/upgrade"
+ avagoUtils "github.com/ava-labs/avalanchego/utils"
+ "github.com/ava-labs/avalanchego/utils/constants"
"github.com/ava-labs/avalanchego/utils/crypto/bls"
"github.com/ava-labs/avalanchego/utils/set"
"github.com/ava-labs/avalanchego/vms/components/chain"
@@ -30,6 +33,7 @@ import (
"github.com/ava-labs/coreth/precompile/contract"
"github.com/ava-labs/coreth/precompile/contracts/warp"
"github.com/ava-labs/coreth/predicate"
+ "github.com/ava-labs/coreth/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require"
@@ -42,6 +46,20 @@ var (
exampleWarpABI string
)
+type warpMsgFrom int
+
+const (
+ fromSubnet warpMsgFrom = iota
+ fromPrimary
+)
+
+type useWarpMsgSigners int
+
+const (
+ signersSubnet useWarpMsgSigners = iota
+ signersPrimary
+)
+
func TestSendWarpMessage(t *testing.T) {
require := require.New(t)
issuer, vm, _, _, _ := GenesisVM(t, true, genesisJSONDurango, "", "")
@@ -54,7 +72,7 @@ func TestSendWarpMessage(t *testing.T) {
logsSub := vm.eth.APIBackend.SubscribeAcceptedLogsEvent(acceptedLogsChan)
defer logsSub.Unsubscribe()
- payloadData := utils.RandomBytes(100)
+ payloadData := avagoUtils.RandomBytes(100)
warpSendMessageInput, err := warp.PackSendWarpMessage(payloadData)
require.NoError(err)
@@ -390,37 +408,154 @@ func TestReceiveWarpMessage(t *testing.T) {
require.NoError(vm.Shutdown(context.Background()))
}()
- acceptedLogsChan := make(chan []*types.Log, 10)
- logsSub := vm.eth.APIBackend.SubscribeAcceptedLogsEvent(acceptedLogsChan)
- defer logsSub.Unsubscribe()
+ // enable warp at the default genesis time
+ enableTime := upgrade.InitiallyActiveTime
+ enableConfig := warp.NewDefaultConfig(utils.TimeToNewUint64(enableTime))
+
+ // disable warp so we can re-enable it with RequirePrimaryNetworkSigners
+ disableTime := upgrade.InitiallyActiveTime.Add(10 * time.Second)
+ disableConfig := warp.NewDisableConfig(utils.TimeToNewUint64(disableTime))
- payloadData := utils.RandomBytes(100)
+ // re-enable warp with RequirePrimaryNetworkSigners
+ reEnableTime := disableTime.Add(10 * time.Second)
+ reEnableConfig := warp.NewConfig(
+ utils.TimeToNewUint64(reEnableTime),
+ 0, // QuorumNumerator
+ true, // RequirePrimaryNetworkSigners
+ )
+
+ vm.chainConfig.UpgradeConfig = params.UpgradeConfig{
+ PrecompileUpgrades: []params.PrecompileUpgrade{
+ {Config: enableConfig},
+ {Config: disableConfig},
+ {Config: reEnableConfig},
+ },
+ }
+
+ type test struct {
+ name string
+ sourceChainID ids.ID
+ msgFrom warpMsgFrom
+ useSigners useWarpMsgSigners
+ blockTime time.Time
+ }
+
+ blockGap := 2 * time.Second // Build blocks with a gap. Blocks built too quickly will have high fees.
+ tests := []test{
+ {
+ name: "subnet message should be signed by subnet without RequirePrimaryNetworkSigners",
+ sourceChainID: vm.ctx.ChainID,
+ msgFrom: fromSubnet,
+ useSigners: signersSubnet,
+ blockTime: upgrade.InitiallyActiveTime,
+ },
+ {
+ name: "P-Chain message should be signed by subnet without RequirePrimaryNetworkSigners",
+ sourceChainID: constants.PlatformChainID,
+ msgFrom: fromPrimary,
+ useSigners: signersSubnet,
+ blockTime: upgrade.InitiallyActiveTime.Add(blockGap),
+ },
+ {
+ name: "C-Chain message should be signed by subnet without RequirePrimaryNetworkSigners",
+ sourceChainID: testCChainID,
+ msgFrom: fromPrimary,
+ useSigners: signersSubnet,
+ blockTime: upgrade.InitiallyActiveTime.Add(2 * blockGap),
+ },
+ // Note here we disable warp and re-enable it with RequirePrimaryNetworkSigners
+ // by using reEnableTime.
+ {
+ name: "subnet message should be signed by subnet with RequirePrimaryNetworkSigners (unimpacted)",
+ sourceChainID: vm.ctx.ChainID,
+ msgFrom: fromSubnet,
+ useSigners: signersSubnet,
+ blockTime: reEnableTime,
+ },
+ {
+ name: "P-Chain message should be signed by subnet with RequirePrimaryNetworkSigners (unimpacted)",
+ sourceChainID: constants.PlatformChainID,
+ msgFrom: fromPrimary,
+ useSigners: signersSubnet,
+ blockTime: reEnableTime.Add(blockGap),
+ },
+ {
+ name: "C-Chain message should be signed by primary with RequirePrimaryNetworkSigners (impacted)",
+ sourceChainID: testCChainID,
+ msgFrom: fromPrimary,
+ useSigners: signersPrimary,
+ blockTime: reEnableTime.Add(2 * blockGap),
+ },
+ }
+ // Note each test corresponds to a block, the tests must be ordered by block
+ // time and cannot, eg be run in parallel or a separate golang test.
+ for _, test := range tests {
+ testReceiveWarpMessage(
+ t, issuer, vm, test.sourceChainID, test.msgFrom, test.useSigners, test.blockTime,
+ )
+ }
+}
+func testReceiveWarpMessage(
+ t *testing.T, issuer chan commonEng.Message, vm *VM,
+ sourceChainID ids.ID,
+ msgFrom warpMsgFrom, useSigners useWarpMsgSigners,
+ blockTime time.Time,
+) {
+ require := require.New(t)
+ payloadData := avagoUtils.RandomBytes(100)
addressedPayload, err := payload.NewAddressedCall(
testEthAddrs[0].Bytes(),
payloadData,
)
require.NoError(err)
+
+ vm.ctx.SubnetID = ids.GenerateTestID()
+ vm.ctx.NetworkID = testNetworkID
unsignedMessage, err := avalancheWarp.NewUnsignedMessage(
vm.ctx.NetworkID,
- vm.ctx.ChainID,
+ sourceChainID,
addressedPayload.Bytes(),
)
require.NoError(err)
- nodeID1 := ids.GenerateTestNodeID()
- blsSecretKey1, err := bls.NewSecretKey()
- require.NoError(err)
- blsPublicKey1 := bls.PublicFromSecretKey(blsSecretKey1)
- blsSignature1 := bls.Sign(blsSecretKey1, unsignedMessage.Bytes())
+ type signer struct {
+ networkID ids.ID
+ nodeID ids.NodeID
+ secret *bls.SecretKey
+ signature *bls.Signature
+ weight uint64
+ }
+ newSigner := func(networkID ids.ID, weight uint64) signer {
+ secret, err := bls.NewSecretKey()
+ require.NoError(err)
+ return signer{
+ networkID: networkID,
+ nodeID: ids.GenerateTestNodeID(),
+ secret: secret,
+ signature: bls.Sign(secret, unsignedMessage.Bytes()),
+ weight: weight,
+ }
+ }
- nodeID2 := ids.GenerateTestNodeID()
- blsSecretKey2, err := bls.NewSecretKey()
- require.NoError(err)
- blsPublicKey2 := bls.PublicFromSecretKey(blsSecretKey2)
- blsSignature2 := bls.Sign(blsSecretKey2, unsignedMessage.Bytes())
+ primarySigners := []signer{
+ newSigner(constants.PrimaryNetworkID, 50),
+ newSigner(constants.PrimaryNetworkID, 50),
+ }
+ subnetSigners := []signer{
+ newSigner(vm.ctx.SubnetID, 50),
+ newSigner(vm.ctx.SubnetID, 50),
+ }
+ signers := subnetSigners
+ if useSigners == signersPrimary {
+ signers = primarySigners
+ }
- blsAggregatedSignature, err := bls.AggregateSignatures([]*bls.Signature{blsSignature1, blsSignature2})
+ blsSignatures := make([]*bls.Signature, len(signers))
+ for i := range signers {
+ blsSignatures[i] = signers[i].signature
+ }
+ blsAggregatedSignature, err := bls.AggregateSignatures(blsSignatures)
require.NoError(err)
minimumValidPChainHeight := uint64(10)
@@ -428,30 +563,36 @@ func TestReceiveWarpMessage(t *testing.T) {
vm.ctx.ValidatorState = &validatorstest.State{
GetSubnetIDF: func(ctx context.Context, chainID ids.ID) (ids.ID, error) {
- return ids.Empty, nil
+ if msgFrom == fromPrimary {
+ return constants.PrimaryNetworkID, nil
+ }
+ return vm.ctx.SubnetID, nil
},
GetValidatorSetF: func(ctx context.Context, height uint64, subnetID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
if height < minimumValidPChainHeight {
return nil, getValidatorSetTestErr
}
- return map[ids.NodeID]*validators.GetValidatorOutput{
- nodeID1: {
- NodeID: nodeID1,
- PublicKey: blsPublicKey1,
- Weight: 50,
- },
- nodeID2: {
- NodeID: nodeID2,
- PublicKey: blsPublicKey2,
- Weight: 50,
- },
- }, nil
+ signers := subnetSigners
+ if subnetID == constants.PrimaryNetworkID {
+ signers = primarySigners
+ }
+
+ vdrOutput := make(map[ids.NodeID]*validators.GetValidatorOutput)
+ for _, s := range signers {
+ vdrOutput[s.nodeID] = &validators.GetValidatorOutput{
+ NodeID: s.nodeID,
+ PublicKey: bls.PublicFromSecretKey(s.secret),
+ Weight: s.weight,
+ }
+ }
+ return vdrOutput, nil
},
}
signersBitSet := set.NewBits()
- signersBitSet.Add(0)
- signersBitSet.Add(1)
+ for i := range signers {
+ signersBitSet.Add(i)
+ }
warpSignature := &avalancheWarp.BitSetSignature{
Signers: signersBitSet.Bytes(),
@@ -471,7 +612,7 @@ func TestReceiveWarpMessage(t *testing.T) {
getVerifiedWarpMessageTx, err := types.SignTx(
predicate.NewPredicateTx(
vm.chainConfig.ChainID,
- 0,
+ vm.txPool.Nonce(testEthAddrs[0]),
&warp.Module.Address,
1_000_000,
big.NewInt(225*params.GWei),
@@ -495,12 +636,28 @@ func TestReceiveWarpMessage(t *testing.T) {
validProposerCtx := &block.Context{
PChainHeight: minimumValidPChainHeight,
}
- vm.clock.Set(vm.clock.Time().Add(2 * time.Second))
+ vm.clock.Set(blockTime)
<-issuer
block2, err := vm.BuildBlockWithContext(context.Background(), validProposerCtx)
require.NoError(err)
+ // Require the block was built with a successful predicate result
+ ethBlock := block2.(*chain.BlockWrapper).Block.(*Block).ethBlock
+ headerPredicateResultsBytes, ok := predicate.GetPredicateResultBytes(ethBlock.Extra())
+ require.True(ok)
+ results, err := predicate.ParseResults(headerPredicateResultsBytes)
+ require.NoError(err)
+
+ // Predicate results encode the index of invalid warp messages in a bitset.
+ // An empty bitset indicates success.
+ txResultsBytes := results.GetResults(
+ getVerifiedWarpMessageTx.Hash(),
+ warp.ContractAddress,
+ )
+ bitset := set.BitsFromBytes(txResultsBytes)
+ require.Zero(bitset.Len()) // Empty bitset indicates success
+
block2VerifyWithCtx, ok := block2.(block.WithVerifyContext)
require.True(ok)
shouldVerifyWithCtx, err := block2VerifyWithCtx.ShouldVerifyWithContext(context.Background())
@@ -524,7 +681,6 @@ func TestReceiveWarpMessage(t *testing.T) {
require.NoError(block2.Accept(context.Background()))
vm.blockChain.DrainAcceptorQueue()
- ethBlock := block2.(*chain.BlockWrapper).Block.(*Block).ethBlock
verifiedMessageReceipts := vm.blockChain.GetReceiptsByHash(ethBlock.Hash())
require.Len(verifiedMessageReceipts, 1)
verifiedMessageTxReceipt := verifiedMessageReceipts[0]
@@ -532,7 +688,7 @@ func TestReceiveWarpMessage(t *testing.T) {
expectedOutput, err := warp.PackGetVerifiedWarpMessageOutput(warp.GetVerifiedWarpMessageOutput{
Message: warp.WarpMessage{
- SourceChainID: common.Hash(vm.ctx.ChainID),
+ SourceChainID: common.Hash(sourceChainID),
OriginSenderAddress: testEthAddrs[0],
Payload: payloadData,
},
diff --git a/precompile/contract/interfaces.go b/precompile/contract/interfaces.go
index 22035e04b9..b3ffb02fe2 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/coreth/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)
GetBalanceMultiCoin(common.Address, common.Hash) *big.Int
CreateAccount(common.Address)
diff --git a/precompile/contract/mocks.go b/precompile/contract/mocks.go
index 5f3e974b32..94bf9c5ba0 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/coreth/precompile/precompileconfig"
common "github.com/ethereum/go-ethereum/common"
+ uint256 "github.com/holiman/uint256"
gomock "go.uber.org/mock/gomock"
)
@@ -203,7 +204,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)
}
@@ -253,10 +254,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/warp/config.go b/precompile/contracts/warp/config.go
index d564546e5a..ce90cf282c 100644
--- a/precompile/contracts/warp/config.go
+++ b/precompile/contracts/warp/config.go
@@ -47,22 +47,24 @@ var (
// adds specific configuration for Warp.
type Config struct {
precompileconfig.Upgrade
- QuorumNumerator uint64 `json:"quorumNumerator"`
+ QuorumNumerator uint64 `json:"quorumNumerator"`
+ RequirePrimaryNetworkSigners bool `json:"requirePrimaryNetworkSigners"`
}
// NewConfig returns a config for a network upgrade at [blockTimestamp] that enables
// Warp with the given quorum numerator.
-func NewConfig(blockTimestamp *uint64, quorumNumerator uint64) *Config {
+func NewConfig(blockTimestamp *uint64, quorumNumerator uint64, requirePrimaryNetworkSigners bool) *Config {
return &Config{
- Upgrade: precompileconfig.Upgrade{BlockTimestamp: blockTimestamp},
- QuorumNumerator: quorumNumerator,
+ Upgrade: precompileconfig.Upgrade{BlockTimestamp: blockTimestamp},
+ QuorumNumerator: quorumNumerator,
+ RequirePrimaryNetworkSigners: requirePrimaryNetworkSigners,
}
}
// NewDefaultConfig returns a config for a network upgrade at [blockTimestamp] that enables
// Warp with the default quorum numerator (0 denotes using the default).
func NewDefaultConfig(blockTimestamp *uint64) *Config {
- return NewConfig(blockTimestamp, 0)
+ return NewConfig(blockTimestamp, 0, false)
}
// NewDisableConfig returns config for a network upgrade at [blockTimestamp]
@@ -198,11 +200,19 @@ func (c *Config) VerifyPredicate(predicateContext *precompileconfig.PredicateCon
}
log.Debug("verifying warp message", "warpMsg", warpMsg, "quorumNum", quorumNumerator, "quorumDenom", WarpQuorumDenominator)
+
+ // Wrap validators.State on the chain snow context to special case the Primary Network
+ state := warpValidators.NewState(
+ predicateContext.SnowCtx.ValidatorState,
+ predicateContext.SnowCtx.SubnetID,
+ warpMsg.SourceChainID,
+ c.RequirePrimaryNetworkSigners,
+ )
err = warpMsg.Signature.Verify(
context.Background(),
&warpMsg.UnsignedMessage,
predicateContext.SnowCtx.NetworkID,
- warpValidators.NewState(predicateContext.SnowCtx), // Wrap validators.State on the chain snow context to special case the Primary Network
+ state,
predicateContext.ProposerVMBlockCtx.PChainHeight,
quorumNumerator,
WarpQuorumDenominator,
diff --git a/precompile/contracts/warp/config_test.go b/precompile/contracts/warp/config_test.go
index 419b9bbeee..526a5ce51a 100644
--- a/precompile/contracts/warp/config_test.go
+++ b/precompile/contracts/warp/config_test.go
@@ -16,24 +16,24 @@ import (
func TestVerify(t *testing.T) {
tests := map[string]testutils.ConfigVerifyTest{
"quorum numerator less than minimum": {
- Config: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum-1),
+ Config: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum-1, false),
ExpectedError: fmt.Sprintf("cannot specify quorum numerator (%d) < min quorum numerator (%d)", WarpQuorumNumeratorMinimum-1, WarpQuorumNumeratorMinimum),
},
"quorum numerator greater than quorum denominator": {
- Config: NewConfig(utils.NewUint64(3), WarpQuorumDenominator+1),
+ Config: NewConfig(utils.NewUint64(3), WarpQuorumDenominator+1, false),
ExpectedError: fmt.Sprintf("cannot specify quorum numerator (%d) > quorum denominator (%d)", WarpQuorumDenominator+1, WarpQuorumDenominator),
},
"default quorum numerator": {
Config: NewDefaultConfig(utils.NewUint64(3)),
},
"valid quorum numerator 1 less than denominator": {
- Config: NewConfig(utils.NewUint64(3), WarpQuorumDenominator-1),
+ Config: NewConfig(utils.NewUint64(3), WarpQuorumDenominator-1, false),
},
"valid quorum numerator 1 more than minimum": {
- Config: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+1),
+ Config: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+1, false),
},
"invalid cannot activated before Durango activation": {
- Config: NewConfig(utils.NewUint64(3), 0),
+ Config: NewConfig(utils.NewUint64(3), 0, false),
ChainConfig: func() precompileconfig.ChainConfig {
config := precompileconfig.NewMockChainConfig(gomock.NewController(t))
config.EXPECT().IsDurango(gomock.Any()).Return(false)
@@ -66,8 +66,8 @@ func TestEqualWarpConfig(t *testing.T) {
},
"different quorum numerator": {
- Config: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+1),
- Other: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+2),
+ Config: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+1, false),
+ Other: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+2, false),
Expected: false,
},
@@ -78,8 +78,8 @@ func TestEqualWarpConfig(t *testing.T) {
},
"same non-default config": {
- Config: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+5),
- Other: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+5),
+ Config: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+5, false),
+ Other: NewConfig(utils.NewUint64(3), WarpQuorumNumeratorMinimum+5, false),
Expected: true,
},
}
diff --git a/precompile/contracts/warp/predicate_test.go b/precompile/contracts/warp/predicate_test.go
index d0351b844b..04ddd46705 100644
--- a/precompile/contracts/warp/predicate_test.go
+++ b/precompile/contracts/warp/predicate_test.go
@@ -25,7 +25,6 @@ import (
"github.com/ava-labs/coreth/predicate"
"github.com/ava-labs/coreth/utils"
"github.com/stretchr/testify/require"
- "go.uber.org/mock/gomock"
)
const pChainHeight uint64 = 1337
@@ -49,7 +48,6 @@ var (
numTestVdrs = 10_000
testVdrs []*testValidator
vdrs map[ids.NodeID]*validators.GetValidatorOutput
- tests []signatureTest
predicateTests = make(map[string]testutils.PredicateTest)
)
@@ -132,15 +130,6 @@ func newTestValidator() *testValidator {
}
}
-type signatureTest struct {
- name string
- stateF func(*gomock.Controller) validators.State
- quorumNum uint64
- quorumDen uint64
- msgF func(*require.Assertions) *avalancheWarp.Message
- err error
-}
-
// createWarpMessage constructs a signed warp message using the global variable [unsignedMsg]
// and the first [numKeys] signatures from [blsSignatures]
func createWarpMessage(numKeys int) *avalancheWarp.Message {
@@ -228,6 +217,12 @@ func createValidPredicateTest(snowCtx *snow.Context, numKeys uint64, predicateBy
}
func TestWarpMessageFromPrimaryNetwork(t *testing.T) {
+ for _, requirePrimaryNetworkSigners := range []bool{true, false} {
+ testWarpMessageFromPrimaryNetwork(t, requirePrimaryNetworkSigners)
+ }
+}
+
+func testWarpMessageFromPrimaryNetwork(t *testing.T, requirePrimaryNetworkSigners bool) {
require := require.New(t)
numKeys := 10
cChainID := ids.GenerateTestID()
@@ -273,13 +268,17 @@ func TestWarpMessageFromPrimaryNetwork(t *testing.T) {
return constants.PrimaryNetworkID, nil // Return Primary Network SubnetID
},
GetValidatorSetF: func(ctx context.Context, height uint64, subnetID ids.ID) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
- require.Equal(snowCtx.SubnetID, subnetID)
+ expectedSubnetID := snowCtx.SubnetID
+ if requirePrimaryNetworkSigners {
+ expectedSubnetID = constants.PrimaryNetworkID
+ }
+ require.Equal(expectedSubnetID, subnetID)
return getValidatorsOutput, nil
},
}
test := testutils.PredicateTest{
- Config: NewDefaultConfig(utils.NewUint64(0)),
+ Config: NewConfig(utils.NewUint64(0), 0, requirePrimaryNetworkSigners),
PredicateContext: &precompileconfig.PredicateContext{
SnowCtx: snowCtx,
ProposerVMBlockCtx: &block.Context{
@@ -576,7 +575,7 @@ func TestWarpSignatureWeightsNonDefaultQuorumNumerator(t *testing.T) {
name := fmt.Sprintf("non-default quorum %d signature(s)", numSigners)
tests[name] = testutils.PredicateTest{
- Config: NewConfig(utils.NewUint64(0), uint64(nonDefaultQuorumNumerator)),
+ Config: NewConfig(utils.NewUint64(0), uint64(nonDefaultQuorumNumerator), false),
PredicateContext: &precompileconfig.PredicateContext{
SnowCtx: snowCtx,
ProposerVMBlockCtx: &block.Context{
diff --git a/precompile/contracts/warp/signature_verification_test.go b/precompile/contracts/warp/signature_verification_test.go
index dadefeb4d6..5b54cd29b8 100644
--- a/precompile/contracts/warp/signature_verification_test.go
+++ b/precompile/contracts/warp/signature_verification_test.go
@@ -10,6 +10,7 @@ import (
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/snow/validators"
+ "github.com/ava-labs/avalanchego/snow/validators/validatorsmock"
"github.com/ava-labs/avalanchego/utils/crypto/bls"
"github.com/ava-labs/avalanchego/utils/set"
avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp"
@@ -17,14 +18,23 @@ import (
"go.uber.org/mock/gomock"
)
-// This test copies the test coverage from https://github.com/ava-labs/avalanchego/blob/v1.10.0/vms/platformvm/warp/signature_test.go#L137.
+type signatureTest struct {
+ name string
+ stateF func(*gomock.Controller) validators.State
+ quorumNum uint64
+ quorumDen uint64
+ msgF func(*require.Assertions) *avalancheWarp.Message
+ err error
+}
+
+// This test copies the test coverage from https://github.com/ava-labs/avalanchego/blob/0117ab96/vms/platformvm/warp/signature_test.go#L137.
// These tests are only expected to fail if there is a breaking change in AvalancheGo that unexpectedly changes behavior.
func TestSignatureVerification(t *testing.T) {
- tests = []signatureTest{
+ tests := []signatureTest{
{
name: "can't get subnetID",
stateF: func(ctrl *gomock.Controller) validators.State {
- state := validators.NewMockState(ctrl)
+ state := validatorsmock.NewState(ctrl)
state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, errTest)
return state
},
@@ -50,7 +60,7 @@ func TestSignatureVerification(t *testing.T) {
{
name: "can't get validator set",
stateF: func(ctrl *gomock.Controller) validators.State {
- state := validators.NewMockState(ctrl)
+ state := validatorsmock.NewState(ctrl)
state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil)
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(nil, errTest)
return state
@@ -77,7 +87,7 @@ func TestSignatureVerification(t *testing.T) {
{
name: "weight overflow",
stateF: func(ctrl *gomock.Controller) validators.State {
- state := validators.NewMockState(ctrl)
+ state := validatorsmock.NewState(ctrl)
state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil)
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(map[ids.NodeID]*validators.GetValidatorOutput{
testVdrs[0].nodeID: {
@@ -117,7 +127,7 @@ func TestSignatureVerification(t *testing.T) {
{
name: "invalid bit set index",
stateF: func(ctrl *gomock.Controller) validators.State {
- state := validators.NewMockState(ctrl)
+ state := validatorsmock.NewState(ctrl)
state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil)
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil)
return state
@@ -147,7 +157,7 @@ func TestSignatureVerification(t *testing.T) {
{
name: "unknown index",
stateF: func(ctrl *gomock.Controller) validators.State {
- state := validators.NewMockState(ctrl)
+ state := validatorsmock.NewState(ctrl)
state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil)
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil)
return state
@@ -180,7 +190,7 @@ func TestSignatureVerification(t *testing.T) {
{
name: "insufficient weight",
stateF: func(ctrl *gomock.Controller) validators.State {
- state := validators.NewMockState(ctrl)
+ state := validatorsmock.NewState(ctrl)
state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil)
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil)
return state
@@ -224,7 +234,7 @@ func TestSignatureVerification(t *testing.T) {
{
name: "can't parse sig",
stateF: func(ctrl *gomock.Controller) validators.State {
- state := validators.NewMockState(ctrl)
+ state := validatorsmock.NewState(ctrl)
state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil)
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil)
return state
@@ -258,7 +268,7 @@ func TestSignatureVerification(t *testing.T) {
{
name: "no validators",
stateF: func(ctrl *gomock.Controller) validators.State {
- state := validators.NewMockState(ctrl)
+ state := validatorsmock.NewState(ctrl)
state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil)
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(nil, nil)
return state
@@ -293,7 +303,7 @@ func TestSignatureVerification(t *testing.T) {
{
name: "invalid signature (substitute)",
stateF: func(ctrl *gomock.Controller) validators.State {
- state := validators.NewMockState(ctrl)
+ state := validatorsmock.NewState(ctrl)
state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil)
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil)
return state
@@ -337,7 +347,7 @@ func TestSignatureVerification(t *testing.T) {
{
name: "invalid signature (missing one)",
stateF: func(ctrl *gomock.Controller) validators.State {
- state := validators.NewMockState(ctrl)
+ state := validatorsmock.NewState(ctrl)
state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil)
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil)
return state
@@ -377,7 +387,7 @@ func TestSignatureVerification(t *testing.T) {
{
name: "invalid signature (extra one)",
stateF: func(ctrl *gomock.Controller) validators.State {
- state := validators.NewMockState(ctrl)
+ state := validatorsmock.NewState(ctrl)
state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil)
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil)
return state
@@ -422,7 +432,7 @@ func TestSignatureVerification(t *testing.T) {
{
name: "valid signature",
stateF: func(ctrl *gomock.Controller) validators.State {
- state := validators.NewMockState(ctrl)
+ state := validatorsmock.NewState(ctrl)
state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil)
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil)
return state
@@ -466,7 +476,7 @@ func TestSignatureVerification(t *testing.T) {
{
name: "valid signature (boundary)",
stateF: func(ctrl *gomock.Controller) validators.State {
- state := validators.NewMockState(ctrl)
+ state := validatorsmock.NewState(ctrl)
state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil)
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(vdrs, nil)
return state
@@ -510,7 +520,7 @@ func TestSignatureVerification(t *testing.T) {
{
name: "valid signature (missing key)",
stateF: func(ctrl *gomock.Controller) validators.State {
- state := validators.NewMockState(ctrl)
+ state := validatorsmock.NewState(ctrl)
state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil)
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(map[ids.NodeID]*validators.GetValidatorOutput{
testVdrs[0].nodeID: {
@@ -571,7 +581,7 @@ func TestSignatureVerification(t *testing.T) {
{
name: "valid signature (duplicate key)",
stateF: func(ctrl *gomock.Controller) validators.State {
- state := validators.NewMockState(ctrl)
+ state := validatorsmock.NewState(ctrl)
state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(sourceSubnetID, nil)
state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, sourceSubnetID).Return(map[ids.NodeID]*validators.GetValidatorOutput{
testVdrs[0].nodeID: {
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/scripts/build_test.sh b/scripts/build_test.sh
index c62dc95789..460ebcd564 100755
--- a/scripts/build_test.sh
+++ b/scripts/build_test.sh
@@ -15,4 +15,44 @@ source "$CORETH_PATH"/scripts/constants.sh
# We pass in the arguments to this script directly to enable easily passing parameters such as enabling race detection,
# parallelism, and test coverage.
-go test -shuffle=on -race -timeout="${TIMEOUT:-600s}" -coverprofile=coverage.out -covermode=atomic ./... "$@"
+# DO NOT RUN tests from the top level "tests" directory since they are run by ginkgo
+race="-race"
+if [[ -n "${NO_RACE:-}" ]]; then
+ race=""
+fi
+
+# MAX_RUNS bounds the attempts to retry the tests before giving up
+# This is useful for flaky tests
+MAX_RUNS=4
+for ((i = 1; i <= MAX_RUNS; i++));
+do
+ # shellcheck disable=SC2046
+ go test -shuffle=on ${race:-} -timeout="${TIMEOUT:-600s}" -coverprofile=coverage.out -covermode=atomic "$@" ./... | tee test.out || command_status=$?
+
+ # If the test passed, exit
+ if [[ ${command_status:-0} == 0 ]]; then
+ rm test.out
+ exit 0
+ else
+ unset command_status # Clear the error code for the next run
+ fi
+
+ # If the test failed, print the output
+ unexpected_failures=$(
+ # First grep pattern corresponds to test failures, second pattern corresponds to test panics due to timeouts
+ (grep "^--- FAIL" test.out | awk '{print $3}' || grep -E '^\s+Test.+ \(' test.out | awk '{print $1}') |
+ sort -u | comm -23 - ./scripts/known_flakes.txt
+ )
+ if [ -n "${unexpected_failures}" ]; then
+ echo "Unexpected test failures: ${unexpected_failures}"
+ exit 1
+ fi
+
+ # Note the absence of unexpected failures cannot be indicative that we only need to run the tests that failed,
+ # for example a test may panic and cause subsequent tests in that package to not run.
+ # So we loop here.
+ echo "Test run $i failed with known flakes, retrying..."
+done
+
+# If we reach here, we have failed all retries
+exit 1
diff --git a/scripts/constants.sh b/scripts/constants.sh
index e4d0e81744..42b4f16829 100644
--- a/scripts/constants.sh
+++ b/scripts/constants.sh
@@ -7,6 +7,8 @@ set -euo pipefail
# Set the PATHS
GOPATH="$(go env GOPATH)"
+DEFAULT_PLUGIN_DIR="${HOME}/.avalanchego/plugins"
+DEFAULT_VM_ID="srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy"
# Set binary location
binary_path=${CORETH_BINARY_PATH:-"$GOPATH/src/github.com/ava-labs/avalanchego/build/plugins/evm"}
diff --git a/scripts/known_flakes.txt b/scripts/known_flakes.txt
new file mode 100644
index 0000000000..05c41794ba
--- /dev/null
+++ b/scripts/known_flakes.txt
@@ -0,0 +1,11 @@
+TestClientCancelWebsocket
+TestExpDecaySampleNanosecondRegression
+TestGolangBindings
+TestMempoolAtmTxsAppGossipHandlingDiscardedTx
+TestMempoolEthTxsAppGossipHandling
+TestResumeSyncAccountsTrieInterrupted
+TestResyncNewRootAfterDeletes
+TestTransactionSkipIndexing
+TestVMShutdownWhileSyncing
+TestWalletNotifications
+TestWebsocketLargeRead
\ No newline at end of file
diff --git a/scripts/shellcheck.sh b/scripts/shellcheck.sh
index 61fc09f90b..f57b853362 100755
--- a/scripts/shellcheck.sh
+++ b/scripts/shellcheck.sh
@@ -4,6 +4,14 @@ set -euo pipefail
VERSION="v0.9.0"
+# Scripts that are sourced from upstream and not maintained in this repo will not be shellchecked.
+# Also ignore the local avalanchego clone.
+IGNORED_FILES="
+ cmd/evm/transition-test.sh
+ metrics/validate.sh
+ avalanchego/*
+"
+
function get_version {
local target_path=$1
if command -v "${target_path}" > /dev/null; then
@@ -36,4 +44,12 @@ else
fi
fi
-find "${REPO_ROOT}" -name "*.sh" -type f -print0 | xargs -0 "${SHELLCHECK}" "${@}"
+IGNORED_CONDITIONS=()
+for file in ${IGNORED_FILES}; do
+ if [[ -n "${IGNORED_CONDITIONS-}" ]]; then
+ IGNORED_CONDITIONS+=(-o)
+ fi
+ IGNORED_CONDITIONS+=(-path "${REPO_ROOT}/${file}" -prune)
+done
+
+find "${REPO_ROOT}" \( "${IGNORED_CONDITIONS[@]}" \) -o -type f -name "*.sh" -print0 | xargs -0 "${SHELLCHECK}" "${@}"
diff --git a/scripts/versions.sh b/scripts/versions.sh
index 5bed52626d..4154fe89c0 100644
--- a/scripts/versions.sh
+++ b/scripts/versions.sh
@@ -6,4 +6,4 @@
set -euo pipefail
# Don't export them as they're used in the context of other calls
-AVALANCHE_VERSION=${AVALANCHE_VERSION:-'1ac532af76df'}
+AVALANCHE_VERSION=${AVALANCHE_VERSION:-'v1.11.11'}
diff --git a/signer/core/apitypes/types.go b/signer/core/apitypes/types.go
index 2d5cb7f79b..9f85e9ce7e 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/sync/client/client_test.go b/sync/client/client_test.go
index 58a0d6f3b4..9902c4e694 100644
--- a/sync/client/client_test.go
+++ b/sync/client/client_test.go
@@ -25,7 +25,7 @@ import (
"github.com/ava-labs/coreth/sync/handlers"
handlerstats "github.com/ava-labs/coreth/sync/handlers/stats"
"github.com/ava-labs/coreth/sync/syncutils"
- "github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/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)
@@ -794,7 +794,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 2ca5c629a3..7b0124d8f9 100644
--- a/sync/handlers/block_request_test.go
+++ b/sync/handlers/block_request_test.go
@@ -17,7 +17,7 @@ import (
"github.com/ava-labs/coreth/params"
"github.com/ava-labs/coreth/plugin/evm/message"
"github.com/ava-labs/coreth/sync/handlers/stats"
- "github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/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 9519a349f0..4bb0fcfb61 100644
--- a/sync/handlers/leafs_request.go
+++ b/sync/handlers/leafs_request.go
@@ -19,6 +19,7 @@ import (
"github.com/ava-labs/coreth/sync/handlers/stats"
"github.com/ava-labs/coreth/sync/syncutils"
"github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/triedb"
"github.com/ava-labs/coreth/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
@@ -42,14 +43,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 9be7c3f616..1c8201fdad 100644
--- a/sync/handlers/leafs_request_test.go
+++ b/sync/handlers/leafs_request_test.go
@@ -17,6 +17,7 @@ import (
"github.com/ava-labs/coreth/sync/handlers/stats"
"github.com/ava-labs/coreth/sync/syncutils"
"github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/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 0a43781025..305b981b8f 100644
--- a/sync/statesync/state_syncer.go
+++ b/sync/statesync/state_syncer.go
@@ -10,7 +10,7 @@ import (
"github.com/ava-labs/coreth/core/state/snapshot"
syncclient "github.com/ava-labs/coreth/sync/client"
- "github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/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 82f063a864..31b2518187 100644
--- a/sync/statesync/sync_test.go
+++ b/sync/statesync/sync_test.go
@@ -22,6 +22,7 @@ import (
handlerstats "github.com/ava-labs/coreth/sync/handlers/stats"
"github.com/ava-labs/coreth/sync/syncutils"
"github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/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 418eb675fd..0a5ec45164 100644
--- a/sync/statesync/test_sync.go
+++ b/sync/statesync/test_sync.go
@@ -12,7 +12,7 @@ import (
"github.com/ava-labs/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/sync/syncutils"
- "github.com/ava-labs/coreth/trie"
+ "github.com/ava-labs/coreth/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 ea1b7eff04..b66e53e905 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,6 +14,8 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/trie"
"github.com/ava-labs/coreth/trie/trienode"
+ "github.com/ava-labs/coreth/triedb"
+ "github.com/holiman/uint256"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
@@ -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/state_test_util.go b/tests/state_test_util.go
index 29e934efe5..a919bbb725 100644
--- a/tests/state_test_util.go
+++ b/tests/state_test_util.go
@@ -27,32 +27,40 @@
package tests
import (
- "github.com/ava-labs/coreth/core"
"github.com/ava-labs/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/state"
"github.com/ava-labs/coreth/core/state/snapshot"
"github.com/ava-labs/coreth/core/types"
- "github.com/ava-labs/coreth/trie"
- "github.com/ava-labs/coreth/trie/triedb/hashdb"
- "github.com/ava-labs/coreth/trie/triedb/pathdb"
+ "github.com/ava-labs/coreth/triedb"
+ "github.com/ava-labs/coreth/triedb/hashdb"
+ "github.com/ava-labs/coreth/triedb/pathdb"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
+ "github.com/holiman/uint256"
)
-func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter bool, scheme string) (*trie.Database, *snapshot.Tree, *state.StateDB) {
- tconf := &trie.Config{Preimages: true}
+// 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 := trie.NewDatabase(db, tconf)
+ 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, a.Balance)
+ statedb.SetBalance(addr, uint256.MustFromBig(a.Balance))
for k, v := range a.Storage {
statedb.SetState(addr, k, v)
}
@@ -60,6 +68,7 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter boo
// 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{
@@ -71,5 +80,19 @@ func MakePreState(db ethdb.Database, accounts core.GenesisAlloc, snapshotter boo
snaps, _ = snapshot.New(snapconfig, db, triedb, common.Hash{}, root)
}
statedb, _ = state.New(root, sdb, snaps)
- return triedb, snaps, statedb
+ 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 ac317ecb33..0480a8e115 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 156e04d940..9eb9bf6f29 100644
--- a/trie/database_test.go
+++ b/trie/database_test.go
@@ -28,23 +28,135 @@ package trie
import (
"github.com/ava-labs/coreth/core/rawdb"
- "github.com/ava-labs/coreth/trie/triedb/hashdb"
- "github.com/ava-labs/coreth/trie/triedb/pathdb"
+ "github.com/ava-labs/coreth/core/types"
+ "github.com/ava-labs/coreth/trie/trienode"
+ "github.com/ava-labs/coreth/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 3cbd807897..966f689c2a 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 aae0a11b3a..3fb0ba363d 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 bb81f85819..f47c49bf8b 100644
--- a/trie/secure_trie.go
+++ b/trie/secure_trie.go
@@ -29,6 +29,7 @@ package trie
import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/trie/trienode"
+ "github.com/ava-labs/coreth/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 8081f9b500..440910b89b 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 daf1185e49..ec5006df8f 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 3a7767d360..b68f6e1b2c 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 71cf0d5530..b11c69f754 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 211b60a2ed..89460a5f03 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 66c3d60627..f12a4d0f6e 100644
--- a/trie/trie.go
+++ b/trie/trie.go
@@ -34,6 +34,7 @@ import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/trie/trienode"
+ "github.com/ava-labs/coreth/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 99fce64436..0b5e8a6cf9 100644
--- a/trie/trie_reader.go
+++ b/trie/trie_reader.go
@@ -29,33 +29,21 @@ package trie
import (
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/trie/triestate"
+ "github.com/ava-labs/coreth/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 81402d5cbd..61014feb70 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,20 +740,20 @@ 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 {
+ if exp, root := common.HexToHash("dfb9311ba769a2bdb9d4126d0ae49046f9551063c738d10b9021343fb6550b3f"), trie.Hash(); exp != root {
t.Errorf("1: got %x, exp %x", root, exp)
}
trie.MustUpdate(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001338"), accounts[4])
- if exp, root := common.HexToHash("ec63b967e98a5720e7f720482151963982890d82c9093c0d486b7eb8883a66b1"), trie.Hash(); exp != root {
+ if exp, root := common.HexToHash("21d0f2f4c72fed985d1196993a784d36321a44085bbe60990cb65b7bc478f52b"), trie.Hash(); exp != root {
t.Errorf("2: got %x, exp %x", root, exp)
}
trie.MustUpdate(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000001339"), accounts[4])
- if exp, root := common.HexToHash("0608c1d1dc3905fa22204c7a0e43644831c3b6d3def0f274be623a948197e64a"), trie.Hash(); exp != root {
+ if exp, root := common.HexToHash("e71f7f0bbcd0daf37bc03a3389408eced206e796ed6d76186387847e2193ac4e"), 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])
}
@@ -779,7 +774,7 @@ func TestCommitAfterHash(t *testing.T) {
trie.Hash()
trie.Commit(false)
root := trie.Hash()
- exp := common.HexToHash("72f9d3f3fe1e1dd7b8936442e7642aef76371472d94319900790053c493f3fe6")
+ exp := common.HexToHash("6dcf62a0c1575866467426b55e3acab075312b38c6b112457c3cd23ab9b94fc1")
if exp != root {
t.Errorf("got %x, exp %x", root, exp)
}
@@ -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("2e4ec8744409f17d6a3fe1540282e3ba0cf434b3a11974a2a033e3caa476a83c")},
+ {200, common.FromHex("f7abb2c93e89e7e68696d855fa8982cb454190dcd7e4e7f4c7d60fd5c9f465f3")},
+ {2000, common.FromHex("226f735a06e25b5306216d52ce0652ba9df17341bb0d1ae8be5484d691e8fe5c")},
} {
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 a8b84431b6..82cef89c0e 100644
--- a/trie/verkle.go
+++ b/trie/verkle.go
@@ -20,11 +20,11 @@ import (
"encoding/binary"
"errors"
"fmt"
- "math/big"
"github.com/ava-labs/coreth/core/types"
"github.com/ava-labs/coreth/trie/trienode"
"github.com/ava-labs/coreth/trie/utils"
+ "github.com/ava-labs/coreth/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 b0a382e2af..2f45a8ef9a 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/coreth/core/rawdb"
"github.com/ava-labs/coreth/core/types"
- "github.com/ava-labs/coreth/trie/triedb/pathdb"
"github.com/ava-labs/coreth/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 a3bd3ecc65..295c723bbc 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/coreth/trie/triedb/hashdb"
- "github.com/ava-labs/coreth/trie/triedb/pathdb"
+ "github.com/ava-labs/coreth/trie"
"github.com/ava-labs/coreth/trie/trienode"
"github.com/ava-labs/coreth/trie/triestate"
+ "github.com/ava-labs/coreth/triedb/database"
+ "github.com/ava-labs/coreth/triedb/hashdb"
+ "github.com/ava-labs/coreth/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 5f76c6dab4..ed7d237480 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 1502372f7c..adb3857ab3 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"
diff --git a/utils/snow.go b/utils/snow.go
index 92a1d236c1..77368f0750 100644
--- a/utils/snow.go
+++ b/utils/snow.go
@@ -26,7 +26,7 @@ func TestSnowContext() *snow.Context {
PublicKey: pk,
Log: logging.NoLog{},
BCLookup: ids.NewAliaser(),
- Metrics: metrics.NewMultiGatherer(),
+ Metrics: metrics.NewPrefixGatherer(),
ChainDataDir: "",
ValidatorState: &validatorstest.State{},
}
diff --git a/warp/service.go b/warp/service.go
index 6eec775e37..8b92da80db 100644
--- a/warp/service.go
+++ b/warp/service.go
@@ -9,11 +9,12 @@ import (
"fmt"
"github.com/ava-labs/avalanchego/ids"
+ "github.com/ava-labs/avalanchego/snow/validators"
"github.com/ava-labs/avalanchego/vms/platformvm/warp"
"github.com/ava-labs/avalanchego/vms/platformvm/warp/payload"
"github.com/ava-labs/coreth/peer"
"github.com/ava-labs/coreth/warp/aggregator"
- "github.com/ava-labs/coreth/warp/validators"
+ warpValidators "github.com/ava-labs/coreth/warp/validators"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
)
@@ -25,18 +26,20 @@ type API struct {
networkID uint32
sourceSubnetID, sourceChainID ids.ID
backend Backend
- state *validators.State
+ state validators.State
client peer.NetworkClient
+ requirePrimaryNetworkSigners func() bool
}
-func NewAPI(networkID uint32, sourceSubnetID ids.ID, sourceChainID ids.ID, state *validators.State, backend Backend, client peer.NetworkClient) *API {
+func NewAPI(networkID uint32, sourceSubnetID ids.ID, sourceChainID ids.ID, state validators.State, backend Backend, client peer.NetworkClient, requirePrimaryNetworkSigners func() bool) *API {
return &API{
- networkID: networkID,
- sourceSubnetID: sourceSubnetID,
- sourceChainID: sourceChainID,
- backend: backend,
- state: state,
- client: client,
+ networkID: networkID,
+ sourceSubnetID: sourceSubnetID,
+ sourceChainID: sourceChainID,
+ backend: backend,
+ state: state,
+ client: client,
+ requirePrimaryNetworkSigners: requirePrimaryNetworkSigners,
}
}
@@ -104,7 +107,8 @@ func (a *API) aggregateSignatures(ctx context.Context, unsignedMessage *warp.Uns
return nil, err
}
- validators, totalWeight, err := warp.GetCanonicalValidatorSet(ctx, a.state, pChainHeight, subnetID)
+ state := warpValidators.NewState(a.state, a.sourceSubnetID, a.sourceChainID, a.requirePrimaryNetworkSigners())
+ validators, totalWeight, err := warp.GetCanonicalValidatorSet(ctx, state, pChainHeight, subnetID)
if err != nil {
return nil, fmt.Errorf("failed to get validator set: %w", err)
}
diff --git a/warp/validators/state.go b/warp/validators/state.go
index c2929a5807..61e6e14cfe 100644
--- a/warp/validators/state.go
+++ b/warp/validators/state.go
@@ -7,7 +7,6 @@ import (
"context"
"github.com/ava-labs/avalanchego/ids"
- "github.com/ava-labs/avalanchego/snow"
"github.com/ava-labs/avalanchego/snow/validators"
"github.com/ava-labs/avalanchego/utils/constants"
)
@@ -19,18 +18,22 @@ var _ validators.State = (*State)(nil)
// signatures from a threshold of the RECEIVING subnet validator set rather than the full Primary Network
// since the receiving subnet already relies on a majority of its validators being correct.
type State struct {
- chainContext *snow.Context
validators.State
+ mySubnetID ids.ID
+ sourceChainID ids.ID
+ requirePrimaryNetworkSigners bool
}
// NewState returns a wrapper of [validators.State] which special cases the handling of the Primary Network.
//
-// The wrapped state will return the chainContext's Subnet validator set instead of the Primary Network when
+// The wrapped state will return the [mySubnetID's] validator set instead of the Primary Network when
// the Primary Network SubnetID is passed in.
-func NewState(chainContext *snow.Context) *State {
+func NewState(state validators.State, mySubnetID ids.ID, sourceChainID ids.ID, requirePrimaryNetworkSigners bool) *State {
return &State{
- chainContext: chainContext,
- State: chainContext.ValidatorState,
+ State: state,
+ mySubnetID: mySubnetID,
+ sourceChainID: sourceChainID,
+ requirePrimaryNetworkSigners: requirePrimaryNetworkSigners,
}
}
@@ -39,13 +42,14 @@ func (s *State) GetValidatorSet(
height uint64,
subnetID ids.ID,
) (map[ids.NodeID]*validators.GetValidatorOutput, error) {
- // If the subnetID is anything other than the Primary Network, this is a direct
- // passthrough
- if subnetID != constants.PrimaryNetworkID {
+ // If the subnetID is anything other than the Primary Network, or Primary
+ // Network signers are required (except P-Chain), this is a direct passthrough.
+ usePrimary := s.requirePrimaryNetworkSigners && s.sourceChainID != constants.PlatformChainID
+ if usePrimary || subnetID != constants.PrimaryNetworkID {
return s.State.GetValidatorSet(ctx, height, subnetID)
}
// If the requested subnet is the primary network, then we return the validator
// set for the Subnet that is receiving the message instead.
- return s.State.GetValidatorSet(ctx, height, s.chainContext.SubnetID)
+ return s.State.GetValidatorSet(ctx, height, s.mySubnetID)
}
diff --git a/warp/validators/state_test.go b/warp/validators/state_test.go
index e06d1374ad..610927bb95 100644
--- a/warp/validators/state_test.go
+++ b/warp/validators/state_test.go
@@ -9,6 +9,7 @@ import (
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/snow/validators"
+ "github.com/ava-labs/avalanchego/snow/validators/validatorsmock"
"github.com/ava-labs/avalanchego/utils/constants"
"github.com/ava-labs/coreth/utils"
"github.com/stretchr/testify/require"
@@ -22,11 +23,11 @@ func TestGetValidatorSetPrimaryNetwork(t *testing.T) {
mySubnetID := ids.GenerateTestID()
otherSubnetID := ids.GenerateTestID()
- mockState := validators.NewMockState(ctrl)
+ mockState := validatorsmock.NewState(ctrl)
snowCtx := utils.TestSnowContext()
snowCtx.SubnetID = mySubnetID
snowCtx.ValidatorState = mockState
- state := NewState(snowCtx)
+ state := NewState(snowCtx.ValidatorState, snowCtx.SubnetID, snowCtx.ChainID, false)
// Expect that requesting my validator set returns my validator set
mockState.EXPECT().GetValidatorSet(gomock.Any(), gomock.Any(), mySubnetID).Return(make(map[ids.NodeID]*validators.GetValidatorOutput), nil)
output, err := state.GetValidatorSet(context.Background(), 10, mySubnetID)