aboutsummaryrefslogtreecommitdiffstats
path: root/accounts/abi/bind/backends/simulated.go
diff options
context:
space:
mode:
Diffstat (limited to 'accounts/abi/bind/backends/simulated.go')
-rw-r--r--accounts/abi/bind/backends/simulated.go162
1 files changed, 74 insertions, 88 deletions
diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index 687a31bf1..c2542f40e 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -17,8 +17,10 @@
package backends
import (
+ "fmt"
"math/big"
+ "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
@@ -79,58 +81,44 @@ func (b *SimulatedBackend) Rollback() {
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
}
-// HasCode implements ContractVerifier.HasCode, checking whether there is any
-// code associated with a certain account in the blockchain.
-func (b *SimulatedBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) {
- if pending {
- return len(b.pendingState.GetCode(contract)) > 0, nil
+// CodeAt implements ChainStateReader.CodeAt, returning the code associated with
+// a certain account at a given block number in the blockchain.
+func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
+ if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
+ return nil, fmt.Errorf("SimulatedBackend cannot access blocks other than the latest block")
}
statedb, _ := b.blockchain.State()
- return len(statedb.GetCode(contract)) > 0, nil
+ return statedb.GetCode(contract), nil
}
-// ContractCall implements ContractCaller.ContractCall, executing the specified
-// contract with the given input data.
-func (b *SimulatedBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) {
- // Create a copy of the current state db to screw around with
- var (
- block *types.Block
- statedb *state.StateDB
- )
- if pending {
- block, statedb = b.pendingBlock, b.pendingState.Copy()
- } else {
- block = b.blockchain.CurrentBlock()
- statedb, _ = b.blockchain.State()
- }
- // If there's no code to interact with, respond with an appropriate error
- if code := statedb.GetCode(contract); len(code) == 0 {
- return nil, bind.ErrNoCode
- }
- // Set infinite balance to the a fake caller account
- from := statedb.GetOrNewStateObject(common.Address{})
- from.SetBalance(common.MaxBig)
+// PendingCodeAt implements PendingStateReader.PendingCodeAt, returning the
+// code associated with a certain account in the pending state of the blockchain.
+func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
+ return b.pendingState.GetCode(contract), nil
+}
- // Assemble the call invocation to measure the gas usage
- msg := callmsg{
- from: from,
- to: &contract,
- gasPrice: new(big.Int),
- gasLimit: common.MaxBig,
- value: new(big.Int),
- data: data,
+// CallContract executes a contract call.
+func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
+ if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
+ return nil, fmt.Errorf("SimulatedBackend cannot access blocks other than the latest block")
}
- // Execute the call and return
- vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
- gaspool := new(core.GasPool).AddGas(common.MaxBig)
+ state, err := b.blockchain.State()
+ if err != nil {
+ return nil, err
+ }
+ rval, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
+ return rval, err
+}
- out, _, err := core.ApplyMessage(vmenv, msg, gaspool)
- return out, err
+// PendingCallContract executes a contract call on the pending state.
+func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
+ rval, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState.Copy())
+ return rval, err
}
-// PendingAccountNonce implements ContractTransactor.PendingAccountNonce, retrieving
+// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
// the nonce currently pending for the account.
-func (b *SimulatedBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) {
+func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
return b.pendingState.GetOrNewStateObject(account).Nonce(), nil
}
@@ -140,45 +128,49 @@ func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error
return big.NewInt(1), nil
}
-// EstimateGasLimit implements ContractTransactor.EstimateGasLimit, executing the
-// requested code against the currently pending block/state and returning the used
-// gas.
-func (b *SimulatedBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) {
- // Create a copy of the currently pending state db to screw around with
- var (
- block = b.pendingBlock
- statedb = b.pendingState.Copy()
- )
- // If there's no code to interact with, respond with an appropriate error
- if contract != nil {
- if code := statedb.GetCode(*contract); len(code) == 0 {
- return nil, bind.ErrNoCode
- }
- }
- // Set infinite balance to the a fake caller account
- from := statedb.GetOrNewStateObject(sender)
- from.SetBalance(common.MaxBig)
+// 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) (*big.Int, error) {
+ _, gas, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState.Copy())
+ return gas, err
+}
- // Assemble the call invocation to measure the gas usage
- msg := callmsg{
- from: from,
- to: contract,
- gasPrice: new(big.Int),
- gasLimit: common.MaxBig,
- value: value,
- data: data,
+// callContract implemens 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, block *types.Block, statedb *state.StateDB) ([]byte, *big.Int, error) {
+ // Ensure message is initialized properly.
+ if call.GasPrice == nil {
+ call.GasPrice = big.NewInt(1)
+ }
+ if call.Gas == nil || call.Gas.BitLen() == 0 {
+ call.Gas = big.NewInt(50000000)
}
- // Execute the call and return
+ if call.Value == nil {
+ call.Value = new(big.Int)
+ }
+ // Set infinite balance to the fake caller account.
+ from := statedb.GetOrNewStateObject(call.From)
+ from.SetBalance(common.MaxBig)
+ // Execute the call.
+ msg := callmsg{call}
vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
gaspool := new(core.GasPool).AddGas(common.MaxBig)
-
- _, gas, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
- return gas, err
+ ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
+ return ret, gasUsed, err
}
-// SendTransaction implements ContractTransactor.SendTransaction, delegating the raw
-// transaction injection to the remote node.
+// SendTransaction updates the pending block to include the given transaction.
+// It panics if the transaction is invalid.
func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
+ sender, err := tx.From()
+ 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))
+ }
+
blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) {
for _, tx := range b.pendingBlock.Transactions() {
block.AddTx(tx)
@@ -187,26 +179,20 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
})
b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
-
return nil
}
// callmsg implements core.Message to allow passing it as a transaction simulator.
type callmsg struct {
- from *state.StateObject
- to *common.Address
- gasLimit *big.Int
- gasPrice *big.Int
- value *big.Int
- data []byte
+ ethereum.CallMsg
}
-func (m callmsg) From() (common.Address, error) { return m.from.Address(), nil }
-func (m callmsg) FromFrontier() (common.Address, error) { return m.from.Address(), nil }
+func (m callmsg) From() (common.Address, error) { return m.CallMsg.From, nil }
+func (m callmsg) FromFrontier() (common.Address, error) { return m.CallMsg.From, nil }
func (m callmsg) Nonce() uint64 { return 0 }
func (m callmsg) CheckNonce() bool { return false }
-func (m callmsg) To() *common.Address { return m.to }
-func (m callmsg) GasPrice() *big.Int { return m.gasPrice }
-func (m callmsg) Gas() *big.Int { return m.gasLimit }
-func (m callmsg) Value() *big.Int { return m.value }
-func (m callmsg) Data() []byte { return m.data }
+func (m callmsg) To() *common.Address { return m.CallMsg.To }
+func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
+func (m callmsg) Gas() *big.Int { return m.CallMsg.Gas }
+func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
+func (m callmsg) Data() []byte { return m.CallMsg.Data }