b9df7ecdc3
* all: seperate consensus error and evm internal error There are actually two types of error will be returned when a tranaction/message call is executed: (a) consensus error (b) evm internal error. The former should be converted to a consensus issue, e.g. The sender doesn't enough asset to purchase the gas it specifies. The latter is allowed since evm itself is a blackbox and internal error is allowed to happen. This PR emphasizes the difference by introducing a executionResult structure. The evm error is embedded inside. So if any error returned, it indicates consensus issue happens. And also this PR improve the `EstimateGas` API to return the concrete revert reason if the transaction always fails * all: polish * accounts/abi/bind/backends: add tests * accounts/abi/bind/backends, internal: cleanup error message * all: address comments * core: fix lint * accounts, core, eth, internal: address comments * accounts, internal: resolve revert reason if possible * accounts, internal: address comments
931 lines
33 KiB
Go
931 lines
33 KiB
Go
// 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 backends
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"math/big"
|
|
"strings"
|
|
"testing"
|
|
"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/core"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/ethereum/go-ethereum/crypto"
|
|
"github.com/ethereum/go-ethereum/params"
|
|
)
|
|
|
|
func TestSimulatedBackend(t *testing.T) {
|
|
var gasLimit uint64 = 8000029
|
|
key, _ := crypto.GenerateKey() // nolint: gosec
|
|
auth := bind.NewKeyedTransactor(key)
|
|
genAlloc := make(core.GenesisAlloc)
|
|
genAlloc[auth.From] = core.GenesisAccount{Balance: big.NewInt(9223372036854775807)}
|
|
|
|
sim := NewSimulatedBackend(genAlloc, gasLimit)
|
|
defer sim.Close()
|
|
|
|
// should return an error if the tx is not found
|
|
txHash := common.HexToHash("2")
|
|
_, isPending, err := sim.TransactionByHash(context.Background(), txHash)
|
|
|
|
if isPending {
|
|
t.Fatal("transaction should not be pending")
|
|
}
|
|
if err != ethereum.NotFound {
|
|
t.Fatalf("err should be `ethereum.NotFound` but received %v", err)
|
|
}
|
|
|
|
// generate a transaction and confirm you can retrieve it
|
|
code := `6060604052600a8060106000396000f360606040526008565b00`
|
|
var gas uint64 = 3000000
|
|
tx := types.NewContractCreation(0, big.NewInt(0), gas, big.NewInt(1), common.FromHex(code))
|
|
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, key)
|
|
|
|
err = sim.SendTransaction(context.Background(), tx)
|
|
if err != nil {
|
|
t.Fatal("error sending transaction")
|
|
}
|
|
|
|
txHash = tx.Hash()
|
|
_, isPending, err = sim.TransactionByHash(context.Background(), txHash)
|
|
if err != nil {
|
|
t.Fatalf("error getting transaction with hash: %v", txHash.String())
|
|
}
|
|
if !isPending {
|
|
t.Fatal("transaction should have pending status")
|
|
}
|
|
|
|
sim.Commit()
|
|
_, isPending, err = sim.TransactionByHash(context.Background(), txHash)
|
|
if err != nil {
|
|
t.Fatalf("error getting transaction with hash: %v", txHash.String())
|
|
}
|
|
if isPending {
|
|
t.Fatal("transaction should not have pending status")
|
|
}
|
|
}
|
|
|
|
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
|
|
// the following is based on this contract:
|
|
// contract T {
|
|
// event received(address sender, uint amount, bytes memo);
|
|
// event receivedAddr(address sender);
|
|
//
|
|
// function receive(bytes calldata memo) external payable returns (string memory res) {
|
|
// emit received(msg.sender, msg.value, memo);
|
|
// emit receivedAddr(msg.sender);
|
|
// return "hello world";
|
|
// }
|
|
// }
|
|
const abiJSON = `[ { "constant": false, "inputs": [ { "name": "memo", "type": "bytes" } ], "name": "receive", "outputs": [ { "name": "res", "type": "string" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" }, { "indexed": false, "name": "amount", "type": "uint256" }, { "indexed": false, "name": "memo", "type": "bytes" } ], "name": "received", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" } ], "name": "receivedAddr", "type": "event" } ]`
|
|
const abiBin = `0x608060405234801561001057600080fd5b506102a0806100206000396000f3fe60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063a69b6ed014610040575b600080fd5b6100b76004803603602081101561005657600080fd5b810190808035906020019064010000000081111561007357600080fd5b82018360208201111561008557600080fd5b803590602001918460018302840111640100000000831117156100a757600080fd5b9091929391929390505050610132565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f75780820151818401526020810190506100dc565b50505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60607f75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed33348585604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509550505050505060405180910390a17f46923992397eac56cf13058aced2a1871933622717e27b24eabc13bf9dd329c833604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a16040805190810160405280600b81526020017f68656c6c6f20776f726c6400000000000000000000000000000000000000000081525090509291505056fea165627a7a72305820ff0c57dad254cfeda48c9cfb47f1353a558bccb4d1bc31da1dae69315772d29e0029`
|
|
const deployedCode = `60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063a69b6ed014610040575b600080fd5b6100b76004803603602081101561005657600080fd5b810190808035906020019064010000000081111561007357600080fd5b82018360208201111561008557600080fd5b803590602001918460018302840111640100000000831117156100a757600080fd5b9091929391929390505050610132565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f75780820151818401526020810190506100dc565b50505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60607f75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed33348585604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509550505050505060405180910390a17f46923992397eac56cf13058aced2a1871933622717e27b24eabc13bf9dd329c833604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a16040805190810160405280600b81526020017f68656c6c6f20776f726c6400000000000000000000000000000000000000000081525090509291505056fea165627a7a72305820ff0c57dad254cfeda48c9cfb47f1353a558bccb4d1bc31da1dae69315772d29e0029`
|
|
|
|
// expected return value contains "hello world"
|
|
var expectedReturn = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
|
|
|
func TestNewSimulatedBackend(t *testing.T) {
|
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
|
expectedBal := big.NewInt(10000000000)
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{
|
|
testAddr: {Balance: expectedBal},
|
|
}, 10000000,
|
|
)
|
|
defer sim.Close()
|
|
|
|
if sim.config != params.AllEthashProtocolChanges {
|
|
t.Errorf("expected sim config to equal params.AllEthashProtocolChanges, got %v", sim.config)
|
|
}
|
|
|
|
if sim.blockchain.Config() != params.AllEthashProtocolChanges {
|
|
t.Errorf("expected sim blockchain config to equal params.AllEthashProtocolChanges, got %v", sim.config)
|
|
}
|
|
|
|
statedb, _ := sim.blockchain.State()
|
|
bal := statedb.GetBalance(testAddr)
|
|
if bal.Cmp(expectedBal) != 0 {
|
|
t.Errorf("expected balance for test address not received. expected: %v actual: %v", expectedBal, bal)
|
|
}
|
|
}
|
|
|
|
func TestSimulatedBackend_AdjustTime(t *testing.T) {
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{}, 10000000,
|
|
)
|
|
defer sim.Close()
|
|
|
|
prevTime := sim.pendingBlock.Time()
|
|
err := sim.AdjustTime(time.Second)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
newTime := sim.pendingBlock.Time()
|
|
|
|
if newTime-prevTime != uint64(time.Second.Seconds()) {
|
|
t.Errorf("adjusted time not equal to a second. prev: %v, new: %v", prevTime, newTime)
|
|
}
|
|
}
|
|
|
|
func TestSimulatedBackend_BalanceAt(t *testing.T) {
|
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
|
expectedBal := big.NewInt(10000000000)
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{
|
|
testAddr: {Balance: expectedBal},
|
|
}, 10000000,
|
|
)
|
|
defer sim.Close()
|
|
bgCtx := context.Background()
|
|
|
|
bal, err := sim.BalanceAt(bgCtx, testAddr, nil)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if bal.Cmp(expectedBal) != 0 {
|
|
t.Errorf("expected balance for test address not received. expected: %v actual: %v", expectedBal, bal)
|
|
}
|
|
}
|
|
|
|
func TestSimulatedBackend_BlockByHash(t *testing.T) {
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{}, 10000000,
|
|
)
|
|
defer sim.Close()
|
|
bgCtx := context.Background()
|
|
|
|
block, err := sim.BlockByNumber(bgCtx, nil)
|
|
if err != nil {
|
|
t.Errorf("could not get recent block: %v", err)
|
|
}
|
|
blockByHash, err := sim.BlockByHash(bgCtx, block.Hash())
|
|
if err != nil {
|
|
t.Errorf("could not get recent block: %v", err)
|
|
}
|
|
|
|
if block.Hash() != blockByHash.Hash() {
|
|
t.Errorf("did not get expected block")
|
|
}
|
|
}
|
|
|
|
func TestSimulatedBackend_BlockByNumber(t *testing.T) {
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{}, 10000000,
|
|
)
|
|
defer sim.Close()
|
|
bgCtx := context.Background()
|
|
|
|
block, err := sim.BlockByNumber(bgCtx, nil)
|
|
if err != nil {
|
|
t.Errorf("could not get recent block: %v", err)
|
|
}
|
|
if block.NumberU64() != 0 {
|
|
t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64())
|
|
}
|
|
|
|
// create one block
|
|
sim.Commit()
|
|
|
|
block, err = sim.BlockByNumber(bgCtx, nil)
|
|
if err != nil {
|
|
t.Errorf("could not get recent block: %v", err)
|
|
}
|
|
if block.NumberU64() != 1 {
|
|
t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64())
|
|
}
|
|
|
|
blockByNumber, err := sim.BlockByNumber(bgCtx, big.NewInt(1))
|
|
if err != nil {
|
|
t.Errorf("could not get block by number: %v", err)
|
|
}
|
|
if blockByNumber.Hash() != block.Hash() {
|
|
t.Errorf("did not get the same block with height of 1 as before")
|
|
}
|
|
}
|
|
|
|
func TestSimulatedBackend_NonceAt(t *testing.T) {
|
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
|
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{
|
|
testAddr: {Balance: big.NewInt(10000000000)},
|
|
}, 10000000,
|
|
)
|
|
defer sim.Close()
|
|
bgCtx := context.Background()
|
|
|
|
nonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(0))
|
|
if err != nil {
|
|
t.Errorf("could not get nonce for test addr: %v", err)
|
|
}
|
|
|
|
if nonce != uint64(0) {
|
|
t.Errorf("received incorrect nonce. expected 0, got %v", nonce)
|
|
}
|
|
|
|
// create a signed transaction to send
|
|
tx := types.NewTransaction(nonce, testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
|
|
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
|
if err != nil {
|
|
t.Errorf("could not sign tx: %v", err)
|
|
}
|
|
|
|
// send tx to simulated backend
|
|
err = sim.SendTransaction(bgCtx, signedTx)
|
|
if err != nil {
|
|
t.Errorf("could not add tx to pending block: %v", err)
|
|
}
|
|
sim.Commit()
|
|
|
|
newNonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(1))
|
|
if err != nil {
|
|
t.Errorf("could not get nonce for test addr: %v", err)
|
|
}
|
|
|
|
if newNonce != nonce+uint64(1) {
|
|
t.Errorf("received incorrect nonce. expected 1, got %v", nonce)
|
|
}
|
|
}
|
|
|
|
func TestSimulatedBackend_SendTransaction(t *testing.T) {
|
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
|
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{
|
|
testAddr: {Balance: big.NewInt(10000000000)},
|
|
}, 10000000,
|
|
)
|
|
defer sim.Close()
|
|
bgCtx := context.Background()
|
|
|
|
// create a signed transaction to send
|
|
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
|
|
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
|
if err != nil {
|
|
t.Errorf("could not sign tx: %v", err)
|
|
}
|
|
|
|
// send tx to simulated backend
|
|
err = sim.SendTransaction(bgCtx, signedTx)
|
|
if err != nil {
|
|
t.Errorf("could not add tx to pending block: %v", err)
|
|
}
|
|
sim.Commit()
|
|
|
|
block, err := sim.BlockByNumber(bgCtx, big.NewInt(1))
|
|
if err != nil {
|
|
t.Errorf("could not get block at height 1: %v", err)
|
|
}
|
|
|
|
if signedTx.Hash() != block.Transactions()[0].Hash() {
|
|
t.Errorf("did not commit sent transaction. expected hash %v got hash %v", block.Transactions()[0].Hash(), signedTx.Hash())
|
|
}
|
|
}
|
|
|
|
func TestSimulatedBackend_TransactionByHash(t *testing.T) {
|
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
|
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{
|
|
testAddr: {Balance: big.NewInt(10000000000)},
|
|
}, 10000000,
|
|
)
|
|
defer sim.Close()
|
|
bgCtx := context.Background()
|
|
|
|
// create a signed transaction to send
|
|
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
|
|
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
|
if err != nil {
|
|
t.Errorf("could not sign tx: %v", err)
|
|
}
|
|
|
|
// send tx to simulated backend
|
|
err = sim.SendTransaction(bgCtx, signedTx)
|
|
if err != nil {
|
|
t.Errorf("could not add tx to pending block: %v", err)
|
|
}
|
|
|
|
// ensure tx is committed pending
|
|
receivedTx, pending, err := sim.TransactionByHash(bgCtx, signedTx.Hash())
|
|
if err != nil {
|
|
t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err)
|
|
}
|
|
if !pending {
|
|
t.Errorf("expected transaction to be in pending state")
|
|
}
|
|
if receivedTx.Hash() != signedTx.Hash() {
|
|
t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash())
|
|
}
|
|
|
|
sim.Commit()
|
|
|
|
// ensure tx is not and committed pending
|
|
receivedTx, pending, err = sim.TransactionByHash(bgCtx, signedTx.Hash())
|
|
if err != nil {
|
|
t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err)
|
|
}
|
|
if pending {
|
|
t.Errorf("expected transaction to not be in pending state")
|
|
}
|
|
if receivedTx.Hash() != signedTx.Hash() {
|
|
t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash())
|
|
}
|
|
}
|
|
|
|
func TestSimulatedBackend_EstimateGas(t *testing.T) {
|
|
/*
|
|
pragma solidity ^0.6.4;
|
|
contract GasEstimation {
|
|
function PureRevert() public { revert(); }
|
|
function Revert() public { revert("revert reason");}
|
|
function OOG() public { for (uint i = 0; ; i++) {}}
|
|
function Assert() public { assert(false);}
|
|
function Valid() public {}
|
|
}*/
|
|
const contractAbi = "[{\"inputs\":[],\"name\":\"Assert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OOG\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PureRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Revert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Valid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
|
|
const contractBin = "0x60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040523480156100115760006000fd5b506004361061005c5760003560e01c806350f6fe3414610062578063aa8b1d301461006c578063b9b046f914610076578063d8b9839114610080578063e09fface1461008a5761005c565b60006000fd5b61006a610094565b005b6100746100ad565b005b61007e6100b5565b005b6100886100c2565b005b610092610135565b005b6000600090505b5b808060010191505061009b565b505b565b60006000fd5b565b600015156100bf57fe5b5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081526020015060200191505060405180910390fd5b565b5b56fea2646970667358221220345bbcbb1a5ecf22b53a78eaebf95f8ee0eceff6d10d4b9643495084d2ec934a64736f6c63430006040033"
|
|
|
|
key, _ := crypto.GenerateKey()
|
|
addr := crypto.PubkeyToAddress(key.PublicKey)
|
|
opts := bind.NewKeyedTransactor(key)
|
|
|
|
sim := NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(params.Ether)}}, 10000000)
|
|
defer sim.Close()
|
|
|
|
parsed, _ := abi.JSON(strings.NewReader(contractAbi))
|
|
contractAddr, _, _, _ := bind.DeployContract(opts, parsed, common.FromHex(contractBin), sim)
|
|
sim.Commit()
|
|
|
|
var cases = []struct {
|
|
name string
|
|
message ethereum.CallMsg
|
|
expect uint64
|
|
expectError error
|
|
}{
|
|
{"plain transfer(valid)", ethereum.CallMsg{
|
|
From: addr,
|
|
To: &addr,
|
|
Gas: 0,
|
|
GasPrice: big.NewInt(0),
|
|
Value: big.NewInt(1),
|
|
Data: nil,
|
|
}, params.TxGas, nil},
|
|
|
|
{"plain transfer(invalid)", ethereum.CallMsg{
|
|
From: addr,
|
|
To: &contractAddr,
|
|
Gas: 0,
|
|
GasPrice: big.NewInt(0),
|
|
Value: big.NewInt(1),
|
|
Data: nil,
|
|
}, 0, errors.New("always failing transaction (execution reverted)")},
|
|
|
|
{"Revert", ethereum.CallMsg{
|
|
From: addr,
|
|
To: &contractAddr,
|
|
Gas: 0,
|
|
GasPrice: big.NewInt(0),
|
|
Value: nil,
|
|
Data: common.Hex2Bytes("d8b98391"),
|
|
}, 0, errors.New("always failing transaction (execution reverted) (revert reason)")},
|
|
|
|
{"PureRevert", ethereum.CallMsg{
|
|
From: addr,
|
|
To: &contractAddr,
|
|
Gas: 0,
|
|
GasPrice: big.NewInt(0),
|
|
Value: nil,
|
|
Data: common.Hex2Bytes("aa8b1d30"),
|
|
}, 0, errors.New("always failing transaction (execution reverted)")},
|
|
|
|
{"OOG", ethereum.CallMsg{
|
|
From: addr,
|
|
To: &contractAddr,
|
|
Gas: 100000,
|
|
GasPrice: big.NewInt(0),
|
|
Value: nil,
|
|
Data: common.Hex2Bytes("50f6fe34"),
|
|
}, 0, errors.New("gas required exceeds allowance (100000)")},
|
|
|
|
{"Assert", ethereum.CallMsg{
|
|
From: addr,
|
|
To: &contractAddr,
|
|
Gas: 100000,
|
|
GasPrice: big.NewInt(0),
|
|
Value: nil,
|
|
Data: common.Hex2Bytes("b9b046f9"),
|
|
}, 0, errors.New("always failing transaction (invalid opcode: opcode 0xfe not defined)")},
|
|
|
|
{"Valid", ethereum.CallMsg{
|
|
From: addr,
|
|
To: &contractAddr,
|
|
Gas: 100000,
|
|
GasPrice: big.NewInt(0),
|
|
Value: nil,
|
|
Data: common.Hex2Bytes("e09fface"),
|
|
}, 21275, nil},
|
|
}
|
|
for _, c := range cases {
|
|
got, err := sim.EstimateGas(context.Background(), c.message)
|
|
if c.expectError != nil {
|
|
if err == nil {
|
|
t.Fatalf("Expect error, got nil")
|
|
}
|
|
if c.expectError.Error() != err.Error() {
|
|
t.Fatalf("Expect error, want %v, got %v", c.expectError, err)
|
|
}
|
|
continue
|
|
}
|
|
if got != c.expect {
|
|
t.Fatalf("Gas estimation mismatch, want %d, got %d", c.expect, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSimulatedBackend_HeaderByHash(t *testing.T) {
|
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
|
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{
|
|
testAddr: {Balance: big.NewInt(10000000000)},
|
|
}, 10000000,
|
|
)
|
|
defer sim.Close()
|
|
bgCtx := context.Background()
|
|
|
|
header, err := sim.HeaderByNumber(bgCtx, nil)
|
|
if err != nil {
|
|
t.Errorf("could not get recent block: %v", err)
|
|
}
|
|
headerByHash, err := sim.HeaderByHash(bgCtx, header.Hash())
|
|
if err != nil {
|
|
t.Errorf("could not get recent block: %v", err)
|
|
}
|
|
|
|
if header.Hash() != headerByHash.Hash() {
|
|
t.Errorf("did not get expected block")
|
|
}
|
|
}
|
|
|
|
func TestSimulatedBackend_HeaderByNumber(t *testing.T) {
|
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
|
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{
|
|
testAddr: {Balance: big.NewInt(10000000000)},
|
|
}, 10000000,
|
|
)
|
|
defer sim.Close()
|
|
bgCtx := context.Background()
|
|
|
|
latestBlockHeader, err := sim.HeaderByNumber(bgCtx, nil)
|
|
if err != nil {
|
|
t.Errorf("could not get header for tip of chain: %v", err)
|
|
}
|
|
if latestBlockHeader == nil {
|
|
t.Errorf("received a nil block header")
|
|
}
|
|
if latestBlockHeader.Number.Uint64() != uint64(0) {
|
|
t.Errorf("expected block header number 0, instead got %v", latestBlockHeader.Number.Uint64())
|
|
}
|
|
|
|
sim.Commit()
|
|
|
|
latestBlockHeader, err = sim.HeaderByNumber(bgCtx, nil)
|
|
if err != nil {
|
|
t.Errorf("could not get header for blockheight of 1: %v", err)
|
|
}
|
|
|
|
blockHeader, err := sim.HeaderByNumber(bgCtx, big.NewInt(1))
|
|
if err != nil {
|
|
t.Errorf("could not get header for blockheight of 1: %v", err)
|
|
}
|
|
|
|
if blockHeader.Hash() != latestBlockHeader.Hash() {
|
|
t.Errorf("block header and latest block header are not the same")
|
|
}
|
|
if blockHeader.Number.Int64() != int64(1) {
|
|
t.Errorf("did not get blockheader for block 1. instead got block %v", blockHeader.Number.Int64())
|
|
}
|
|
|
|
block, err := sim.BlockByNumber(bgCtx, big.NewInt(1))
|
|
if err != nil {
|
|
t.Errorf("could not get block for blockheight of 1: %v", err)
|
|
}
|
|
|
|
if block.Hash() != blockHeader.Hash() {
|
|
t.Errorf("block hash and block header hash do not match. expected %v, got %v", block.Hash(), blockHeader.Hash())
|
|
}
|
|
}
|
|
|
|
func TestSimulatedBackend_TransactionCount(t *testing.T) {
|
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
|
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{
|
|
testAddr: {Balance: big.NewInt(10000000000)},
|
|
}, 10000000,
|
|
)
|
|
defer sim.Close()
|
|
bgCtx := context.Background()
|
|
currentBlock, err := sim.BlockByNumber(bgCtx, nil)
|
|
if err != nil || currentBlock == nil {
|
|
t.Error("could not get current block")
|
|
}
|
|
|
|
count, err := sim.TransactionCount(bgCtx, currentBlock.Hash())
|
|
if err != nil {
|
|
t.Error("could not get current block's transaction count")
|
|
}
|
|
|
|
if count != 0 {
|
|
t.Errorf("expected transaction count of %v does not match actual count of %v", 0, count)
|
|
}
|
|
|
|
// create a signed transaction to send
|
|
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
|
|
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
|
if err != nil {
|
|
t.Errorf("could not sign tx: %v", err)
|
|
}
|
|
|
|
// send tx to simulated backend
|
|
err = sim.SendTransaction(bgCtx, signedTx)
|
|
if err != nil {
|
|
t.Errorf("could not add tx to pending block: %v", err)
|
|
}
|
|
|
|
sim.Commit()
|
|
|
|
lastBlock, err := sim.BlockByNumber(bgCtx, nil)
|
|
if err != nil {
|
|
t.Errorf("could not get header for tip of chain: %v", err)
|
|
}
|
|
|
|
count, err = sim.TransactionCount(bgCtx, lastBlock.Hash())
|
|
if err != nil {
|
|
t.Error("could not get current block's transaction count")
|
|
}
|
|
|
|
if count != 1 {
|
|
t.Errorf("expected transaction count of %v does not match actual count of %v", 1, count)
|
|
}
|
|
}
|
|
|
|
func TestSimulatedBackend_TransactionInBlock(t *testing.T) {
|
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
|
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{
|
|
testAddr: {Balance: big.NewInt(10000000000)},
|
|
}, 10000000,
|
|
)
|
|
defer sim.Close()
|
|
bgCtx := context.Background()
|
|
|
|
transaction, err := sim.TransactionInBlock(bgCtx, sim.pendingBlock.Hash(), uint(0))
|
|
if err == nil && err != errTransactionDoesNotExist {
|
|
t.Errorf("expected a transaction does not exist error to be received but received %v", err)
|
|
}
|
|
if transaction != nil {
|
|
t.Errorf("expected transaction to be nil but received %v", transaction)
|
|
}
|
|
|
|
// expect pending nonce to be 0 since account has not been used
|
|
pendingNonce, err := sim.PendingNonceAt(bgCtx, testAddr)
|
|
if err != nil {
|
|
t.Errorf("did not get the pending nonce: %v", err)
|
|
}
|
|
|
|
if pendingNonce != uint64(0) {
|
|
t.Errorf("expected pending nonce of 0 got %v", pendingNonce)
|
|
}
|
|
|
|
// create a signed transaction to send
|
|
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
|
|
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
|
if err != nil {
|
|
t.Errorf("could not sign tx: %v", err)
|
|
}
|
|
|
|
// send tx to simulated backend
|
|
err = sim.SendTransaction(bgCtx, signedTx)
|
|
if err != nil {
|
|
t.Errorf("could not add tx to pending block: %v", err)
|
|
}
|
|
|
|
sim.Commit()
|
|
|
|
lastBlock, err := sim.BlockByNumber(bgCtx, nil)
|
|
if err != nil {
|
|
t.Errorf("could not get header for tip of chain: %v", err)
|
|
}
|
|
|
|
transaction, err = sim.TransactionInBlock(bgCtx, lastBlock.Hash(), uint(1))
|
|
if err == nil && err != errTransactionDoesNotExist {
|
|
t.Errorf("expected a transaction does not exist error to be received but received %v", err)
|
|
}
|
|
if transaction != nil {
|
|
t.Errorf("expected transaction to be nil but received %v", transaction)
|
|
}
|
|
|
|
transaction, err = sim.TransactionInBlock(bgCtx, lastBlock.Hash(), uint(0))
|
|
if err != nil {
|
|
t.Errorf("could not get transaction in the lastest block with hash %v: %v", lastBlock.Hash().String(), err)
|
|
}
|
|
|
|
if signedTx.Hash().String() != transaction.Hash().String() {
|
|
t.Errorf("received transaction that did not match the sent transaction. expected hash %v, got hash %v", signedTx.Hash().String(), transaction.Hash().String())
|
|
}
|
|
}
|
|
|
|
func TestSimulatedBackend_PendingNonceAt(t *testing.T) {
|
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
|
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{
|
|
testAddr: {Balance: big.NewInt(10000000000)},
|
|
}, 10000000,
|
|
)
|
|
defer sim.Close()
|
|
bgCtx := context.Background()
|
|
|
|
// expect pending nonce to be 0 since account has not been used
|
|
pendingNonce, err := sim.PendingNonceAt(bgCtx, testAddr)
|
|
if err != nil {
|
|
t.Errorf("did not get the pending nonce: %v", err)
|
|
}
|
|
|
|
if pendingNonce != uint64(0) {
|
|
t.Errorf("expected pending nonce of 0 got %v", pendingNonce)
|
|
}
|
|
|
|
// create a signed transaction to send
|
|
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
|
|
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
|
if err != nil {
|
|
t.Errorf("could not sign tx: %v", err)
|
|
}
|
|
|
|
// send tx to simulated backend
|
|
err = sim.SendTransaction(bgCtx, signedTx)
|
|
if err != nil {
|
|
t.Errorf("could not add tx to pending block: %v", err)
|
|
}
|
|
|
|
// expect pending nonce to be 1 since account has submitted one transaction
|
|
pendingNonce, err = sim.PendingNonceAt(bgCtx, testAddr)
|
|
if err != nil {
|
|
t.Errorf("did not get the pending nonce: %v", err)
|
|
}
|
|
|
|
if pendingNonce != uint64(1) {
|
|
t.Errorf("expected pending nonce of 1 got %v", pendingNonce)
|
|
}
|
|
|
|
// make a new transaction with a nonce of 1
|
|
tx = types.NewTransaction(uint64(1), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
|
|
signedTx, err = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
|
if err != nil {
|
|
t.Errorf("could not sign tx: %v", err)
|
|
}
|
|
err = sim.SendTransaction(bgCtx, signedTx)
|
|
if err != nil {
|
|
t.Errorf("could not send tx: %v", err)
|
|
}
|
|
|
|
// expect pending nonce to be 2 since account now has two transactions
|
|
pendingNonce, err = sim.PendingNonceAt(bgCtx, testAddr)
|
|
if err != nil {
|
|
t.Errorf("did not get the pending nonce: %v", err)
|
|
}
|
|
|
|
if pendingNonce != uint64(2) {
|
|
t.Errorf("expected pending nonce of 2 got %v", pendingNonce)
|
|
}
|
|
}
|
|
|
|
func TestSimulatedBackend_TransactionReceipt(t *testing.T) {
|
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
|
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{
|
|
testAddr: {Balance: big.NewInt(10000000000)},
|
|
}, 10000000,
|
|
)
|
|
defer sim.Close()
|
|
bgCtx := context.Background()
|
|
|
|
// create a signed transaction to send
|
|
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
|
|
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
|
if err != nil {
|
|
t.Errorf("could not sign tx: %v", err)
|
|
}
|
|
|
|
// send tx to simulated backend
|
|
err = sim.SendTransaction(bgCtx, signedTx)
|
|
if err != nil {
|
|
t.Errorf("could not add tx to pending block: %v", err)
|
|
}
|
|
sim.Commit()
|
|
|
|
receipt, err := sim.TransactionReceipt(bgCtx, signedTx.Hash())
|
|
if err != nil {
|
|
t.Errorf("could not get transaction receipt: %v", err)
|
|
}
|
|
|
|
if receipt.ContractAddress != testAddr && receipt.TxHash != signedTx.Hash() {
|
|
t.Errorf("received receipt is not correct: %v", receipt)
|
|
}
|
|
}
|
|
|
|
func TestSimulatedBackend_SuggestGasPrice(t *testing.T) {
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{},
|
|
10000000,
|
|
)
|
|
defer sim.Close()
|
|
bgCtx := context.Background()
|
|
gasPrice, err := sim.SuggestGasPrice(bgCtx)
|
|
if err != nil {
|
|
t.Errorf("could not get gas price: %v", err)
|
|
}
|
|
if gasPrice.Uint64() != uint64(1) {
|
|
t.Errorf("gas price was not expected value of 1. actual: %v", gasPrice.Uint64())
|
|
}
|
|
}
|
|
|
|
func TestSimulatedBackend_PendingCodeAt(t *testing.T) {
|
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{
|
|
testAddr: {Balance: big.NewInt(10000000000)},
|
|
},
|
|
10000000,
|
|
)
|
|
defer sim.Close()
|
|
bgCtx := context.Background()
|
|
code, err := sim.CodeAt(bgCtx, testAddr, nil)
|
|
if err != nil {
|
|
t.Errorf("could not get code at test addr: %v", err)
|
|
}
|
|
if len(code) != 0 {
|
|
t.Errorf("got code for account that does not have contract code")
|
|
}
|
|
|
|
parsed, err := abi.JSON(strings.NewReader(abiJSON))
|
|
if err != nil {
|
|
t.Errorf("could not get code at test addr: %v", err)
|
|
}
|
|
auth := bind.NewKeyedTransactor(testKey)
|
|
contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
|
|
if err != nil {
|
|
t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
|
|
}
|
|
|
|
code, err = sim.PendingCodeAt(bgCtx, contractAddr)
|
|
if err != nil {
|
|
t.Errorf("could not get code at test addr: %v", err)
|
|
}
|
|
if len(code) == 0 {
|
|
t.Errorf("did not get code for account that has contract code")
|
|
}
|
|
// ensure code received equals code deployed
|
|
if !bytes.Equal(code, common.FromHex(deployedCode)) {
|
|
t.Errorf("code received did not match expected deployed code:\n expected %v\n actual %v", common.FromHex(deployedCode), code)
|
|
}
|
|
}
|
|
|
|
func TestSimulatedBackend_CodeAt(t *testing.T) {
|
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{
|
|
testAddr: {Balance: big.NewInt(10000000000)},
|
|
},
|
|
10000000,
|
|
)
|
|
defer sim.Close()
|
|
bgCtx := context.Background()
|
|
code, err := sim.CodeAt(bgCtx, testAddr, nil)
|
|
if err != nil {
|
|
t.Errorf("could not get code at test addr: %v", err)
|
|
}
|
|
if len(code) != 0 {
|
|
t.Errorf("got code for account that does not have contract code")
|
|
}
|
|
|
|
parsed, err := abi.JSON(strings.NewReader(abiJSON))
|
|
if err != nil {
|
|
t.Errorf("could not get code at test addr: %v", err)
|
|
}
|
|
auth := bind.NewKeyedTransactor(testKey)
|
|
contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
|
|
if err != nil {
|
|
t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
|
|
}
|
|
|
|
sim.Commit()
|
|
code, err = sim.CodeAt(bgCtx, contractAddr, nil)
|
|
if err != nil {
|
|
t.Errorf("could not get code at test addr: %v", err)
|
|
}
|
|
if len(code) == 0 {
|
|
t.Errorf("did not get code for account that has contract code")
|
|
}
|
|
// ensure code received equals code deployed
|
|
if !bytes.Equal(code, common.FromHex(deployedCode)) {
|
|
t.Errorf("code received did not match expected deployed code:\n expected %v\n actual %v", common.FromHex(deployedCode), code)
|
|
}
|
|
}
|
|
|
|
// When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt:
|
|
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
|
|
func TestSimulatedBackend_PendingAndCallContract(t *testing.T) {
|
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
|
sim := NewSimulatedBackend(
|
|
core.GenesisAlloc{
|
|
testAddr: {Balance: big.NewInt(10000000000)},
|
|
},
|
|
10000000,
|
|
)
|
|
defer sim.Close()
|
|
bgCtx := context.Background()
|
|
|
|
parsed, err := abi.JSON(strings.NewReader(abiJSON))
|
|
if err != nil {
|
|
t.Errorf("could not get code at test addr: %v", err)
|
|
}
|
|
contractAuth := bind.NewKeyedTransactor(testKey)
|
|
addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(abiBin), sim)
|
|
if err != nil {
|
|
t.Errorf("could not deploy contract: %v", err)
|
|
}
|
|
|
|
input, err := parsed.Pack("receive", []byte("X"))
|
|
if err != nil {
|
|
t.Errorf("could pack receive function on contract: %v", err)
|
|
}
|
|
|
|
// make sure you can call the contract in pending state
|
|
res, err := sim.PendingCallContract(bgCtx, ethereum.CallMsg{
|
|
From: testAddr,
|
|
To: &addr,
|
|
Data: input,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("could not call receive method on contract: %v", err)
|
|
}
|
|
if len(res) == 0 {
|
|
t.Errorf("result of contract call was empty: %v", res)
|
|
}
|
|
|
|
// while comparing against the byte array is more exact, also compare against the human readable string for readability
|
|
if !bytes.Equal(res, expectedReturn) || !strings.Contains(string(res), "hello world") {
|
|
t.Errorf("response from calling contract was expected to be 'hello world' instead received %v", string(res))
|
|
}
|
|
|
|
sim.Commit()
|
|
|
|
// make sure you can call the contract
|
|
res, err = sim.CallContract(bgCtx, ethereum.CallMsg{
|
|
From: testAddr,
|
|
To: &addr,
|
|
Data: input,
|
|
}, nil)
|
|
if err != nil {
|
|
t.Errorf("could not call receive method on contract: %v", err)
|
|
}
|
|
if len(res) == 0 {
|
|
t.Errorf("result of contract call was empty: %v", res)
|
|
}
|
|
|
|
if !bytes.Equal(res, expectedReturn) || !strings.Contains(string(res), "hello world") {
|
|
t.Errorf("response from calling contract was expected to be 'hello world' instead received %v", string(res))
|
|
}
|
|
}
|