github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/accounts/abi/bind/backends/simulated.go (about)

     1  // Copyright 2016 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package backends
    18  
    19  import (
    20  	"math/big"
    21  
    22  	"github.com/ethereumproject/go-ethereum/accounts/abi/bind"
    23  	"github.com/ethereumproject/go-ethereum/common"
    24  	"github.com/ethereumproject/go-ethereum/core"
    25  	"github.com/ethereumproject/go-ethereum/core/state"
    26  	"github.com/ethereumproject/go-ethereum/core/types"
    27  	"github.com/ethereumproject/go-ethereum/ethdb"
    28  	"github.com/ethereumproject/go-ethereum/event"
    29  )
    30  
    31  // This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
    32  var _ bind.ContractBackend = (*SimulatedBackend)(nil)
    33  
    34  // SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
    35  // the background. Its main purpose is to allow easily testing contract bindings.
    36  type SimulatedBackend struct {
    37  	database   ethdb.Database   // In memory database to store our testing data
    38  	blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
    39  
    40  	pendingBlock *types.Block   // Currently pending block that will be imported on request
    41  	pendingState *state.StateDB // Currently pending state that will be the active on on request
    42  }
    43  
    44  // NewSimulatedBackend creates a new binding backend using a simulated blockchain
    45  // for testing purposes.
    46  func NewSimulatedBackend(accounts ...core.GenesisAccount) *SimulatedBackend {
    47  	database, _ := ethdb.NewMemDatabase()
    48  	core.WriteGenesisBlockForTesting(database, accounts...)
    49  	blockchain, _ := core.NewBlockChain(database, core.DefaultConfigMorden.ChainConfig, new(core.FakePow), new(event.TypeMux))
    50  
    51  	backend := &SimulatedBackend{
    52  		database:   database,
    53  		blockchain: blockchain,
    54  	}
    55  	backend.Rollback()
    56  
    57  	return backend
    58  }
    59  
    60  // Commit imports all the pending transactions as a single block and starts a
    61  // fresh new state.
    62  func (b *SimulatedBackend) Commit() {
    63  	if res := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); res.Error != nil {
    64  		panic(res.Error) // This cannot happen unless the simulator is wrong, fail in that case
    65  	}
    66  	b.Rollback()
    67  }
    68  
    69  // Rollback aborts all pending transactions, reverting to the last committed state.
    70  func (b *SimulatedBackend) Rollback() {
    71  	blocks, _ := core.GenerateChain(core.DefaultConfigMorden.ChainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {})
    72  
    73  	b.pendingBlock = blocks[0]
    74  	b.pendingState, _ = state.New(b.pendingBlock.Root(), state.NewDatabase(b.database))
    75  }
    76  
    77  // HasCode implements ContractVerifier.HasCode, checking whether there is any
    78  // code associated with a certain account in the blockchain.
    79  func (b *SimulatedBackend) HasCode(contract common.Address, pending bool) (bool, error) {
    80  	if pending {
    81  		return len(b.pendingState.GetCode(contract)) > 0, nil
    82  	}
    83  	statedb, _ := b.blockchain.State()
    84  	return len(statedb.GetCode(contract)) > 0, nil
    85  }
    86  
    87  // ContractCall implements ContractCaller.ContractCall, executing the specified
    88  // contract with the given input data.
    89  func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pending bool) ([]byte, error) {
    90  	// Create a copy of the current state db to screw around with
    91  	var (
    92  		block   *types.Block
    93  		statedb *state.StateDB
    94  	)
    95  	if pending {
    96  		block, statedb = b.pendingBlock, b.pendingState
    97  		defer statedb.RevertToSnapshot(statedb.Snapshot())
    98  	} else {
    99  		block = b.blockchain.CurrentBlock()
   100  		statedb, _ = b.blockchain.State()
   101  	}
   102  	// If there's no code to interact with, respond with an appropriate error
   103  	if code := statedb.GetCode(contract); len(code) == 0 {
   104  		return nil, bind.ErrNoCode
   105  	}
   106  	// Set infinite balance to the a fake caller account
   107  	from := statedb.GetOrNewStateObject(common.Address{})
   108  	from.SetBalance(common.MaxBig)
   109  
   110  	// Assemble the call invocation to measure the gas usage
   111  	msg := callmsg{
   112  		from:     from,
   113  		to:       &contract,
   114  		gasPrice: new(big.Int),
   115  		gasLimit: common.MaxBig,
   116  		value:    new(big.Int),
   117  		data:     data,
   118  	}
   119  	// Execute the call and return
   120  	vmenv := core.NewEnv(statedb, core.DefaultConfigMorden.ChainConfig, b.blockchain, msg, block.Header())
   121  	gaspool := new(core.GasPool).AddGas(common.MaxBig)
   122  
   123  	out, _, _, err := core.ApplyMessage(vmenv, msg, gaspool)
   124  	return out, err
   125  }
   126  
   127  // PendingAccountNonce implements ContractTransactor.PendingAccountNonce, retrieving
   128  // the nonce currently pending for the account.
   129  func (b *SimulatedBackend) PendingAccountNonce(account common.Address) (uint64, error) {
   130  	return b.pendingState.GetOrNewStateObject(account).Nonce(), nil
   131  }
   132  
   133  // SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
   134  // chain doens't have miners, we just return a gas price of 1 for any call.
   135  func (b *SimulatedBackend) SuggestGasPrice() (*big.Int, error) {
   136  	return big.NewInt(1), nil
   137  }
   138  
   139  // EstimateGasLimit implements ContractTransactor.EstimateGasLimit, executing the
   140  // requested code against the currently pending block/state and returning the used
   141  // gas.
   142  func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) {
   143  	// Create a copy of the currently pending state db to screw around with
   144  	var (
   145  		block   = b.pendingBlock
   146  		statedb = b.pendingState
   147  	)
   148  	defer statedb.RevertToSnapshot(statedb.Snapshot())
   149  
   150  	// If there's no code to interact with, respond with an appropriate error
   151  	if contract != nil {
   152  		if code := statedb.GetCode(*contract); len(code) == 0 {
   153  			return nil, bind.ErrNoCode
   154  		}
   155  	}
   156  	// Set infinite balance to the a fake caller account
   157  	from := statedb.GetOrNewStateObject(sender)
   158  	from.SetBalance(common.MaxBig)
   159  
   160  	// Assemble the call invocation to measure the gas usage
   161  	msg := callmsg{
   162  		from:     from,
   163  		to:       contract,
   164  		gasPrice: new(big.Int),
   165  		gasLimit: common.MaxBig,
   166  		value:    value,
   167  		data:     data,
   168  	}
   169  	// Execute the call and return
   170  	vmenv := core.NewEnv(statedb, core.DefaultConfigMorden.ChainConfig, b.blockchain, msg, block.Header())
   171  	gaspool := new(core.GasPool).AddGas(common.MaxBig)
   172  
   173  	_, gas, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
   174  	return gas, err
   175  }
   176  
   177  // SendTransaction implements ContractTransactor.SendTransaction, delegating the raw
   178  // transaction injection to the remote node.
   179  func (b *SimulatedBackend) SendTransaction(tx *types.Transaction) error {
   180  	blocks, _ := core.GenerateChain(core.DefaultConfigMorden.ChainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) {
   181  		for _, tx := range b.pendingBlock.Transactions() {
   182  			block.AddTx(tx)
   183  		}
   184  		block.AddTx(tx)
   185  	})
   186  	b.pendingBlock = blocks[0]
   187  	b.pendingState, _ = state.New(b.pendingBlock.Root(), state.NewDatabase(b.database))
   188  
   189  	return nil
   190  }
   191  
   192  // callmsg implements core.Message to allow passing it as a transaction simulator.
   193  type callmsg struct {
   194  	from     *state.StateObject
   195  	to       *common.Address
   196  	gasLimit *big.Int
   197  	gasPrice *big.Int
   198  	value    *big.Int
   199  	data     []byte
   200  }
   201  
   202  func (m callmsg) From() (common.Address, error)         { return m.from.Address(), nil }
   203  func (m callmsg) FromFrontier() (common.Address, error) { return m.from.Address(), nil }
   204  func (m callmsg) Nonce() uint64                         { return m.from.Nonce() }
   205  func (m callmsg) To() *common.Address                   { return m.to }
   206  func (m callmsg) GasPrice() *big.Int                    { return m.gasPrice }
   207  func (m callmsg) Gas() *big.Int                         { return m.gasLimit }
   208  func (m callmsg) Value() *big.Int                       { return m.value }
   209  func (m callmsg) Data() []byte                          { return m.data }