forked from cerc-io/plugeth
Merge branch 'master' into release/1.13
This commit is contained in:
commit
dd938d103d
@ -84,6 +84,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 {
|
||||
ethereum.GasEstimator
|
||||
ethereum.GasPricer
|
||||
ethereum.GasPricer1559
|
||||
ethereum.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)
|
||||
@ -93,38 +98,6 @@ type ContractTransactor interface {
|
||||
|
||||
// PendingNonceAt retrieves the current pending nonce associated with an account.
|
||||
PendingNonceAt(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 ethereum.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 ethereum.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 ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error)
|
||||
}
|
||||
|
||||
// DeployBackend wraps the operations needed by WaitMined and WaitDeployed.
|
||||
@ -133,6 +106,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 {
|
||||
ethereum.LogFilterer
|
||||
}
|
||||
|
||||
// ContractBackend defines the methods needed to work with contracts on a read-write basis.
|
||||
type ContractBackend interface {
|
||||
ContractCaller
|
||||
|
@ -18,958 +18,35 @@ package backends
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"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/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/filters"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/ethclient/simulated"
|
||||
)
|
||||
|
||||
// This nil assignment ensures at compile time that SimulatedBackend implements bind.ContractBackend.
|
||||
var _ bind.ContractBackend = (*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, PendingContractCaller, TransactionReader, and TransactionSender
|
||||
// SimulatedBackend is a simulated blockchain.
|
||||
// Deprecated: use package github.com/ethereum/go-ethereum/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
|
||||
pendingBlock *types.Block // Currently pending block that will be imported on request
|
||||
pendingState *state.StateDB // Currently pending state that will be the active on request
|
||||
pendingReceipts types.Receipts // Currently receipts for the pending block
|
||||
|
||||
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 {
|
||||
genesis := core.Genesis{
|
||||
Config: params.AllEthashProtocolChanges,
|
||||
GasLimit: gasLimit,
|
||||
Alloc: alloc,
|
||||
}
|
||||
blockchain, _ := core.NewBlockChain(database, nil, &genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
|
||||
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, false)
|
||||
|
||||
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.
|
||||
//
|
||||
// Deprecated: please use simulated.Backend from package
|
||||
// github.com/ethereum/go-ethereum/ethclient/simulated instead.
|
||||
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 pending transactions as a single block and starts a
|
||||
// fresh new state.
|
||||
func (b *SimulatedBackend) Commit() common.Hash {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil {
|
||||
panic(err) // This cannot happen unless the simulator is wrong, fail in that case
|
||||
}
|
||||
blockHash := b.pendingBlock.Hash()
|
||||
|
||||
// Using the last inserted block here makes it possible to build on a side
|
||||
// chain after a fork.
|
||||
b.rollback(b.pendingBlock)
|
||||
|
||||
return blockHash
|
||||
}
|
||||
|
||||
// Rollback aborts all pending 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, ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {})
|
||||
|
||||
b.pendingBlock = blocks[0]
|
||||
b.pendingState, _ = state.New(b.pendingBlock.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.pendingBlock.Transactions()) != 0 {
|
||||
return errors.New("pending 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, ethereum.NotFound
|
||||
}
|
||||
return receipt, nil
|
||||
}
|
||||
|
||||
// TransactionByHash checks the pool of pending transactions in addition to the
|
||||
// blockchain. The isPending 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 pending.
|
||||
func (b *SimulatedBackend) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
tx := b.pendingBlock.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, ethereum.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.pendingBlock.Hash() {
|
||||
return b.pendingBlock, 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.pendingBlock.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.pendingBlock.Hash() {
|
||||
return b.pendingBlock.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.pendingBlock.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.pendingBlock.Hash() {
|
||||
return uint(b.pendingBlock.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.pendingBlock.Hash() {
|
||||
transactions := b.pendingBlock.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
|
||||
}
|
||||
|
||||
// PendingCodeAt returns the code associated with an account in the pending state.
|
||||
func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
return b.pendingState.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()),
|
||||
b := simulated.New(alloc, gasLimit)
|
||||
return &SimulatedBackend{
|
||||
Backend: b,
|
||||
Client: b.Client(),
|
||||
}
|
||||
}
|
||||
|
||||
// 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 ethereum.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 ethereum.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 ethereum.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
|
||||
}
|
||||
|
||||
// PendingCallContract executes a contract call on the pending state.
|
||||
func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
|
||||
|
||||
res, err := b.callContract(ctx, call, b.pendingBlock.Header(), b.pendingState)
|
||||
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
|
||||
}
|
||||
|
||||
// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
|
||||
// the nonce currently pending for the account.
|
||||
func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
return b.pendingState.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.pendingBlock.Header().BaseFee != nil {
|
||||
return b.pendingBlock.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 pending block/state and
|
||||
// returns the used amount of gas.
|
||||
func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.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.pendingBlock.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.pendingState.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.Warn("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.pendingState.Snapshot()
|
||||
res, err := b.callContract(ctx, call, b.pendingBlock.Header(), b.pendingState)
|
||||
b.pendingState.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, vm.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 pending contract calls.
|
||||
// state is modified during execution, make sure to copy it if necessary.
|
||||
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.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().IsLondon(header.Number) {
|
||||
// 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 pending 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.pendingBlock.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.pendingState.GetNonce(sender)
|
||||
if tx.Nonce() != nonce {
|
||||
return fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)
|
||||
}
|
||||
// Include tx in chain
|
||||
blocks, receipts := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||
for _, tx := range b.pendingBlock.Transactions() {
|
||||
block.AddTxWithChain(b.blockchain, tx)
|
||||
}
|
||||
block.AddTxWithChain(b.blockchain, tx)
|
||||
})
|
||||
stateDB, err := b.blockchain.State()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.pendingBlock = blocks[0]
|
||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
|
||||
b.pendingReceipts = receipts[0]
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.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
|
||||
}
|
||||
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 ethereum.FilterQuery, ch chan<- types.Log) (ethereum.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) (ethereum.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.pendingBlock.Transactions()) != 0 {
|
||||
return errors.New("could not adjust time on non-empty block")
|
||||
}
|
||||
// Get the last block
|
||||
block := b.blockchain.GetBlockByHash(b.pendingBlock.ParentHash())
|
||||
if block == nil {
|
||||
return errors.New("could not find parent")
|
||||
}
|
||||
|
||||
blocks, _ := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||
block.OffsetTime(int64(adjustment.Seconds()))
|
||||
})
|
||||
stateDB, err := b.blockchain.State()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.pendingBlock = blocks[0]
|
||||
b.pendingState, _ = state.New(b.pendingBlock.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) 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:
|
||||
if block := fb.backend.pendingBlock; block != nil {
|
||||
return block.Header(), nil
|
||||
}
|
||||
return nil, nil
|
||||
case rpc.LatestBlockNumber:
|
||||
return fb.bc.CurrentHeader(), nil
|
||||
case rpc.FinalizedBlockNumber:
|
||||
return fb.bc.CurrentFinalBlock(), nil
|
||||
case rpc.SafeBlockNumber:
|
||||
return fb.bc.CurrentSafeBlock(), 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) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
|
||||
return fb.backend.pendingBlock, fb.backend.pendingReceipts
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -305,6 +305,7 @@ var bindTests = []struct {
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to deploy interactor contract: %v", err)
|
||||
}
|
||||
sim.Commit()
|
||||
if _, err := interactor.Transact(auth, "Transact string"); err != nil {
|
||||
t.Fatalf("Failed to transact with interactor contract: %v", err)
|
||||
}
|
||||
@ -512,6 +513,7 @@ var bindTests = []struct {
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to deploy defaulter contract: %v", err)
|
||||
}
|
||||
sim.Commit()
|
||||
if _, err := (&DefaulterRaw{defaulter}).Transfer(auth); err != nil {
|
||||
t.Fatalf("Failed to invoke default method: %v", err)
|
||||
}
|
||||
@ -1874,6 +1876,7 @@ 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/ethereum/go-ethereum/accounts/abi/bind"
|
||||
@ -1895,7 +1898,7 @@ var bindTests = []struct {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sim.Commit()
|
||||
_, err = bind.WaitDeployed(nil, sim, tx)
|
||||
_, err = bind.WaitDeployed(context.Background(), sim, tx)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@ -1926,6 +1929,7 @@ 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/ethereum/go-ethereum/accounts/abi/bind"
|
||||
@ -1948,7 +1952,7 @@ var bindTests = []struct {
|
||||
}
|
||||
sim.Commit()
|
||||
|
||||
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, <deployment tx>) got err %v; want nil err", sim, err)
|
||||
}
|
||||
@ -1974,6 +1978,7 @@ 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/ethereum/go-ethereum/accounts/abi/bind"
|
||||
@ -1996,7 +2001,7 @@ var bindTests = []struct {
|
||||
}
|
||||
sim.Commit()
|
||||
|
||||
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, <deployment tx>) got err %v; want nil err", sim, err)
|
||||
}
|
||||
@ -2014,6 +2019,7 @@ 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/ethereum/go-ethereum/accounts/abi/bind"
|
||||
@ -2034,7 +2040,7 @@ var bindTests = []struct {
|
||||
}
|
||||
sim.Commit()
|
||||
|
||||
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)
|
||||
}
|
||||
`,
|
||||
|
@ -24,11 +24,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethclient/simulated"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
@ -55,7 +56,7 @@ var waitDeployedTests = map[string]struct {
|
||||
func TestWaitDeployed(t *testing.T) {
|
||||
t.Parallel()
|
||||
for name, test := range waitDeployedTests {
|
||||
backend := backends.NewSimulatedBackend(
|
||||
backend := simulated.New(
|
||||
core.GenesisAlloc{
|
||||
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
|
||||
},
|
||||
@ -64,11 +65,11 @@ func TestWaitDeployed(t *testing.T) {
|
||||
defer backend.Close()
|
||||
|
||||
// Create the transaction
|
||||
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))
|
||||
|
||||
tx := types.NewContractCreation(0, big.NewInt(0), test.gas, gasPrice, common.FromHex(test.code))
|
||||
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||
tx, _ = types.SignTx(tx, types.LatestSignerForChainID(big.NewInt(1337)), testKey)
|
||||
|
||||
// Wait for it to get mined in the background.
|
||||
var (
|
||||
@ -78,12 +79,12 @@ 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.
|
||||
backend.SendTransaction(ctx, tx)
|
||||
backend.Client().SendTransaction(ctx, tx)
|
||||
backend.Commit()
|
||||
|
||||
select {
|
||||
@ -101,8 +102,7 @@ func TestWaitDeployed(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWaitDeployedCornerCases(t *testing.T) {
|
||||
t.Parallel()
|
||||
backend := backends.NewSimulatedBackend(
|
||||
backend := simulated.New(
|
||||
core.GenesisAlloc{
|
||||
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
|
||||
},
|
||||
@ -110,33 +110,33 @@ func TestWaitDeployedCornerCases(t *testing.T) {
|
||||
)
|
||||
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.AllDevChainProtocolChanges), testKey)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
backend.SendTransaction(ctx, tx)
|
||||
backend.Client().SendTransaction(ctx, tx)
|
||||
backend.Commit()
|
||||
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.AllDevChainProtocolChanges), 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()
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
"reflect"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
@ -41,8 +42,7 @@ func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
|
||||
case common.Address:
|
||||
copy(topic[common.HashLength-common.AddressLength:], rule[:])
|
||||
case *big.Int:
|
||||
blob := rule.Bytes()
|
||||
copy(topic[common.HashLength-len(blob):], blob)
|
||||
copy(topic[:], math.U256Bytes(rule))
|
||||
case bool:
|
||||
if rule {
|
||||
topic[common.HashLength-1] = 1
|
||||
|
@ -17,6 +17,7 @@
|
||||
package abi
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"testing"
|
||||
@ -55,9 +56,27 @@ func TestMakeTopics(t *testing.T) {
|
||||
false,
|
||||
},
|
||||
{
|
||||
"support *big.Int types in topics",
|
||||
args{[][]interface{}{{big.NewInt(1).Lsh(big.NewInt(2), 254)}}},
|
||||
[][]common.Hash{{common.Hash{128}}},
|
||||
"support positive *big.Int types in topics",
|
||||
args{[][]interface{}{
|
||||
{big.NewInt(1)},
|
||||
{big.NewInt(1).Lsh(big.NewInt(2), 254)},
|
||||
}},
|
||||
[][]common.Hash{
|
||||
{common.HexToHash("0000000000000000000000000000000000000000000000000000000000000001")},
|
||||
{common.Hash{128}},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
"support negative *big.Int types in topics",
|
||||
args{[][]interface{}{
|
||||
{big.NewInt(-1)},
|
||||
{big.NewInt(math.MinInt64)},
|
||||
}},
|
||||
[][]common.Hash{
|
||||
{common.MaxHash},
|
||||
{common.HexToHash("ffffffffffffffffffffffffffffffffffffffffffffffff8000000000000000")},
|
||||
},
|
||||
false,
|
||||
},
|
||||
{
|
||||
|
@ -136,7 +136,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 {
|
||||
|
13
build/ci.go
13
build/ci.go
@ -123,12 +123,13 @@ var (
|
||||
// wily, yakkety, zesty, artful, cosmic, disco, eoan, groovy, hirsuite, impish,
|
||||
// kinetic
|
||||
debDistroGoBoots = map[string]string{
|
||||
"trusty": "golang-1.11", // EOL: 04/2024
|
||||
"xenial": "golang-go", // EOL: 04/2026
|
||||
"bionic": "golang-go", // EOL: 04/2028
|
||||
"focal": "golang-go", // EOL: 04/2030
|
||||
"jammy": "golang-go", // EOL: 04/2032
|
||||
"lunar": "golang-go", // EOL: 01/2024
|
||||
"trusty": "golang-1.11", // 14.04, EOL: 04/2024
|
||||
"xenial": "golang-go", // 16.04, EOL: 04/2026
|
||||
"bionic": "golang-go", // 18.04, EOL: 04/2028
|
||||
"focal": "golang-go", // 20.04, EOL: 04/2030
|
||||
"jammy": "golang-go", // 22.04, EOL: 04/2032
|
||||
"lunar": "golang-go", // 23.04, EOL: 01/2024
|
||||
"mantic": "golang-go", // 23.10, EOL: 07/2024
|
||||
}
|
||||
|
||||
debGoBootPaths = map[string]string{
|
||||
|
@ -790,7 +790,7 @@ func (s *Suite) TestBlobViolations(t *utesting.T) {
|
||||
if err := s.engine.sendForkchoiceUpdated(); err != nil {
|
||||
t.Fatalf("send fcu failed: %v", err)
|
||||
}
|
||||
// Create blob txs for each tests with unqiue tx hashes.
|
||||
// Create blob txs for each tests with unique tx hashes.
|
||||
var (
|
||||
t1 = s.makeBlobTxs(2, 3, 0x1)
|
||||
t2 = s.makeBlobTxs(2, 3, 0x2)
|
||||
|
@ -128,7 +128,7 @@ func (s *Suite) sendInvalidTxs(txs []*types.Transaction) error {
|
||||
invalids[tx.Hash()] = struct{}{}
|
||||
}
|
||||
|
||||
// Get repsonses.
|
||||
// Get responses.
|
||||
recvConn.SetReadDeadline(time.Now().Add(timeout))
|
||||
for {
|
||||
msg, err := recvConn.ReadEth()
|
||||
|
@ -214,7 +214,7 @@ exitcode:3 OK
|
||||
|
||||
The chain configuration to be used for a transition is specified via the
|
||||
`--state.fork` CLI flag. A list of possible values and configurations can be
|
||||
found in [`tests/init.go`](tests/init.go).
|
||||
found in [`tests/init.go`](../../tests/init.go).
|
||||
|
||||
#### Examples
|
||||
##### Basic usage
|
||||
|
@ -140,6 +140,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||
rejectedTxs []*rejectedTx
|
||||
includedTxs types.Transactions
|
||||
gasUsed = uint64(0)
|
||||
blobGasUsed = uint64(0)
|
||||
receipts = make(types.Receipts, 0)
|
||||
txIndex = 0
|
||||
)
|
||||
@ -189,7 +190,6 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
|
||||
core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb)
|
||||
}
|
||||
var blobGasUsed uint64
|
||||
|
||||
for i := 0; txIt.Next(); i++ {
|
||||
tx, err := txIt.Tx()
|
||||
@ -210,15 +210,15 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
|
||||
continue
|
||||
}
|
||||
txBlobGas := uint64(0)
|
||||
if tx.Type() == types.BlobTxType {
|
||||
txBlobGas := uint64(params.BlobTxBlobGasPerBlob * len(tx.BlobHashes()))
|
||||
txBlobGas = uint64(params.BlobTxBlobGasPerBlob * len(tx.BlobHashes()))
|
||||
if used, max := blobGasUsed+txBlobGas, uint64(params.MaxBlobGasPerBlock); used > max {
|
||||
err := fmt.Errorf("blob gas (%d) would exceed maximum allowance %d", used, max)
|
||||
log.Warn("rejected tx", "index", i, "err", err)
|
||||
rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()})
|
||||
continue
|
||||
}
|
||||
blobGasUsed += txBlobGas
|
||||
}
|
||||
tracer, err := getTracerFn(txIndex, tx.Hash())
|
||||
if err != nil {
|
||||
@ -247,6 +247,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||
if hashError != nil {
|
||||
return nil, nil, nil, NewError(ErrorMissingBlockhash, hashError)
|
||||
}
|
||||
blobGasUsed += txBlobGas
|
||||
gasUsed += msgResult.UsedGas
|
||||
|
||||
// Receipt:
|
||||
|
@ -43,12 +43,22 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
removeStateDataFlag = &cli.BoolFlag{
|
||||
Name: "remove.state",
|
||||
Usage: "If set, selects the state data for removal",
|
||||
}
|
||||
removeChainDataFlag = &cli.BoolFlag{
|
||||
Name: "remove.chain",
|
||||
Usage: "If set, selects the state data for removal",
|
||||
}
|
||||
|
||||
removedbCommand = &cli.Command{
|
||||
Action: removeDB,
|
||||
Name: "removedb",
|
||||
Usage: "Remove blockchain and state databases",
|
||||
ArgsUsage: "",
|
||||
Flags: utils.DatabaseFlags,
|
||||
Flags: flags.Merge(utils.DatabaseFlags,
|
||||
[]cli.Flag{removeStateDataFlag, removeChainDataFlag}),
|
||||
Description: `
|
||||
Remove blockchain and state databases`,
|
||||
}
|
||||
@ -211,11 +221,11 @@ func removeDB(ctx *cli.Context) error {
|
||||
}
|
||||
// Delete state data
|
||||
statePaths := []string{rootDir, filepath.Join(ancientDir, rawdb.StateFreezerName)}
|
||||
confirmAndRemoveDB(statePaths, "state data")
|
||||
confirmAndRemoveDB(statePaths, "state data", ctx, removeStateDataFlag.Name)
|
||||
|
||||
// Delete ancient chain
|
||||
chainPaths := []string{filepath.Join(ancientDir, rawdb.ChainFreezerName)}
|
||||
confirmAndRemoveDB(chainPaths, "ancient chain")
|
||||
confirmAndRemoveDB(chainPaths, "ancient chain", ctx, removeChainDataFlag.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -238,14 +248,26 @@ func removeFolder(dir string) {
|
||||
|
||||
// confirmAndRemoveDB prompts the user for a last confirmation and removes the
|
||||
// list of folders if accepted.
|
||||
func confirmAndRemoveDB(paths []string, kind string) {
|
||||
func confirmAndRemoveDB(paths []string, kind string, ctx *cli.Context, removeFlagName string) {
|
||||
var (
|
||||
confirm bool
|
||||
err error
|
||||
)
|
||||
msg := fmt.Sprintf("Location(s) of '%s': \n", kind)
|
||||
for _, path := range paths {
|
||||
msg += fmt.Sprintf("\t- %s\n", path)
|
||||
}
|
||||
fmt.Println(msg)
|
||||
|
||||
confirm, err := prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove '%s'?", kind))
|
||||
if ctx.IsSet(removeFlagName) {
|
||||
confirm = ctx.Bool(removeFlagName)
|
||||
if confirm {
|
||||
fmt.Printf("Remove '%s'? [y/n] y\n", kind)
|
||||
} else {
|
||||
fmt.Printf("Remove '%s'? [y/n] n\n", kind)
|
||||
}
|
||||
} else {
|
||||
confirm, err = prompt.Stdin.PromptConfirm(fmt.Sprintf("Remove '%s'?", kind))
|
||||
}
|
||||
switch {
|
||||
case err != nil:
|
||||
utils.Fatalf("%v", err)
|
||||
|
@ -203,7 +203,6 @@ var app = flags.NewApp("the go-ethereum command line interface")
|
||||
func init() {
|
||||
// Initialize the CLI app and start Geth
|
||||
app.Action = geth
|
||||
app.Copyright = "Copyright 2013-2023 The go-ethereum Authors"
|
||||
app.Commands = []*cli.Command{
|
||||
// See chaincmd.go:
|
||||
initCommand,
|
||||
|
2
cmd/geth/testdata/logging/logtest-json.txt
vendored
2
cmd/geth/testdata/logging/logtest-json.txt
vendored
@ -29,7 +29,7 @@
|
||||
{"t":"2023-11-22T15:42:00.408237+08:00","lvl":"info","msg":"repeated-key 2","xx":"short","xx":"longer"}
|
||||
{"t":"2023-11-22T15:42:00.408241+08:00","lvl":"info","msg":"log at level info"}
|
||||
{"t":"2023-11-22T15:42:00.408244+08:00","lvl":"warn","msg":"log at level warn"}
|
||||
{"t":"2023-11-22T15:42:00.408247+08:00","lvl":"eror","msg":"log at level error"}
|
||||
{"t":"2023-11-22T15:42:00.408247+08:00","lvl":"error","msg":"log at level error"}
|
||||
{"t":"2023-11-22T15:42:00.408251+08:00","lvl":"info","msg":"test","bar":"short","a":"aligned left"}
|
||||
{"t":"2023-11-22T15:42:00.408254+08:00","lvl":"info","msg":"test","bar":"a long message","a":1}
|
||||
{"t":"2023-11-22T15:42:00.408258+08:00","lvl":"info","msg":"test","bar":"short","a":"aligned right"}
|
||||
|
2
cmd/geth/testdata/logging/logtest-logfmt.txt
vendored
2
cmd/geth/testdata/logging/logtest-logfmt.txt
vendored
@ -29,7 +29,7 @@ t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="repeated-key 1" foo=alpha foo=beta
|
||||
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="repeated-key 2" xx=short xx=longer
|
||||
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg="log at level info"
|
||||
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=warn msg="log at level warn"
|
||||
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=eror msg="log at level error"
|
||||
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=error msg="log at level error"
|
||||
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=test bar=short a="aligned left"
|
||||
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=test bar="a long message" a=1
|
||||
t=xxxx-xx-xxTxx:xx:xx+xxxx lvl=info msg=test bar=short a="aligned right"
|
||||
|
@ -91,8 +91,10 @@ func TestCreation(t *testing.T) {
|
||||
{5000000, 0, ID{Hash: checksumToBytes(0x757a1c47), Next: 5062605}}, // Last Berlin block
|
||||
{5062605, 0, ID{Hash: checksumToBytes(0xB8C6299D), Next: 1678832736}}, // First London block
|
||||
{6000000, 1678832735, ID{Hash: checksumToBytes(0xB8C6299D), Next: 1678832736}}, // Last London block
|
||||
{6000001, 1678832736, ID{Hash: checksumToBytes(0xf9843abf), Next: 0}}, // First Shanghai block
|
||||
{6500000, 2678832736, ID{Hash: checksumToBytes(0xf9843abf), Next: 0}}, // Future Shanghai block
|
||||
{6000001, 1678832736, ID{Hash: checksumToBytes(0xf9843abf), Next: 1705473120}}, // First Shanghai block
|
||||
{6500002, 1705473119, ID{Hash: checksumToBytes(0xf9843abf), Next: 1705473120}}, // Last Shanghai block
|
||||
{6500003, 1705473120, ID{Hash: checksumToBytes(0x70cc14e2), Next: 0}}, // First Cancun block
|
||||
{6500003, 2705473120, ID{Hash: checksumToBytes(0x70cc14e2), Next: 0}}, // Future Cancun block
|
||||
},
|
||||
},
|
||||
// Sepolia test cases
|
||||
|
@ -331,10 +331,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.Hash{}
|
||||
}
|
||||
|
||||
// GetState retrieves a value from the given account's storage trie.
|
||||
|
@ -197,7 +197,7 @@ var (
|
||||
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall)
|
||||
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode)
|
||||
gasSelfdestructEIP2929 = makeSelfdestructGasFn(true)
|
||||
// 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
|
||||
@ -214,12 +214,12 @@ var (
|
||||
// see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified
|
||||
gasSStoreEIP2929 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP2200)
|
||||
|
||||
// gasSStoreEIP2539 implements gas cost for SSTORE according to EIP-2539
|
||||
// gasSStoreEIP3529 implements gas cost for SSTORE according to EIP-3529
|
||||
// Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800)
|
||||
gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
|
||||
)
|
||||
|
||||
// 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 (
|
||||
|
@ -19,16 +19,17 @@ package catalyst
|
||||
import (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"math/big"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/beacon/engine"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
@ -81,6 +82,11 @@ type SimulatedBeacon struct {
|
||||
lastBlockTime uint64
|
||||
}
|
||||
|
||||
// NewSimulatedBeacon constructs a new simulated beacon chain.
|
||||
// Period sets the period in which blocks should be produced.
|
||||
//
|
||||
// - If period is set to 0, a block is produced on every transaction.
|
||||
// via Commit, Fork and AdjustTime.
|
||||
func NewSimulatedBeacon(period uint64, eth *eth.Ethereum) (*SimulatedBeacon, error) {
|
||||
block := eth.BlockChain().CurrentBlock()
|
||||
current := engine.ForkchoiceStateV1{
|
||||
@ -116,7 +122,9 @@ func (c *SimulatedBeacon) setFeeRecipient(feeRecipient common.Address) {
|
||||
// Start invokes the SimulatedBeacon life-cycle function in a goroutine.
|
||||
func (c *SimulatedBeacon) Start() error {
|
||||
if c.period == 0 {
|
||||
go c.loopOnDemand()
|
||||
// if period is set to 0, do not mine at all
|
||||
// this is used in the simulated backend where blocks
|
||||
// are explicitly mined via Commit, AdjustTime and Fork
|
||||
} else {
|
||||
go c.loop()
|
||||
}
|
||||
@ -131,10 +139,9 @@ func (c *SimulatedBeacon) Stop() error {
|
||||
|
||||
// sealBlock initiates payload building for a new block and creates a new block
|
||||
// with the completed payload.
|
||||
func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal) error {
|
||||
tstamp := uint64(time.Now().Unix())
|
||||
if tstamp <= c.lastBlockTime {
|
||||
tstamp = c.lastBlockTime + 1
|
||||
func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal, timestamp uint64) error {
|
||||
if timestamp <= c.lastBlockTime {
|
||||
timestamp = c.lastBlockTime + 1
|
||||
}
|
||||
c.feeRecipientLock.Lock()
|
||||
feeRecipient := c.feeRecipient
|
||||
@ -149,7 +156,7 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal) error {
|
||||
var random [32]byte
|
||||
rand.Read(random[:])
|
||||
fcResponse, err := c.engineAPI.ForkchoiceUpdatedV2(c.curForkchoiceState, &engine.PayloadAttributes{
|
||||
Timestamp: tstamp,
|
||||
Timestamp: timestamp,
|
||||
SuggestedFeeRecipient: feeRecipient,
|
||||
Withdrawals: withdrawals,
|
||||
Random: random,
|
||||
@ -183,6 +190,7 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal) error {
|
||||
return err
|
||||
}
|
||||
c.setCurrentState(payload.BlockHash, finalizedHash)
|
||||
|
||||
// Mark the block containing the payload as canonical
|
||||
if _, err = c.engineAPI.ForkchoiceUpdatedV2(c.curForkchoiceState, nil); err != nil {
|
||||
return err
|
||||
@ -191,32 +199,6 @@ func (c *SimulatedBeacon) sealBlock(withdrawals []*types.Withdrawal) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// loopOnDemand runs the block production loop for "on-demand" configuration (period = 0)
|
||||
func (c *SimulatedBeacon) loopOnDemand() {
|
||||
var (
|
||||
newTxs = make(chan core.NewTxsEvent)
|
||||
sub = c.eth.TxPool().SubscribeTransactions(newTxs, true)
|
||||
)
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-c.shutdownCh:
|
||||
return
|
||||
case w := <-c.withdrawals.pending:
|
||||
withdrawals := append(c.withdrawals.gatherPending(9), w)
|
||||
if err := c.sealBlock(withdrawals); err != nil {
|
||||
log.Warn("Error performing sealing work", "err", err)
|
||||
}
|
||||
case <-newTxs:
|
||||
withdrawals := c.withdrawals.gatherPending(10)
|
||||
if err := c.sealBlock(withdrawals); err != nil {
|
||||
log.Warn("Error performing sealing work", "err", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loop runs the block production loop for non-zero period configuration
|
||||
func (c *SimulatedBeacon) loop() {
|
||||
timer := time.NewTimer(0)
|
||||
@ -226,7 +208,7 @@ func (c *SimulatedBeacon) loop() {
|
||||
return
|
||||
case <-timer.C:
|
||||
withdrawals := c.withdrawals.gatherPending(10)
|
||||
if err := c.sealBlock(withdrawals); err != nil {
|
||||
if err := c.sealBlock(withdrawals, uint64(time.Now().Unix())); err != nil {
|
||||
log.Warn("Error performing sealing work", "err", err)
|
||||
} else {
|
||||
timer.Reset(time.Second * time.Duration(c.period))
|
||||
@ -235,8 +217,8 @@ func (c *SimulatedBeacon) loop() {
|
||||
}
|
||||
}
|
||||
|
||||
// finalizedBlockHash returns the block hash of the finalized block corresponding to the given number
|
||||
// or nil if doesn't exist in the chain.
|
||||
// finalizedBlockHash returns the block hash of the finalized block corresponding
|
||||
// to the given number or nil if doesn't exist in the chain.
|
||||
func (c *SimulatedBeacon) finalizedBlockHash(number uint64) *common.Hash {
|
||||
var finalizedNumber uint64
|
||||
if number%devEpochLength == 0 {
|
||||
@ -244,7 +226,6 @@ func (c *SimulatedBeacon) finalizedBlockHash(number uint64) *common.Hash {
|
||||
} else {
|
||||
finalizedNumber = (number - 1) / devEpochLength * devEpochLength
|
||||
}
|
||||
|
||||
if finalizedBlock := c.eth.BlockChain().GetBlockByNumber(finalizedNumber); finalizedBlock != nil {
|
||||
fh := finalizedBlock.Hash()
|
||||
return &fh
|
||||
@ -261,11 +242,60 @@ func (c *SimulatedBeacon) setCurrentState(headHash, finalizedHash common.Hash) {
|
||||
}
|
||||
}
|
||||
|
||||
// Commit seals a block on demand.
|
||||
func (c *SimulatedBeacon) Commit() common.Hash {
|
||||
withdrawals := c.withdrawals.gatherPending(10)
|
||||
if err := c.sealBlock(withdrawals, uint64(time.Now().Unix())); err != nil {
|
||||
log.Warn("Error performing sealing work", "err", err)
|
||||
}
|
||||
return c.eth.BlockChain().CurrentBlock().Hash()
|
||||
}
|
||||
|
||||
// Rollback un-sends previously added transactions.
|
||||
func (c *SimulatedBeacon) Rollback() {
|
||||
// Flush all transactions from the transaction pools
|
||||
maxUint256 := new(big.Int).Sub(new(big.Int).Lsh(common.Big1, 256), common.Big1)
|
||||
c.eth.TxPool().SetGasTip(maxUint256)
|
||||
// Set the gas tip back to accept new transactions
|
||||
// TODO (Marius van der Wijden): set gas tip to parameter passed by config
|
||||
c.eth.TxPool().SetGasTip(big.NewInt(params.GWei))
|
||||
}
|
||||
|
||||
// Fork sets the head to the provided hash.
|
||||
func (c *SimulatedBeacon) Fork(parentHash common.Hash) error {
|
||||
if len(c.eth.TxPool().Pending(false)) != 0 {
|
||||
return errors.New("pending block dirty")
|
||||
}
|
||||
parent := c.eth.BlockChain().GetBlockByHash(parentHash)
|
||||
if parent == nil {
|
||||
return errors.New("parent not found")
|
||||
}
|
||||
return c.eth.BlockChain().SetHead(parent.NumberU64())
|
||||
}
|
||||
|
||||
// AdjustTime creates a new block with an adjusted timestamp.
|
||||
func (c *SimulatedBeacon) AdjustTime(adjustment time.Duration) error {
|
||||
if len(c.eth.TxPool().Pending(false)) != 0 {
|
||||
return errors.New("could not adjust time on non-empty block")
|
||||
}
|
||||
parent := c.eth.BlockChain().CurrentBlock()
|
||||
if parent == nil {
|
||||
return errors.New("parent not found")
|
||||
}
|
||||
withdrawals := c.withdrawals.gatherPending(10)
|
||||
return c.sealBlock(withdrawals, parent.Time+uint64(adjustment))
|
||||
}
|
||||
|
||||
func RegisterSimulatedBeaconAPIs(stack *node.Node, sim *SimulatedBeacon) {
|
||||
api := &api{sim}
|
||||
if sim.period == 0 {
|
||||
// mine on demand if period is set to 0
|
||||
go api.loop()
|
||||
}
|
||||
stack.RegisterAPIs([]rpc.API{
|
||||
{
|
||||
Namespace: "dev",
|
||||
Service: &api{sim},
|
||||
Service: api,
|
||||
Version: "1.0",
|
||||
},
|
||||
})
|
||||
|
@ -18,19 +18,44 @@ package catalyst
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
type api struct {
|
||||
simBeacon *SimulatedBeacon
|
||||
sim *SimulatedBeacon
|
||||
}
|
||||
|
||||
func (a *api) loop() {
|
||||
var (
|
||||
newTxs = make(chan core.NewTxsEvent)
|
||||
sub = a.sim.eth.TxPool().SubscribeTransactions(newTxs, true)
|
||||
)
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-a.sim.shutdownCh:
|
||||
return
|
||||
case w := <-a.sim.withdrawals.pending:
|
||||
withdrawals := append(a.sim.withdrawals.gatherPending(9), w)
|
||||
if err := a.sim.sealBlock(withdrawals, uint64(time.Now().Unix())); err != nil {
|
||||
log.Warn("Error performing sealing work", "err", err)
|
||||
}
|
||||
case <-newTxs:
|
||||
a.sim.Commit()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *api) AddWithdrawal(ctx context.Context, withdrawal *types.Withdrawal) error {
|
||||
return a.simBeacon.withdrawals.add(withdrawal)
|
||||
return a.sim.withdrawals.add(withdrawal)
|
||||
}
|
||||
|
||||
func (a *api) SetFeeRecipient(ctx context.Context, feeRecipient common.Address) {
|
||||
a.simBeacon.setFeeRecipient(feeRecipient)
|
||||
a.sim.setFeeRecipient(feeRecipient)
|
||||
}
|
||||
|
@ -101,16 +101,15 @@ func (api *DownloaderAPI) Syncing(ctx context.Context) (*rpc.Subscription, error
|
||||
go func() {
|
||||
statuses := make(chan interface{})
|
||||
sub := api.SubscribeSyncStatus(statuses)
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
for {
|
||||
select {
|
||||
case status := <-statuses:
|
||||
notifier.Notify(rpcSub.ID, status)
|
||||
case <-rpcSub.Err():
|
||||
sub.Unsubscribe()
|
||||
return
|
||||
case <-notifier.Closed():
|
||||
sub.Unsubscribe()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -159,6 +159,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 {
|
||||
@ -176,10 +178,8 @@ func (api *FilterAPI) NewPendingTransactions(ctx context.Context, fullTx *bool)
|
||||
}
|
||||
}
|
||||
case <-rpcSub.Err():
|
||||
pendingTxSub.Unsubscribe()
|
||||
return
|
||||
case <-notifier.Closed():
|
||||
pendingTxSub.Unsubscribe()
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -233,16 +233,15 @@ func (api *FilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, error) {
|
||||
go func() {
|
||||
headers := make(chan *types.Header)
|
||||
headersSub := api.events.SubscribeNewHeads(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
|
||||
}
|
||||
}
|
||||
@ -269,6 +268,7 @@ func (api *FilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc.Subsc
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer logsSub.Unsubscribe()
|
||||
for {
|
||||
select {
|
||||
case logs := <-matchedLogs:
|
||||
@ -277,10 +277,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
|
||||
}
|
||||
}
|
||||
|
@ -307,11 +307,9 @@ 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 {
|
||||
if err == nil && r == nil {
|
||||
return nil, ethereum.NotFound
|
||||
}
|
||||
}
|
||||
return r, err
|
||||
}
|
||||
|
||||
|
190
ethclient/simulated/backend.go
Normal file
190
ethclient/simulated/backend.go
Normal file
@ -0,0 +1,190 @@
|
||||
// Copyright 2023 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package simulated
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/eth/catalyst"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/eth/filters"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
// 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
|
||||
beacon *catalyst.SimulatedBeacon
|
||||
client simClient
|
||||
}
|
||||
|
||||
// simClient wraps ethclient. This exists to prevent extracting ethclient.Client
|
||||
// from the Client interface returned by Backend.
|
||||
type simClient struct {
|
||||
*ethclient.Client
|
||||
}
|
||||
|
||||
// Client exposes the methods provided by the Ethereum RPC client.
|
||||
type Client interface {
|
||||
ethereum.BlockNumberReader
|
||||
ethereum.ChainReader
|
||||
ethereum.ChainStateReader
|
||||
ethereum.ContractCaller
|
||||
ethereum.GasEstimator
|
||||
ethereum.GasPricer
|
||||
ethereum.GasPricer1559
|
||||
ethereum.FeeHistoryReader
|
||||
ethereum.LogFilterer
|
||||
ethereum.PendingStateReader
|
||||
ethereum.PendingContractCaller
|
||||
ethereum.TransactionReader
|
||||
ethereum.TransactionSender
|
||||
ethereum.ChainIDReader
|
||||
}
|
||||
|
||||
// New creates a new binding backend using a simulated blockchain
|
||||
// for testing purposes.
|
||||
// A simulated backend always uses chainID 1337.
|
||||
func New(alloc core.GenesisAlloc, gasLimit uint64) *Backend {
|
||||
// Setup the node object
|
||||
nodeConf := node.DefaultConfig
|
||||
nodeConf.DataDir = ""
|
||||
nodeConf.P2P = p2p.Config{NoDiscovery: true}
|
||||
stack, err := node.New(&nodeConf)
|
||||
if err != nil {
|
||||
// This should never happen, if it does, please open an issue
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Setup ethereum
|
||||
genesis := core.Genesis{
|
||||
Config: params.AllDevChainProtocolChanges,
|
||||
GasLimit: gasLimit,
|
||||
Alloc: alloc,
|
||||
}
|
||||
conf := ethconfig.Defaults
|
||||
conf.Genesis = &genesis
|
||||
conf.SyncMode = downloader.FullSync
|
||||
conf.TxPool.NoLocals = true
|
||||
sim, err := newWithNode(stack, &conf, 0)
|
||||
if err != nil {
|
||||
// This should never happen, if it does, please open an issue
|
||||
panic(err)
|
||||
}
|
||||
return sim
|
||||
}
|
||||
|
||||
// newWithNode sets up a simulated backend on an existing node
|
||||
// this allows users to do persistent simulations.
|
||||
// The provided node must not be started and will be started by newWithNode
|
||||
func newWithNode(stack *node.Node, conf *eth.Config, blockPeriod uint64) (*Backend, error) {
|
||||
backend, err := eth.New(stack, conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Register the filter system
|
||||
filterSystem := filters.NewFilterSystem(backend.APIBackend, filters.Config{})
|
||||
stack.RegisterAPIs([]rpc.API{{
|
||||
Namespace: "eth",
|
||||
Service: filters.NewFilterAPI(filterSystem, false),
|
||||
}})
|
||||
|
||||
// Start the node
|
||||
if err := stack.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set up the simulated beacon
|
||||
beacon, err := catalyst.NewSimulatedBeacon(blockPeriod, backend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Reorg our chain back to genesis
|
||||
if err := beacon.Fork(backend.BlockChain().GetCanonicalHash(0)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Backend{
|
||||
eth: backend,
|
||||
beacon: beacon,
|
||||
client: simClient{ethclient.NewClient(stack.Attach())},
|
||||
}, 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.client = simClient{}
|
||||
}
|
||||
if n.beacon != nil {
|
||||
err := n.beacon.Stop()
|
||||
n.beacon = nil
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Commit seals a block and moves the chain forward to a new empty block.
|
||||
func (n *Backend) Commit() common.Hash {
|
||||
return n.beacon.Commit()
|
||||
}
|
||||
|
||||
// Rollback removes all pending transactions, reverting to the last committed state.
|
||||
func (n *Backend) Rollback() {
|
||||
n.beacon.Rollback()
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return n.beacon.Fork(parentHash)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return n.beacon.AdjustTime(adjustment)
|
||||
}
|
||||
|
||||
// Client returns a client that accesses the simulated chain.
|
||||
func (n *Backend) Client() Client {
|
||||
return n.client
|
||||
}
|
309
ethclient/simulated/backend_test.go
Normal file
309
ethclient/simulated/backend_test.go
Normal file
@ -0,0 +1,309 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
package simulated
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var _ bind.ContractBackend = (Client)(nil)
|
||||
|
||||
var (
|
||||
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
|
||||
)
|
||||
|
||||
func simTestBackend(testAddr common.Address) *Backend {
|
||||
return New(
|
||||
core.GenesisAlloc{
|
||||
testAddr: {Balance: big.NewInt(10000000000000000)},
|
||||
}, 10000000,
|
||||
)
|
||||
}
|
||||
|
||||
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(1))
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
chainid, _ := client.ChainID(context.Background())
|
||||
nonce, err := client.PendingNonceAt(context.Background(), addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tx := types.NewTx(&types.DynamicFeeTx{
|
||||
ChainID: chainid,
|
||||
Nonce: nonce,
|
||||
GasTipCap: big.NewInt(1),
|
||||
GasFeeCap: gasPrice,
|
||||
Gas: 21000,
|
||||
To: &addr,
|
||||
})
|
||||
return types.SignTx(tx, types.LatestSignerForChainID(chainid), key)
|
||||
}
|
||||
|
||||
func TestNewSim(t *testing.T) {
|
||||
sim := New(core.GenesisAlloc{}, 30_000_000)
|
||||
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()
|
||||
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 := New(core.GenesisAlloc{}, 10_000_000)
|
||||
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()
|
||||
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()
|
||||
}
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
||||
// 3.
|
||||
receipt, _ := client.TransactionReceipt(ctx, tx.Hash())
|
||||
if h := receipt.BlockNumber.Uint64(); h != 1 {
|
||||
t.Errorf("TX included in wrong block: %d", h)
|
||||
}
|
||||
|
||||
// 4.
|
||||
if err := sim.Fork(parent.Hash()); err != nil {
|
||||
t.Errorf("forking: %v", err)
|
||||
}
|
||||
|
||||
// 5.
|
||||
sim.Commit()
|
||||
if err := client.SendTransaction(ctx, tx); err != nil {
|
||||
t.Fatalf("sending transaction: %v", err)
|
||||
}
|
||||
sim.Commit()
|
||||
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()
|
||||
|
||||
// Test if Commit returns the correct block hash
|
||||
h1 := sim.Commit()
|
||||
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.HomesteadSigner{}, testKey)
|
||||
client.SendTransaction(ctx, tx)
|
||||
|
||||
h2 := sim.Commit()
|
||||
|
||||
// Create another block in the original chain
|
||||
sim.Commit()
|
||||
|
||||
// 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()
|
||||
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() // h1
|
||||
h1, _ := client.HeaderByNumber(ctx, nil)
|
||||
|
||||
sim.Commit() // h2
|
||||
sim.Fork(h1.Hash())
|
||||
sim.AdjustTime(1 * time.Second)
|
||||
sim.Commit()
|
||||
|
||||
head, _ := client.HeaderByNumber(ctx, nil)
|
||||
if head.Number.Uint64() == 2 && head.ParentHash != h1.Hash() {
|
||||
t.Errorf("failed to build block on fork")
|
||||
}
|
||||
}
|
@ -199,6 +199,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 {
|
||||
@ -239,3 +249,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)
|
||||
}
|
||||
|
@ -41,7 +41,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
|
||||
|
@ -83,7 +83,7 @@ func LevelAlignedString(l slog.Level) string {
|
||||
}
|
||||
}
|
||||
|
||||
// LevelString returns a 5-character string containing the name of a Lvl.
|
||||
// LevelString returns a string containing the name of a Lvl.
|
||||
func LevelString(l slog.Level) string {
|
||||
switch l {
|
||||
case LevelTrace:
|
||||
@ -95,7 +95,7 @@ func LevelString(l slog.Level) string {
|
||||
case slog.LevelWarn:
|
||||
return "warn"
|
||||
case slog.LevelError:
|
||||
return "eror"
|
||||
return "error"
|
||||
case LevelCrit:
|
||||
return "crit"
|
||||
default:
|
||||
|
@ -10,8 +10,7 @@ import (
|
||||
var root atomic.Value
|
||||
|
||||
func init() {
|
||||
defaultLogger := &logger{slog.New(DiscardHandler())}
|
||||
SetDefault(defaultLogger)
|
||||
root.Store(&logger{slog.New(DiscardHandler())})
|
||||
}
|
||||
|
||||
// SetDefault sets the default global logger
|
||||
|
@ -127,6 +127,7 @@ var (
|
||||
TerminalTotalDifficulty: big.NewInt(10_790_000),
|
||||
TerminalTotalDifficultyPassed: true,
|
||||
ShanghaiTime: newUint64(1678832736),
|
||||
CancunTime: newUint64(1705473120),
|
||||
Clique: &CliqueConfig{
|
||||
Period: 15,
|
||||
Epoch: 30000,
|
||||
|
@ -23,7 +23,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 = 9 // Patch version component of the current release
|
||||
VersionMeta = "stable" // Version metadata to append to the version string
|
||||
)
|
||||
|
||||
|
@ -65,7 +65,7 @@ type ExternalAPI interface {
|
||||
EcRecover(ctx context.Context, data hexutil.Bytes, sig hexutil.Bytes) (common.Address, error)
|
||||
// Version info about the APIs
|
||||
Version(ctx context.Context) (string, error)
|
||||
// SignGnosisSafeTransaction signs/confirms a gnosis-safe multisig transaction
|
||||
// SignGnosisSafeTx signs/confirms a gnosis-safe multisig transaction
|
||||
SignGnosisSafeTx(ctx context.Context, signerAddress common.MixedcaseAddress, gnosisTx GnosisSafeTx, methodSelector *string) (*GnosisSafeTx, error)
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,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 {
|
||||
|
@ -31,7 +31,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
// SignerUIAPI implements methods Clef provides for a UI to query, in the bidirectional communication
|
||||
// UIServerAPI implements methods Clef provides for a UI to query, in the bidirectional communication
|
||||
// channel.
|
||||
// This API is considered secure, since a request can only
|
||||
// ever arrive from the UI -- and the UI is capable of approving any action, thus we can consider these
|
||||
|
@ -23,6 +23,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
|
||||
Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
||||
Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"`
|
||||
ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas" gencodec:"optional"`
|
||||
}
|
||||
var enc stEnv
|
||||
enc.Coinbase = common.UnprefixedAddress(s.Coinbase)
|
||||
@ -32,6 +33,7 @@ func (s stEnv) MarshalJSON() ([]byte, error) {
|
||||
enc.Number = math.HexOrDecimal64(s.Number)
|
||||
enc.Timestamp = math.HexOrDecimal64(s.Timestamp)
|
||||
enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee)
|
||||
enc.ExcessBlobGas = (*math.HexOrDecimal64)(s.ExcessBlobGas)
|
||||
return json.Marshal(&enc)
|
||||
}
|
||||
|
||||
@ -45,6 +47,7 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
|
||||
Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
|
||||
Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"currentBaseFee" gencodec:"optional"`
|
||||
ExcessBlobGas *math.HexOrDecimal64 `json:"currentExcessBlobGas" gencodec:"optional"`
|
||||
}
|
||||
var dec stEnv
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
@ -75,5 +78,8 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
|
||||
if dec.BaseFee != nil {
|
||||
s.BaseFee = (*big.Int)(dec.BaseFee)
|
||||
}
|
||||
if dec.ExcessBlobGas != nil {
|
||||
s.ExcessBlobGas = (*uint64)(dec.ExcessBlobGas)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ 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/consensus/misc/eip4844"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
@ -90,6 +91,7 @@ type stEnv struct {
|
||||
Number uint64 `json:"currentNumber" gencodec:"required"`
|
||||
Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
|
||||
BaseFee *big.Int `json:"currentBaseFee" gencodec:"optional"`
|
||||
ExcessBlobGas *uint64 `json:"currentExcessBlobGas" gencodec:"optional"`
|
||||
}
|
||||
|
||||
type stEnvMarshaling struct {
|
||||
@ -100,6 +102,7 @@ type stEnvMarshaling struct {
|
||||
Number math.HexOrDecimal64
|
||||
Timestamp math.HexOrDecimal64
|
||||
BaseFee *math.HexOrDecimal256
|
||||
ExcessBlobGas *math.HexOrDecimal64
|
||||
}
|
||||
|
||||
//go:generate go run github.com/fjl/gencodec -type stTransaction -field-override stTransactionMarshaling -out gen_sttransaction.go
|
||||
@ -283,6 +286,9 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh
|
||||
context.Random = &rnd
|
||||
context.Difficulty = big.NewInt(0)
|
||||
}
|
||||
if config.IsCancun(new(big.Int), block.Time()) && t.json.Env.ExcessBlobGas != nil {
|
||||
context.BlobBaseFee = eip4844.CalcBlobFee(*t.json.Env.ExcessBlobGas)
|
||||
}
|
||||
evm := vm.NewEVM(context, txContext, statedb, config, vmconfig)
|
||||
|
||||
// Execute the message.
|
||||
|
Loading…
Reference in New Issue
Block a user