2016-11-08 19:01:56 -06:00
|
|
|
// Copyright 2015 The go-ethereum Authors
|
2016-03-17 12:27:37 -05:00
|
|
|
// 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 backends
|
|
|
|
|
|
|
|
import (
|
2017-03-22 12:20:33 -05:00
|
|
|
"context"
|
2016-08-22 16:20:13 -05:00
|
|
|
"errors"
|
2016-08-22 07:01:28 -05:00
|
|
|
"fmt"
|
2016-03-17 12:27:37 -05:00
|
|
|
"math/big"
|
2016-08-22 16:20:13 -05:00
|
|
|
"sync"
|
2017-09-15 08:20:29 -05:00
|
|
|
"time"
|
2016-03-17 12:27:37 -05:00
|
|
|
|
2016-08-22 07:01:28 -05:00
|
|
|
"github.com/ethereum/go-ethereum"
|
2020-04-22 03:25:36 -05:00
|
|
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
2016-03-21 07:34:49 -05:00
|
|
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
2016-03-17 12:27:37 -05:00
|
|
|
"github.com/ethereum/go-ethereum/common"
|
2020-06-08 03:09:49 -05:00
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
common: move big integer math to common/math (#3699)
* common: remove CurrencyToString
Move denomination values to params instead.
* common: delete dead code
* common: move big integer operations to common/math
This commit consolidates all big integer operations into common/math and
adds tests and documentation.
There should be no change in semantics for BigPow, BigMin, BigMax, S256,
U256, Exp and their behaviour is now locked in by tests.
The BigD, BytesToBig and Bytes2Big functions don't provide additional
value, all uses are replaced by new(big.Int).SetBytes().
BigToBytes is now called PaddedBigBytes, its minimum output size
parameter is now specified as the number of bytes instead of bits. The
single use of this function is in the EVM's MSTORE instruction.
Big and String2Big are replaced by ParseBig, which is slightly stricter.
It previously accepted leading zeros for hexadecimal inputs but treated
decimal inputs as octal if a leading zero digit was present.
ParseUint64 is used in places where String2Big was used to decode a
uint64.
The new functions MustParseBig and MustParseUint64 are now used in many
places where parsing errors were previously ignored.
* common: delete unused big integer variables
* accounts/abi: replace uses of BytesToBig with use of encoding/binary
* common: remove BytesToBig
* common: remove Bytes2Big
* common: remove BigTrue
* cmd/utils: add BigFlag and use it for error-checked integer flags
While here, remove environment variable processing for DirectoryFlag
because we don't use it.
* core: add missing error checks in genesis block parser
* common: remove String2Big
* cmd/evm: use utils.BigFlag
* common/math: check for 256 bit overflow in ParseBig
This is supposed to prevent silent overflow/truncation of values in the
genesis block JSON. Without this check, a genesis block that set a
balance larger than 256 bits would lead to weird behaviour in the VM.
* cmd/utils: fixup import
2017-02-26 15:21:51 -06:00
|
|
|
"github.com/ethereum/go-ethereum/common/math"
|
2017-04-04 17:16:29 -05:00
|
|
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
2016-03-17 12:27:37 -05:00
|
|
|
"github.com/ethereum/go-ethereum/core"
|
2018-01-05 04:39:24 -06:00
|
|
|
"github.com/ethereum/go-ethereum/core/bloombits"
|
2018-05-07 06:35:06 -05:00
|
|
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
2016-03-17 12:27:37 -05:00
|
|
|
"github.com/ethereum/go-ethereum/core/state"
|
|
|
|
"github.com/ethereum/go-ethereum/core/types"
|
2016-03-01 16:32:43 -06:00
|
|
|
"github.com/ethereum/go-ethereum/core/vm"
|
2018-01-05 04:39:24 -06:00
|
|
|
"github.com/ethereum/go-ethereum/eth/filters"
|
2016-03-17 12:27:37 -05:00
|
|
|
"github.com/ethereum/go-ethereum/ethdb"
|
2018-01-05 04:39:24 -06:00
|
|
|
"github.com/ethereum/go-ethereum/event"
|
2020-05-11 03:08:20 -05:00
|
|
|
"github.com/ethereum/go-ethereum/log"
|
2016-10-20 06:36:29 -05:00
|
|
|
"github.com/ethereum/go-ethereum/params"
|
2018-01-05 04:39:24 -06:00
|
|
|
"github.com/ethereum/go-ethereum/rpc"
|
2016-03-17 12:27:37 -05:00
|
|
|
)
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
// This nil assignment ensures at compile time that SimulatedBackend implements bind.ContractBackend.
|
2016-03-21 07:34:49 -05:00
|
|
|
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
|
|
|
|
|
all: on-chain oracle checkpoint syncing (#19543)
* all: implement simple checkpoint syncing
cmd, les, node: remove callback mechanism
cmd, node: remove callback definition
les: simplify the registrar
les: expose checkpoint rpc services in the light client
les, light: don't store untrusted receipt
cmd, contracts, les: discard stale checkpoint
cmd, contracts/registrar: loose restriction of registeration
cmd, contracts: add replay-protection
all: off-chain multi-signature contract
params: deploy checkpoint contract for rinkeby
cmd/registrar: add raw signing mode for registrar
cmd/registrar, contracts/registrar, les: fixed messages
* cmd/registrar, contracts/registrar: fix lints
* accounts/abi/bind, les: address comments
* cmd, contracts, les, light, params: minor checkpoint sync cleanups
* cmd, eth, les, light: move checkpoint config to config file
* cmd, eth, les, params: address comments
* eth, les, params: address comments
* cmd: polish up the checkpoint admin CLI
* cmd, contracts, params: deploy new version contract
* cmd/checkpoint-admin: add another flag for clef mode signing
* cmd, contracts, les: rename and regen checkpoint oracle with abigen
2019-06-28 02:34:02 -05:00
|
|
|
var (
|
2019-12-20 04:33:32 -06:00
|
|
|
errBlockNumberUnsupported = errors.New("simulatedBackend cannot access blocks other than the latest block")
|
|
|
|
errBlockDoesNotExist = errors.New("block does not exist in blockchain")
|
|
|
|
errTransactionDoesNotExist = errors.New("transaction does not exist")
|
all: on-chain oracle checkpoint syncing (#19543)
* all: implement simple checkpoint syncing
cmd, les, node: remove callback mechanism
cmd, node: remove callback definition
les: simplify the registrar
les: expose checkpoint rpc services in the light client
les, light: don't store untrusted receipt
cmd, contracts, les: discard stale checkpoint
cmd, contracts/registrar: loose restriction of registeration
cmd, contracts: add replay-protection
all: off-chain multi-signature contract
params: deploy checkpoint contract for rinkeby
cmd/registrar: add raw signing mode for registrar
cmd/registrar, contracts/registrar, les: fixed messages
* cmd/registrar, contracts/registrar: fix lints
* accounts/abi/bind, les: address comments
* cmd, contracts, les, light, params: minor checkpoint sync cleanups
* cmd, eth, les, light: move checkpoint config to config file
* cmd, eth, les, params: address comments
* eth, les, params: address comments
* cmd: polish up the checkpoint admin CLI
* cmd, contracts, params: deploy new version contract
* cmd/checkpoint-admin: add another flag for clef mode signing
* cmd, contracts, les: rename and regen checkpoint oracle with abigen
2019-06-28 02:34:02 -05:00
|
|
|
)
|
2016-08-22 16:20:13 -05:00
|
|
|
|
2016-03-17 12:27:37 -05:00
|
|
|
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
|
2020-09-07 06:07:15 -05:00
|
|
|
// the background. Its main purpose is to allow for easy testing of contract bindings.
|
2019-12-20 04:33:32 -06:00
|
|
|
// Simulated backend implements the following interfaces:
|
|
|
|
// ChainReader, ChainStateReader, ContractBackend, ContractCaller, ContractFilterer, ContractTransactor,
|
|
|
|
// DeployBackend, GasEstimator, GasPricer, LogFilterer, PendingContractCaller, TransactionReader, and TransactionSender
|
2016-03-17 12:27:37 -05:00
|
|
|
type SimulatedBackend struct {
|
|
|
|
database ethdb.Database // In memory database to store our testing data
|
|
|
|
blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
|
|
|
|
|
2016-08-22 16:20:13 -05:00
|
|
|
mu sync.Mutex
|
2016-03-17 12:27:37 -05:00
|
|
|
pendingBlock *types.Block // Currently pending block that will be imported on request
|
2020-05-25 03:21:28 -05:00
|
|
|
pendingState *state.StateDB // Currently pending state that will be the active on request
|
2016-10-20 06:36:29 -05:00
|
|
|
|
2018-01-05 04:39:24 -06:00
|
|
|
events *filters.EventSystem // Event system for filtering log events live
|
|
|
|
|
2016-10-20 06:36:29 -05:00
|
|
|
config *params.ChainConfig
|
2016-03-17 12:27:37 -05:00
|
|
|
}
|
|
|
|
|
all: on-chain oracle checkpoint syncing (#19543)
* all: implement simple checkpoint syncing
cmd, les, node: remove callback mechanism
cmd, node: remove callback definition
les: simplify the registrar
les: expose checkpoint rpc services in the light client
les, light: don't store untrusted receipt
cmd, contracts, les: discard stale checkpoint
cmd, contracts/registrar: loose restriction of registeration
cmd, contracts: add replay-protection
all: off-chain multi-signature contract
params: deploy checkpoint contract for rinkeby
cmd/registrar: add raw signing mode for registrar
cmd/registrar, contracts/registrar, les: fixed messages
* cmd/registrar, contracts/registrar: fix lints
* accounts/abi/bind, les: address comments
* cmd, contracts, les, light, params: minor checkpoint sync cleanups
* cmd, eth, les, light: move checkpoint config to config file
* cmd, eth, les, params: address comments
* eth, les, params: address comments
* cmd: polish up the checkpoint admin CLI
* cmd, contracts, params: deploy new version contract
* cmd/checkpoint-admin: add another flag for clef mode signing
* cmd, contracts, les: rename and regen checkpoint oracle with abigen
2019-06-28 02:34:02 -05:00
|
|
|
// NewSimulatedBackendWithDatabase creates a new binding backend based on the given database
|
|
|
|
// and uses a simulated blockchain for testing purposes.
|
|
|
|
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
2018-08-15 02:15:42 -05:00
|
|
|
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
|
2017-03-02 07:03:33 -06:00
|
|
|
genesis.MustCommit(database)
|
2020-05-11 10:58:43 -05:00
|
|
|
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
|
2018-01-05 04:39:24 -06:00
|
|
|
|
|
|
|
backend := &SimulatedBackend{
|
|
|
|
database: database,
|
|
|
|
blockchain: blockchain,
|
|
|
|
config: genesis.Config,
|
2019-12-10 05:39:14 -06:00
|
|
|
events: filters.NewEventSystem(&filterBackend{database, blockchain}, false),
|
2018-01-05 04:39:24 -06:00
|
|
|
}
|
2016-08-22 16:20:13 -05:00
|
|
|
backend.rollback()
|
2016-03-17 12:27:37 -05:00
|
|
|
return backend
|
|
|
|
}
|
|
|
|
|
all: on-chain oracle checkpoint syncing (#19543)
* all: implement simple checkpoint syncing
cmd, les, node: remove callback mechanism
cmd, node: remove callback definition
les: simplify the registrar
les: expose checkpoint rpc services in the light client
les, light: don't store untrusted receipt
cmd, contracts, les: discard stale checkpoint
cmd, contracts/registrar: loose restriction of registeration
cmd, contracts: add replay-protection
all: off-chain multi-signature contract
params: deploy checkpoint contract for rinkeby
cmd/registrar: add raw signing mode for registrar
cmd/registrar, contracts/registrar, les: fixed messages
* cmd/registrar, contracts/registrar: fix lints
* accounts/abi/bind, les: address comments
* cmd, contracts, les, light, params: minor checkpoint sync cleanups
* cmd, eth, les, light: move checkpoint config to config file
* cmd, eth, les, params: address comments
* eth, les, params: address comments
* cmd: polish up the checkpoint admin CLI
* cmd, contracts, params: deploy new version contract
* cmd/checkpoint-admin: add another flag for clef mode signing
* cmd, contracts, les: rename and regen checkpoint oracle with abigen
2019-06-28 02:34:02 -05:00
|
|
|
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
|
|
|
|
// for testing purposes.
|
|
|
|
func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
|
|
|
return NewSimulatedBackendWithDatabase(rawdb.NewMemoryDatabase(), alloc, gasLimit)
|
|
|
|
}
|
|
|
|
|
2019-07-31 03:35:57 -05:00
|
|
|
// Close terminates the underlying blockchain's update loop.
|
|
|
|
func (b *SimulatedBackend) Close() error {
|
|
|
|
b.blockchain.Stop()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-03-17 12:27:37 -05:00
|
|
|
// Commit imports all the pending transactions as a single block and starts a
|
|
|
|
// fresh new state.
|
|
|
|
func (b *SimulatedBackend) Commit() {
|
2016-08-22 16:20:13 -05:00
|
|
|
b.mu.Lock()
|
|
|
|
defer b.mu.Unlock()
|
|
|
|
|
2016-03-17 12:27:37 -05:00
|
|
|
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
|
|
|
|
}
|
2016-08-22 16:20:13 -05:00
|
|
|
b.rollback()
|
2016-03-17 12:27:37 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Rollback aborts all pending transactions, reverting to the last committed state.
|
|
|
|
func (b *SimulatedBackend) Rollback() {
|
2016-08-22 16:20:13 -05:00
|
|
|
b.mu.Lock()
|
|
|
|
defer b.mu.Unlock()
|
|
|
|
|
|
|
|
b.rollback()
|
|
|
|
}
|
2016-03-17 12:27:37 -05:00
|
|
|
|
2016-08-22 16:20:13 -05:00
|
|
|
func (b *SimulatedBackend) rollback() {
|
2017-12-22 06:37:50 -06:00
|
|
|
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {})
|
2020-09-07 06:07:15 -05:00
|
|
|
stateDB, _ := b.blockchain.State()
|
2018-02-05 10:40:32 -06:00
|
|
|
|
2016-03-17 12:27:37 -05:00
|
|
|
b.pendingBlock = blocks[0]
|
2020-09-07 06:07:15 -05:00
|
|
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
|
2016-03-17 12:27:37 -05:00
|
|
|
}
|
|
|
|
|
2020-02-12 04:33:17 -06:00
|
|
|
// 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()
|
|
|
|
}
|
2020-05-19 05:48:27 -05:00
|
|
|
block, err := b.blockByNumberNoLock(ctx, blockNumber)
|
2020-02-12 04:33:17 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-05-19 05:48:27 -05:00
|
|
|
return b.blockchain.StateAt(block.Root())
|
2020-02-12 04:33:17 -06:00
|
|
|
}
|
|
|
|
|
2016-08-22 16:20:13 -05:00
|
|
|
// CodeAt returns the code associated with a certain account in the blockchain.
|
2016-08-22 07:01:28 -05:00
|
|
|
func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
|
2016-08-22 16:20:13 -05:00
|
|
|
b.mu.Lock()
|
|
|
|
defer b.mu.Unlock()
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
|
2020-02-12 04:33:17 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2016-05-20 04:29:28 -05:00
|
|
|
}
|
2020-02-12 04:33:17 -06:00
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
return stateDB.GetCode(contract), nil
|
2016-05-20 04:29:28 -05:00
|
|
|
}
|
|
|
|
|
2016-08-22 16:20:13 -05:00
|
|
|
// 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()
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
|
2020-02-12 04:33:17 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2016-08-22 16:20:13 -05:00
|
|
|
}
|
2020-02-12 04:33:17 -06:00
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
return stateDB.GetBalance(contract), nil
|
2016-08-22 16:20:13 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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()
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
|
2020-02-12 04:33:17 -06:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
2016-08-22 16:20:13 -05:00
|
|
|
}
|
2020-02-12 04:33:17 -06:00
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
return stateDB.GetNonce(contract), nil
|
2016-08-22 16:20:13 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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()
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
|
2020-02-12 04:33:17 -06:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2016-08-22 16:20:13 -05:00
|
|
|
}
|
2020-02-12 04:33:17 -06:00
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
val := stateDB.GetState(contract, key)
|
2016-09-22 14:04:58 -05:00
|
|
|
return val[:], nil
|
2016-08-22 16:20:13 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// TransactionReceipt returns the receipt of a transaction.
|
2020-09-07 06:07:15 -05:00
|
|
|
func (b *SimulatedBackend) TransactionReceipt(_ context.Context, txHash common.Hash) (*types.Receipt, error) {
|
2019-12-20 04:33:32 -06:00
|
|
|
b.mu.Lock()
|
|
|
|
defer b.mu.Unlock()
|
|
|
|
|
2019-04-15 04:36:27 -05:00
|
|
|
receipt, _, _, _ := rawdb.ReadReceipt(b.database, txHash, b.config)
|
2017-07-14 11:39:53 -05:00
|
|
|
return receipt, nil
|
2016-08-22 16:20:13 -05:00
|
|
|
}
|
|
|
|
|
2019-02-12 17:23:10 -06:00
|
|
|
// 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.
|
2020-09-07 06:07:15 -05:00
|
|
|
func (b *SimulatedBackend) TransactionByHash(_ context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
|
2019-02-20 01:08:54 -06:00
|
|
|
b.mu.Lock()
|
|
|
|
defer b.mu.Unlock()
|
2019-02-12 17:23:10 -06:00
|
|
|
|
2019-02-20 01:08:54 -06:00
|
|
|
tx := b.pendingBlock.Transaction(txHash)
|
2019-02-12 17:23:10 -06:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
// BlockByHash retrieves a block based on the block hash.
|
|
|
|
func (b *SimulatedBackend) BlockByHash(_ context.Context, hash common.Hash) (*types.Block, error) {
|
2019-12-20 04:33:32 -06:00
|
|
|
b.mu.Lock()
|
|
|
|
defer b.mu.Unlock()
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
2020-05-19 05:48:27 -05:00
|
|
|
return b.blockByNumberNoLock(ctx, number)
|
|
|
|
}
|
|
|
|
|
|
|
|
// blockByNumberNoLock retrieves a block from the database by number, caching it
|
|
|
|
// (associated with its hash) if found without Lock.
|
2020-09-07 06:07:15 -05:00
|
|
|
func (b *SimulatedBackend) blockByNumberNoLock(_ context.Context, number *big.Int) (*types.Block, error) {
|
2019-12-20 04:33:32 -06:00
|
|
|
if number == nil || number.Cmp(b.pendingBlock.Number()) == 0 {
|
|
|
|
return b.blockchain.CurrentBlock(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
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.
|
2020-09-07 06:07:15 -05:00
|
|
|
func (b *SimulatedBackend) HeaderByHash(_ context.Context, hash common.Hash) (*types.Header, error) {
|
2019-12-20 04:33:32 -06:00
|
|
|
b.mu.Lock()
|
|
|
|
defer b.mu.Unlock()
|
|
|
|
|
|
|
|
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.
|
2020-09-07 06:07:15 -05:00
|
|
|
func (b *SimulatedBackend) HeaderByNumber(_ context.Context, block *big.Int) (*types.Header, error) {
|
2019-12-20 04:33:32 -06:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
// TransactionCount returns the number of transactions in a given block.
|
|
|
|
func (b *SimulatedBackend) TransactionCount(_ context.Context, blockHash common.Hash) (uint, error) {
|
2019-12-20 04:33:32 -06:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
// TransactionInBlock returns the transaction for a specific block at a specific index.
|
|
|
|
func (b *SimulatedBackend) TransactionInBlock(_ context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) {
|
2019-12-20 04:33:32 -06:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2016-08-22 16:20:13 -05:00
|
|
|
// PendingCodeAt returns the code associated with an account in the pending state.
|
2020-09-07 06:07:15 -05:00
|
|
|
func (b *SimulatedBackend) PendingCodeAt(_ context.Context, contract common.Address) ([]byte, error) {
|
2016-08-22 16:20:13 -05:00
|
|
|
b.mu.Lock()
|
|
|
|
defer b.mu.Unlock()
|
|
|
|
|
2016-08-22 07:01:28 -05:00
|
|
|
return b.pendingState.GetCode(contract), nil
|
|
|
|
}
|
2016-03-17 12:27:37 -05:00
|
|
|
|
2020-06-08 03:09:49 -05:00
|
|
|
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()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
// revertError is an API error that encompasses an EVM revert with JSON error
|
2020-06-08 03:09:49 -05:00
|
|
|
// code and a binary data blob.
|
|
|
|
type revertError struct {
|
|
|
|
error
|
|
|
|
reason string // revert reason hex encoded
|
|
|
|
}
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
// ErrorCode returns the JSON error code for a revert.
|
2020-06-08 03:09:49 -05:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2016-08-22 07:01:28 -05:00
|
|
|
// CallContract executes a contract call.
|
|
|
|
func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
2016-08-22 16:20:13 -05:00
|
|
|
b.mu.Lock()
|
|
|
|
defer b.mu.Unlock()
|
|
|
|
|
2016-08-22 07:01:28 -05:00
|
|
|
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
2016-08-22 16:20:13 -05:00
|
|
|
return nil, errBlockNumberUnsupported
|
2016-03-17 12:27:37 -05:00
|
|
|
}
|
2020-09-07 06:07:15 -05:00
|
|
|
stateDB, err := b.blockchain.State()
|
2016-08-22 07:01:28 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-09-07 06:07:15 -05:00
|
|
|
res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), stateDB)
|
2020-04-22 03:25:36 -05:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-06-08 03:09:49 -05:00
|
|
|
// 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
|
2016-08-22 07:01:28 -05:00
|
|
|
}
|
2016-03-17 12:27:37 -05:00
|
|
|
|
2016-08-22 07:01:28 -05:00
|
|
|
// PendingCallContract executes a contract call on the pending state.
|
|
|
|
func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
|
2016-08-22 16:20:13 -05:00
|
|
|
b.mu.Lock()
|
|
|
|
defer b.mu.Unlock()
|
2016-10-04 05:36:02 -05:00
|
|
|
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
|
2016-08-22 16:20:13 -05:00
|
|
|
|
2020-04-22 03:25:36 -05:00
|
|
|
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-06-08 03:09:49 -05:00
|
|
|
// 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
|
2016-03-17 12:27:37 -05:00
|
|
|
}
|
|
|
|
|
2016-08-22 07:01:28 -05:00
|
|
|
// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
|
2016-03-21 07:34:49 -05:00
|
|
|
// the nonce currently pending for the account.
|
2020-09-07 06:07:15 -05:00
|
|
|
func (b *SimulatedBackend) PendingNonceAt(_ context.Context, account common.Address) (uint64, error) {
|
2016-08-22 16:20:13 -05:00
|
|
|
b.mu.Lock()
|
|
|
|
defer b.mu.Unlock()
|
|
|
|
|
2016-03-17 12:27:37 -05:00
|
|
|
return b.pendingState.GetOrNewStateObject(account).Nonce(), nil
|
|
|
|
}
|
|
|
|
|
2016-03-21 07:34:49 -05:00
|
|
|
// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
|
2018-09-28 15:47:46 -05:00
|
|
|
// chain doesn't have miners, we just return a gas price of 1 for any call.
|
2020-09-07 06:07:15 -05:00
|
|
|
func (b *SimulatedBackend) SuggestGasPrice(_ context.Context) (*big.Int, error) {
|
2016-03-17 12:27:37 -05:00
|
|
|
return big.NewInt(1), nil
|
|
|
|
}
|
|
|
|
|
2016-08-22 07:01:28 -05:00
|
|
|
// EstimateGas executes the requested code against the currently pending block/state and
|
|
|
|
// returns the used amount of gas.
|
2017-11-13 05:47:27 -06:00
|
|
|
func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) {
|
2016-08-22 16:20:13 -05:00
|
|
|
b.mu.Lock()
|
|
|
|
defer b.mu.Unlock()
|
|
|
|
|
2017-11-14 10:26:31 -06:00
|
|
|
// Determine the lowest and highest possible gas limits to binary search in between
|
2017-08-23 22:18:21 -05:00
|
|
|
var (
|
2017-11-14 10:26:31 -06:00
|
|
|
lo uint64 = params.TxGas - 1
|
|
|
|
hi uint64
|
|
|
|
cap uint64
|
2017-08-23 22:18:21 -05:00
|
|
|
)
|
2017-11-13 05:47:27 -06:00
|
|
|
if call.Gas >= params.TxGas {
|
|
|
|
hi = call.Gas
|
2017-01-20 16:39:16 -06:00
|
|
|
} else {
|
2017-11-13 05:47:27 -06:00
|
|
|
hi = b.pendingBlock.GasLimit()
|
2017-01-20 16:39:16 -06:00
|
|
|
}
|
2020-05-11 03:08:20 -05:00
|
|
|
// Recap the highest gas allowance with account's balance.
|
2020-07-20 07:52:42 -05:00
|
|
|
if call.GasPrice != nil && call.GasPrice.BitLen() != 0 {
|
2020-05-11 03:08:20 -05:00
|
|
|
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, errors.New("insufficient funds for transfer")
|
|
|
|
}
|
|
|
|
available.Sub(available, call.Value)
|
|
|
|
}
|
|
|
|
allowance := new(big.Int).Div(available, call.GasPrice)
|
2020-07-20 07:52:42 -05:00
|
|
|
if allowance.IsUint64() && hi > allowance.Uint64() {
|
2020-05-11 03:08:20 -05:00
|
|
|
transfer := call.Value
|
|
|
|
if transfer == nil {
|
|
|
|
transfer = new(big.Int)
|
|
|
|
}
|
|
|
|
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
|
|
|
|
"sent", transfer, "gasprice", call.GasPrice, "fundable", allowance)
|
|
|
|
hi = allowance.Uint64()
|
|
|
|
}
|
|
|
|
}
|
2017-11-14 10:26:31 -06:00
|
|
|
cap = hi
|
|
|
|
|
|
|
|
// Create a helper to check if a gas allowance results in an executable transaction
|
2020-04-22 03:25:36 -05:00
|
|
|
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
|
2017-11-13 05:47:27 -06:00
|
|
|
call.Gas = gas
|
2017-01-20 16:39:16 -06:00
|
|
|
|
|
|
|
snapshot := b.pendingState.Snapshot()
|
2020-04-22 03:25:36 -05:00
|
|
|
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
2017-01-20 16:39:16 -06:00
|
|
|
b.pendingState.RevertToSnapshot(snapshot)
|
|
|
|
|
2020-04-22 03:25:36 -05:00
|
|
|
if err != nil {
|
|
|
|
if err == core.ErrIntrinsicGas {
|
|
|
|
return true, nil, nil // Special case, raise gas limit
|
|
|
|
}
|
|
|
|
return true, nil, err // Bail out
|
2017-11-14 10:26:31 -06:00
|
|
|
}
|
2020-04-22 03:25:36 -05:00
|
|
|
return res.Failed(), res, nil
|
2017-11-14 10:26:31 -06:00
|
|
|
}
|
|
|
|
// Execute the binary search and hone in on an executable gas limit
|
|
|
|
for lo+1 < hi {
|
|
|
|
mid := (hi + lo) / 2
|
2020-04-22 03:25:36 -05:00
|
|
|
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 {
|
2017-01-20 16:39:16 -06:00
|
|
|
lo = mid
|
2017-11-14 10:26:31 -06:00
|
|
|
} else {
|
|
|
|
hi = mid
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Reject the transaction as invalid if it still fails at the highest allowance
|
|
|
|
if hi == cap {
|
2020-04-22 03:25:36 -05:00
|
|
|
failed, result, err := executable(hi)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
if failed {
|
|
|
|
if result != nil && result.Err != vm.ErrOutOfGas {
|
|
|
|
if len(result.Revert()) > 0 {
|
2020-06-08 03:09:49 -05:00
|
|
|
return 0, newRevertError(result)
|
2020-04-22 03:25:36 -05:00
|
|
|
}
|
2020-06-08 03:09:49 -05:00
|
|
|
return 0, result.Err
|
2020-04-22 03:25:36 -05:00
|
|
|
}
|
|
|
|
// Otherwise, the specified gas cap is too low
|
|
|
|
return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
|
2017-01-20 16:39:16 -06:00
|
|
|
}
|
|
|
|
}
|
2017-11-13 05:47:27 -06:00
|
|
|
return hi, nil
|
2016-08-22 07:01:28 -05:00
|
|
|
}
|
2016-03-17 12:27:37 -05:00
|
|
|
|
2018-01-05 04:39:24 -06:00
|
|
|
// callContract implements common code between normal and pending contract calls.
|
2016-08-22 07:01:28 -05:00
|
|
|
// state is modified during execution, make sure to copy it if necessary.
|
2020-09-07 06:07:15 -05:00
|
|
|
func (b *SimulatedBackend) callContract(_ context.Context, call ethereum.CallMsg, block *types.Block, stateDB *state.StateDB) (*core.ExecutionResult, error) {
|
2016-08-22 07:01:28 -05:00
|
|
|
// Ensure message is initialized properly.
|
|
|
|
if call.GasPrice == nil {
|
|
|
|
call.GasPrice = big.NewInt(1)
|
|
|
|
}
|
2017-11-13 05:47:27 -06:00
|
|
|
if call.Gas == 0 {
|
|
|
|
call.Gas = 50000000
|
2016-03-17 12:27:37 -05:00
|
|
|
}
|
2016-08-22 07:01:28 -05:00
|
|
|
if call.Value == nil {
|
|
|
|
call.Value = new(big.Int)
|
|
|
|
}
|
|
|
|
// Set infinite balance to the fake caller account.
|
2020-09-07 06:07:15 -05:00
|
|
|
from := stateDB.GetOrNewStateObject(call.From)
|
common: move big integer math to common/math (#3699)
* common: remove CurrencyToString
Move denomination values to params instead.
* common: delete dead code
* common: move big integer operations to common/math
This commit consolidates all big integer operations into common/math and
adds tests and documentation.
There should be no change in semantics for BigPow, BigMin, BigMax, S256,
U256, Exp and their behaviour is now locked in by tests.
The BigD, BytesToBig and Bytes2Big functions don't provide additional
value, all uses are replaced by new(big.Int).SetBytes().
BigToBytes is now called PaddedBigBytes, its minimum output size
parameter is now specified as the number of bytes instead of bits. The
single use of this function is in the EVM's MSTORE instruction.
Big and String2Big are replaced by ParseBig, which is slightly stricter.
It previously accepted leading zeros for hexadecimal inputs but treated
decimal inputs as octal if a leading zero digit was present.
ParseUint64 is used in places where String2Big was used to decode a
uint64.
The new functions MustParseBig and MustParseUint64 are now used in many
places where parsing errors were previously ignored.
* common: delete unused big integer variables
* accounts/abi: replace uses of BytesToBig with use of encoding/binary
* common: remove BytesToBig
* common: remove Bytes2Big
* common: remove BigTrue
* cmd/utils: add BigFlag and use it for error-checked integer flags
While here, remove environment variable processing for DirectoryFlag
because we don't use it.
* core: add missing error checks in genesis block parser
* common: remove String2Big
* cmd/evm: use utils.BigFlag
* common/math: check for 256 bit overflow in ParseBig
This is supposed to prevent silent overflow/truncation of values in the
genesis block JSON. Without this check, a genesis block that set a
balance larger than 256 bits would lead to weird behaviour in the VM.
* cmd/utils: fixup import
2017-02-26 15:21:51 -06:00
|
|
|
from.SetBalance(math.MaxBig256)
|
2016-08-22 07:01:28 -05:00
|
|
|
// Execute the call.
|
2020-09-07 06:07:15 -05:00
|
|
|
msg := callMsg{call}
|
2016-12-05 19:16:03 -06:00
|
|
|
|
2017-04-12 08:38:31 -05:00
|
|
|
evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain, nil)
|
2016-12-05 19:16:03 -06:00
|
|
|
// Create a new environment which holds all relevant information
|
|
|
|
// about the transaction and calling mechanisms.
|
2020-09-07 06:07:15 -05:00
|
|
|
vmEnv := vm.NewEVM(evmContext, stateDB, b.config, vm.Config{})
|
|
|
|
gasPool := new(core.GasPool).AddGas(math.MaxUint64)
|
2017-11-13 05:47:27 -06:00
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
return core.NewStateTransition(vmEnv, msg, gasPool).TransitionDb()
|
2016-03-17 12:27:37 -05:00
|
|
|
}
|
|
|
|
|
2016-08-22 07:01:28 -05:00
|
|
|
// SendTransaction updates the pending block to include the given transaction.
|
|
|
|
// It panics if the transaction is invalid.
|
2020-09-07 06:07:15 -05:00
|
|
|
func (b *SimulatedBackend) SendTransaction(_ context.Context, tx *types.Transaction) error {
|
2016-08-22 16:20:13 -05:00
|
|
|
b.mu.Lock()
|
|
|
|
defer b.mu.Unlock()
|
|
|
|
|
2019-06-18 03:49:30 -05:00
|
|
|
sender, err := types.Sender(types.NewEIP155Signer(b.config.ChainID), tx)
|
2016-08-22 07:01:28 -05:00
|
|
|
if err != nil {
|
|
|
|
panic(fmt.Errorf("invalid transaction: %v", err))
|
|
|
|
}
|
|
|
|
nonce := b.pendingState.GetNonce(sender)
|
|
|
|
if tx.Nonce() != nonce {
|
|
|
|
panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce))
|
|
|
|
}
|
|
|
|
|
2017-12-22 06:37:50 -06:00
|
|
|
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
2016-03-17 12:27:37 -05:00
|
|
|
for _, tx := range b.pendingBlock.Transactions() {
|
2018-01-29 04:47:08 -06:00
|
|
|
block.AddTxWithChain(b.blockchain, tx)
|
2016-03-17 12:27:37 -05:00
|
|
|
}
|
2018-01-29 04:47:08 -06:00
|
|
|
block.AddTxWithChain(b.blockchain, tx)
|
2016-03-17 12:27:37 -05:00
|
|
|
})
|
2020-09-07 06:07:15 -05:00
|
|
|
stateDB, _ := b.blockchain.State()
|
2018-02-05 10:40:32 -06:00
|
|
|
|
2016-03-17 12:27:37 -05:00
|
|
|
b.pendingBlock = blocks[0]
|
2020-09-07 06:07:15 -05:00
|
|
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
|
2016-03-17 12:27:37 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-01-05 04:39:24 -06:00
|
|
|
// 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) {
|
2018-07-12 09:36:07 -05:00
|
|
|
var filter *filters.Filter
|
|
|
|
if query.BlockHash != nil {
|
|
|
|
// Block filter requested, construct a single-shot filter
|
|
|
|
filter = filters.NewBlockFilter(&filterBackend{b.database, b.blockchain}, *query.BlockHash, query.Addresses, query.Topics)
|
|
|
|
} else {
|
2020-09-07 06:07:15 -05:00
|
|
|
// Initialize unset filter boundaries to run from genesis to chain head
|
2018-07-12 09:36:07 -05:00
|
|
|
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 = filters.NewRangeFilter(&filterBackend{b.database, b.blockchain}, from, to, query.Addresses, query.Topics)
|
2018-01-05 04:39:24 -06:00
|
|
|
}
|
2018-07-12 09:36:07 -05:00
|
|
|
// Run the filter and return all the logs
|
2018-01-05 04:39:24 -06:00
|
|
|
logs, err := filter.Logs(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
res := make([]types.Log, len(logs))
|
2020-09-07 06:07:15 -05:00
|
|
|
for i, nLog := range logs {
|
|
|
|
res[i] = *nLog
|
2018-01-05 04:39:24 -06:00
|
|
|
}
|
|
|
|
return res, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SubscribeFilterLogs creates a background log filtering operation, returning a
|
|
|
|
// subscription immediately, which can be used to stream the found events.
|
2020-09-07 06:07:15 -05:00
|
|
|
func (b *SimulatedBackend) SubscribeFilterLogs(_ context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
|
2018-01-05 04:39:24 -06:00
|
|
|
// 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:
|
2020-09-07 06:07:15 -05:00
|
|
|
for _, nlog := range logs {
|
2018-01-05 04:39:24 -06:00
|
|
|
select {
|
2020-09-07 06:07:15 -05:00
|
|
|
case ch <- *nlog:
|
2018-01-05 04:39:24 -06:00
|
|
|
case err := <-sub.Err():
|
|
|
|
return err
|
|
|
|
case <-quit:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case err := <-sub.Err():
|
|
|
|
return err
|
|
|
|
case <-quit:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}), nil
|
|
|
|
}
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
// SubscribeNewHead returns an event subscription for a new header.
|
|
|
|
func (b *SimulatedBackend) SubscribeNewHead(_ context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) {
|
2019-12-20 04:33:32 -06:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2018-01-05 04:39:24 -06:00
|
|
|
// AdjustTime adds a time shift to the simulated clock.
|
2020-08-26 02:37:00 -05:00
|
|
|
// It can only be called on empty blocks.
|
2017-09-15 08:20:29 -05:00
|
|
|
func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
|
|
|
|
b.mu.Lock()
|
|
|
|
defer b.mu.Unlock()
|
2019-12-20 04:33:32 -06:00
|
|
|
|
2020-08-26 02:37:00 -05:00
|
|
|
if len(b.pendingBlock.Transactions()) != 0 {
|
|
|
|
return errors.New("Could not adjust time on non-empty block")
|
|
|
|
}
|
|
|
|
|
2017-12-22 06:37:50 -06:00
|
|
|
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
2017-09-15 08:20:29 -05:00
|
|
|
block.OffsetTime(int64(adjustment.Seconds()))
|
|
|
|
})
|
2020-09-07 06:07:15 -05:00
|
|
|
stateDB, _ := b.blockchain.State()
|
2018-02-05 10:40:32 -06:00
|
|
|
|
2017-09-15 08:20:29 -05:00
|
|
|
b.pendingBlock = blocks[0]
|
2020-09-07 06:07:15 -05:00
|
|
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
|
2017-09-15 08:20:29 -05:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
all: on-chain oracle checkpoint syncing (#19543)
* all: implement simple checkpoint syncing
cmd, les, node: remove callback mechanism
cmd, node: remove callback definition
les: simplify the registrar
les: expose checkpoint rpc services in the light client
les, light: don't store untrusted receipt
cmd, contracts, les: discard stale checkpoint
cmd, contracts/registrar: loose restriction of registeration
cmd, contracts: add replay-protection
all: off-chain multi-signature contract
params: deploy checkpoint contract for rinkeby
cmd/registrar: add raw signing mode for registrar
cmd/registrar, contracts/registrar, les: fixed messages
* cmd/registrar, contracts/registrar: fix lints
* accounts/abi/bind, les: address comments
* cmd, contracts, les, light, params: minor checkpoint sync cleanups
* cmd, eth, les, light: move checkpoint config to config file
* cmd, eth, les, params: address comments
* eth, les, params: address comments
* cmd: polish up the checkpoint admin CLI
* cmd, contracts, params: deploy new version contract
* cmd/checkpoint-admin: add another flag for clef mode signing
* cmd, contracts, les: rename and regen checkpoint oracle with abigen
2019-06-28 02:34:02 -05:00
|
|
|
// Blockchain returns the underlying blockchain.
|
|
|
|
func (b *SimulatedBackend) Blockchain() *core.BlockChain {
|
|
|
|
return b.blockchain
|
|
|
|
}
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
// callMsg implements core.Message to allow passing it as a transaction simulator.
|
|
|
|
type callMsg struct {
|
2016-08-22 07:01:28 -05:00
|
|
|
ethereum.CallMsg
|
2016-03-17 12:27:37 -05:00
|
|
|
}
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
func (m callMsg) From() common.Address { return m.CallMsg.From }
|
|
|
|
func (m callMsg) Nonce() uint64 { return 0 }
|
|
|
|
func (m callMsg) CheckNonce() bool { return false }
|
|
|
|
func (m callMsg) To() *common.Address { return m.CallMsg.To }
|
|
|
|
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
|
|
|
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
|
|
|
|
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
|
|
|
|
func (m callMsg) Data() []byte { return m.CallMsg.Data }
|
2018-01-05 04:39:24 -06:00
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
|
|
|
func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db }
|
|
|
|
func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") }
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
func (fb *filterBackend) HeaderByNumber(_ context.Context, block rpc.BlockNumber) (*types.Header, error) {
|
2018-01-05 04:39:24 -06:00
|
|
|
if block == rpc.LatestBlockNumber {
|
|
|
|
return fb.bc.CurrentHeader(), nil
|
|
|
|
}
|
|
|
|
return fb.bc.GetHeaderByNumber(uint64(block.Int64())), nil
|
|
|
|
}
|
2018-02-22 04:48:14 -06:00
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
func (fb *filterBackend) HeaderByHash(_ context.Context, hash common.Hash) (*types.Header, error) {
|
2018-07-12 09:36:07 -05:00
|
|
|
return fb.bc.GetHeaderByHash(hash), nil
|
|
|
|
}
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
func (fb *filterBackend) GetReceipts(_ context.Context, hash common.Hash) (types.Receipts, error) {
|
2018-05-07 06:35:06 -05:00
|
|
|
number := rawdb.ReadHeaderNumber(fb.db, hash)
|
|
|
|
if number == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2019-04-15 04:36:27 -05:00
|
|
|
return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil
|
2018-01-05 04:39:24 -06:00
|
|
|
}
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
func (fb *filterBackend) GetLogs(_ context.Context, hash common.Hash) ([][]*types.Log, error) {
|
2018-05-07 06:35:06 -05:00
|
|
|
number := rawdb.ReadHeaderNumber(fb.db, hash)
|
|
|
|
if number == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2019-04-15 04:36:27 -05:00
|
|
|
receipts := rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config())
|
2018-02-22 04:48:14 -06:00
|
|
|
if receipts == nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
logs := make([][]*types.Log, len(receipts))
|
|
|
|
for i, receipt := range receipts {
|
|
|
|
logs[i] = receipt.Logs
|
|
|
|
}
|
|
|
|
return logs, nil
|
|
|
|
}
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
func (fb *filterBackend) SubscribeNewTxsEvent(_ chan<- core.NewTxsEvent) event.Subscription {
|
2019-12-10 05:39:14 -06:00
|
|
|
return nullSubscription()
|
2018-01-05 04:39:24 -06:00
|
|
|
}
|
2019-12-10 05:39:14 -06:00
|
|
|
|
2018-01-05 04:39:24 -06:00
|
|
|
func (fb *filterBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
|
|
|
|
return fb.bc.SubscribeChainEvent(ch)
|
|
|
|
}
|
2019-12-10 05:39:14 -06:00
|
|
|
|
2018-01-05 04:39:24 -06:00
|
|
|
func (fb *filterBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
|
|
|
return fb.bc.SubscribeRemovedLogsEvent(ch)
|
|
|
|
}
|
2019-12-10 05:39:14 -06:00
|
|
|
|
2018-01-05 04:39:24 -06:00
|
|
|
func (fb *filterBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
|
|
|
|
return fb.bc.SubscribeLogsEvent(ch)
|
|
|
|
}
|
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
func (fb *filterBackend) SubscribePendingLogsEvent(_ chan<- []*types.Log) event.Subscription {
|
2019-12-10 05:39:14 -06:00
|
|
|
return nullSubscription()
|
|
|
|
}
|
|
|
|
|
2018-01-05 04:39:24 -06:00
|
|
|
func (fb *filterBackend) BloomStatus() (uint64, uint64) { return 4096, 0 }
|
2019-12-10 05:39:14 -06:00
|
|
|
|
2020-09-07 06:07:15 -05:00
|
|
|
func (fb *filterBackend) ServiceFilter(_ context.Context, _ *bloombits.MatcherSession) {
|
2018-01-05 04:39:24 -06:00
|
|
|
panic("not supported")
|
|
|
|
}
|
2019-12-10 05:39:14 -06:00
|
|
|
|
|
|
|
func nullSubscription() event.Subscription {
|
|
|
|
return event.NewSubscription(func(quit <-chan struct{}) error {
|
|
|
|
<-quit
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
}
|