forked from cerc-io/plugeth
commit
5fc032a9d1
@ -20,28 +20,52 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrNoCode is returned by call and transact operations for which the requested
|
var (
|
||||||
// recipient contract to operate on does not exist in the state db or does not
|
// ErrNoCode is returned by call and transact operations for which the requested
|
||||||
// have any code associated with it (i.e. suicided).
|
// recipient contract to operate on does not exist in the state db or does not
|
||||||
var ErrNoCode = errors.New("no contract code at given address")
|
// have any code associated with it (i.e. suicided).
|
||||||
|
ErrNoCode = errors.New("no contract code at given address")
|
||||||
|
|
||||||
|
// This error is raised when attempting to perform a pending state action
|
||||||
|
// on a backend that doesn't implement PendingContractCaller.
|
||||||
|
ErrNoPendingState = errors.New("backend does not support pending state")
|
||||||
|
|
||||||
|
// This error is returned by WaitDeployed if contract creation leaves an
|
||||||
|
// empty contract behind.
|
||||||
|
ErrNoCodeAfterDeploy = errors.New("no contract code after deployment")
|
||||||
|
)
|
||||||
|
|
||||||
// ContractCaller defines the methods needed to allow operating with contract on a read
|
// ContractCaller defines the methods needed to allow operating with contract on a read
|
||||||
// only basis.
|
// only basis.
|
||||||
type ContractCaller interface {
|
type ContractCaller interface {
|
||||||
// HasCode checks if the contract at the given address has any code associated
|
// CodeAt returns the code of the given account. This is needed to differentiate
|
||||||
// with it or not. This is needed to differentiate between contract internal
|
// between contract internal errors and the local chain being out of sync.
|
||||||
// errors and the local chain being out of sync.
|
CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error)
|
||||||
HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error)
|
// ContractCall executes an Ethereum contract call with the specified data as the
|
||||||
|
// input.
|
||||||
|
CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
// ContractCall executes an Ethereum contract call with the specified data as
|
// DeployBackend wraps the operations needed by WaitMined and WaitDeployed.
|
||||||
// the input. The pending flag requests execution against the pending block, not
|
type DeployBackend interface {
|
||||||
// the stable head of the chain.
|
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
|
||||||
ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error)
|
CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingContractCaller defines methods to perform contract calls on the pending state.
|
||||||
|
// Call will try to discover this interface when access to the pending state is requested.
|
||||||
|
// If the backend does not support the pending state, Call returns ErrNoPendingState.
|
||||||
|
type PendingContractCaller interface {
|
||||||
|
// PendingCodeAt returns the code of the given account in the pending state.
|
||||||
|
PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error)
|
||||||
|
// PendingCallContract executes an Ethereum contract call against the pending state.
|
||||||
|
PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractTransactor defines the methods needed to allow operating with contract
|
// ContractTransactor defines the methods needed to allow operating with contract
|
||||||
@ -49,64 +73,25 @@ type ContractCaller interface {
|
|||||||
// used when the user does not provide some needed values, but rather leaves it up
|
// used when the user does not provide some needed values, but rather leaves it up
|
||||||
// to the transactor to decide.
|
// to the transactor to decide.
|
||||||
type ContractTransactor interface {
|
type ContractTransactor interface {
|
||||||
// PendingAccountNonce retrieves the current pending nonce associated with an
|
// PendingCodeAt returns the code of the given account in the pending state.
|
||||||
// account.
|
PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error)
|
||||||
PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error)
|
// PendingNonceAt retrieves the current pending nonce associated with an account.
|
||||||
|
PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
|
||||||
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
|
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
|
||||||
// execution of a transaction.
|
// execution of a transaction.
|
||||||
SuggestGasPrice(ctx context.Context) (*big.Int, error)
|
SuggestGasPrice(ctx context.Context) (*big.Int, error)
|
||||||
|
// EstimateGas tries to estimate the gas needed to execute a specific
|
||||||
// HasCode checks if the contract at the given address has any code associated
|
|
||||||
// with it or not. This is needed to differentiate between contract internal
|
|
||||||
// errors and the local chain being out of sync.
|
|
||||||
HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error)
|
|
||||||
|
|
||||||
// EstimateGasLimit tries to estimate the gas needed to execute a specific
|
|
||||||
// transaction based on the current pending state of the backend blockchain.
|
// transaction based on the current pending state of the backend blockchain.
|
||||||
// There is no guarantee that this is the true gas limit requirement as other
|
// There is no guarantee that this is the true gas limit requirement as other
|
||||||
// transactions may be added or removed by miners, but it should provide a basis
|
// transactions may be added or removed by miners, but it should provide a basis
|
||||||
// for setting a reasonable default.
|
// for setting a reasonable default.
|
||||||
EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error)
|
EstimateGas(ctx context.Context, call ethereum.CallMsg) (usedGas *big.Int, err error)
|
||||||
|
|
||||||
// SendTransaction injects the transaction into the pending pool for execution.
|
// SendTransaction injects the transaction into the pending pool for execution.
|
||||||
SendTransaction(ctx context.Context, tx *types.Transaction) error
|
SendTransaction(ctx context.Context, tx *types.Transaction) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractBackend defines the methods needed to allow operating with contract
|
// ContractBackend defines the methods needed to work with contracts on a read-write basis.
|
||||||
// on a read-write basis.
|
|
||||||
//
|
|
||||||
// This interface is essentially the union of ContractCaller and ContractTransactor
|
|
||||||
// but due to a bug in the Go compiler (https://github.com/golang/go/issues/6977),
|
|
||||||
// we cannot simply list it as the two interfaces. The other solution is to add a
|
|
||||||
// third interface containing the common methods, but that convolutes the user API
|
|
||||||
// as it introduces yet another parameter to require for initialization.
|
|
||||||
type ContractBackend interface {
|
type ContractBackend interface {
|
||||||
// HasCode checks if the contract at the given address has any code associated
|
ContractCaller
|
||||||
// with it or not. This is needed to differentiate between contract internal
|
ContractTransactor
|
||||||
// errors and the local chain being out of sync.
|
|
||||||
HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error)
|
|
||||||
|
|
||||||
// ContractCall executes an Ethereum contract call with the specified data as
|
|
||||||
// the input. The pending flag requests execution against the pending block, not
|
|
||||||
// the stable head of the chain.
|
|
||||||
ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error)
|
|
||||||
|
|
||||||
// PendingAccountNonce retrieves the current pending nonce associated with an
|
|
||||||
// account.
|
|
||||||
PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error)
|
|
||||||
|
|
||||||
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
|
|
||||||
// execution of a transaction.
|
|
||||||
SuggestGasPrice(ctx context.Context) (*big.Int, error)
|
|
||||||
|
|
||||||
// EstimateGasLimit tries to estimate the gas needed to execute a specific
|
|
||||||
// transaction based on the current pending state of the backend blockchain.
|
|
||||||
// There is no guarantee that this is the true gas limit requirement as other
|
|
||||||
// transactions may be added or removed by miners, but it should provide a basis
|
|
||||||
// for setting a reasonable default.
|
|
||||||
EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error)
|
|
||||||
|
|
||||||
// SendTransaction injects the transaction into the pending pool for execution.
|
|
||||||
SendTransaction(ctx context.Context, tx *types.Transaction) error
|
|
||||||
}
|
}
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
// Copyright 2016 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 (
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This nil assignment ensures compile time that nilBackend implements bind.ContractBackend.
|
|
||||||
var _ bind.ContractBackend = (*nilBackend)(nil)
|
|
||||||
|
|
||||||
// nilBackend implements bind.ContractBackend, but panics on any method call.
|
|
||||||
// Its sole purpose is to support the binding tests to construct the generated
|
|
||||||
// wrappers without calling any methods on them.
|
|
||||||
type nilBackend struct{}
|
|
||||||
|
|
||||||
func (*nilBackend) ContractCall(context.Context, common.Address, []byte, bool) ([]byte, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
func (*nilBackend) EstimateGasLimit(context.Context, common.Address, *common.Address, *big.Int, []byte) (*big.Int, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
func (*nilBackend) HasCode(context.Context, common.Address, bool) (bool, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
func (*nilBackend) SuggestGasPrice(context.Context) (*big.Int, error) { panic("not implemented") }
|
|
||||||
func (*nilBackend) PendingAccountNonce(context.Context, common.Address) (uint64, error) {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
func (*nilBackend) SendTransaction(context.Context, *types.Transaction) error {
|
|
||||||
panic("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewNilBackend creates a new binding backend that can be used for instantiation
|
|
||||||
// but will panic on any invocation. Its sole purpose is to help testing.
|
|
||||||
func NewNilBackend() bind.ContractBackend {
|
|
||||||
return new(nilBackend)
|
|
||||||
}
|
|
@ -1,136 +0,0 @@
|
|||||||
// Copyright 2016 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 (
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This nil assignment ensures compile time that rpcBackend implements bind.ContractBackend.
|
|
||||||
var _ bind.ContractBackend = (*rpcBackend)(nil)
|
|
||||||
|
|
||||||
// rpcBackend implements bind.ContractBackend, and acts as the data provider to
|
|
||||||
// Ethereum contracts bound to Go structs. It uses an RPC connection to delegate
|
|
||||||
// all its functionality.
|
|
||||||
type rpcBackend struct {
|
|
||||||
client *rpc.Client // RPC client connection to interact with an API server
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRPCBackend creates a new binding backend to an RPC provider that can be
|
|
||||||
// used to interact with remote contracts.
|
|
||||||
func NewRPCBackend(client *rpc.Client) bind.ContractBackend {
|
|
||||||
return &rpcBackend{client: client}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasCode implements ContractVerifier.HasCode by retrieving any code associated
|
|
||||||
// with the contract from the remote node, and checking its size.
|
|
||||||
func (b *rpcBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) {
|
|
||||||
block := "latest"
|
|
||||||
if pending {
|
|
||||||
block = "pending"
|
|
||||||
}
|
|
||||||
var hex string
|
|
||||||
err := b.client.CallContext(ctx, &hex, "eth_getCode", contract, block)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return len(common.FromHex(hex)) > 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContractCall implements ContractCaller.ContractCall, delegating the execution of
|
|
||||||
// a contract call to the remote node, returning the reply to for local processing.
|
|
||||||
func (b *rpcBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) {
|
|
||||||
args := struct {
|
|
||||||
To common.Address `json:"to"`
|
|
||||||
Data string `json:"data"`
|
|
||||||
}{
|
|
||||||
To: contract,
|
|
||||||
Data: common.ToHex(data),
|
|
||||||
}
|
|
||||||
block := "latest"
|
|
||||||
if pending {
|
|
||||||
block = "pending"
|
|
||||||
}
|
|
||||||
var hex string
|
|
||||||
err := b.client.CallContext(ctx, &hex, "eth_call", args, block)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return common.FromHex(hex), nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// PendingAccountNonce implements ContractTransactor.PendingAccountNonce, delegating
|
|
||||||
// the current account nonce retrieval to the remote node.
|
|
||||||
func (b *rpcBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) {
|
|
||||||
var hex rpc.HexNumber
|
|
||||||
err := b.client.CallContext(ctx, &hex, "eth_getTransactionCount", account.Hex(), "pending")
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return hex.Uint64(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SuggestGasPrice implements ContractTransactor.SuggestGasPrice, delegating the
|
|
||||||
// gas price oracle request to the remote node.
|
|
||||||
func (b *rpcBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
|
||||||
var hex rpc.HexNumber
|
|
||||||
if err := b.client.CallContext(ctx, &hex, "eth_gasPrice"); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return (*big.Int)(&hex), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EstimateGasLimit implements ContractTransactor.EstimateGasLimit, delegating
|
|
||||||
// the gas estimation to the remote node.
|
|
||||||
func (b *rpcBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) {
|
|
||||||
args := struct {
|
|
||||||
From common.Address `json:"from"`
|
|
||||||
To *common.Address `json:"to"`
|
|
||||||
Value *rpc.HexNumber `json:"value"`
|
|
||||||
Data string `json:"data"`
|
|
||||||
}{
|
|
||||||
From: sender,
|
|
||||||
To: contract,
|
|
||||||
Data: common.ToHex(data),
|
|
||||||
Value: rpc.NewHexNumber(value),
|
|
||||||
}
|
|
||||||
// Execute the RPC call and retrieve the response
|
|
||||||
var hex rpc.HexNumber
|
|
||||||
err := b.client.CallContext(ctx, &hex, "eth_estimateGas", args)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return (*big.Int)(&hex), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendTransaction implements ContractTransactor.SendTransaction, delegating the
|
|
||||||
// raw transaction injection to the remote node.
|
|
||||||
func (b *rpcBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
|
||||||
data, err := rlp.EncodeToBytes(tx)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return b.client.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data))
|
|
||||||
}
|
|
@ -17,8 +17,12 @@
|
|||||||
package backends
|
package backends
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
@ -36,12 +40,15 @@ var chainConfig = &core.ChainConfig{HomesteadBlock: big.NewInt(0)}
|
|||||||
// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
|
// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
|
||||||
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
|
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
|
||||||
|
|
||||||
|
var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block")
|
||||||
|
|
||||||
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
|
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
|
||||||
// the background. Its main purpose is to allow easily testing contract bindings.
|
// the background. Its main purpose is to allow easily testing contract bindings.
|
||||||
type SimulatedBackend struct {
|
type SimulatedBackend struct {
|
||||||
database ethdb.Database // In memory database to store our testing data
|
database ethdb.Database // In memory database to store our testing data
|
||||||
blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
|
blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
pendingBlock *types.Block // Currently pending block that will be imported on request
|
pendingBlock *types.Block // Currently pending block that will be imported on request
|
||||||
pendingState *state.StateDB // Currently pending state that will be the active on on request
|
pendingState *state.StateDB // Currently pending state that will be the active on on request
|
||||||
}
|
}
|
||||||
@ -52,85 +59,133 @@ func NewSimulatedBackend(accounts ...core.GenesisAccount) *SimulatedBackend {
|
|||||||
database, _ := ethdb.NewMemDatabase()
|
database, _ := ethdb.NewMemDatabase()
|
||||||
core.WriteGenesisBlockForTesting(database, accounts...)
|
core.WriteGenesisBlockForTesting(database, accounts...)
|
||||||
blockchain, _ := core.NewBlockChain(database, chainConfig, new(core.FakePow), new(event.TypeMux))
|
blockchain, _ := core.NewBlockChain(database, chainConfig, new(core.FakePow), new(event.TypeMux))
|
||||||
|
backend := &SimulatedBackend{database: database, blockchain: blockchain}
|
||||||
backend := &SimulatedBackend{
|
backend.rollback()
|
||||||
database: database,
|
|
||||||
blockchain: blockchain,
|
|
||||||
}
|
|
||||||
backend.Rollback()
|
|
||||||
|
|
||||||
return backend
|
return backend
|
||||||
}
|
}
|
||||||
|
|
||||||
// Commit imports all the pending transactions as a single block and starts a
|
// Commit imports all the pending transactions as a single block and starts a
|
||||||
// fresh new state.
|
// fresh new state.
|
||||||
func (b *SimulatedBackend) Commit() {
|
func (b *SimulatedBackend) Commit() {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil {
|
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
|
panic(err) // This cannot happen unless the simulator is wrong, fail in that case
|
||||||
}
|
}
|
||||||
b.Rollback()
|
b.rollback()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rollback aborts all pending transactions, reverting to the last committed state.
|
// Rollback aborts all pending transactions, reverting to the last committed state.
|
||||||
func (b *SimulatedBackend) Rollback() {
|
func (b *SimulatedBackend) Rollback() {
|
||||||
blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {})
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
b.rollback()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *SimulatedBackend) rollback() {
|
||||||
|
blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {})
|
||||||
b.pendingBlock = blocks[0]
|
b.pendingBlock = blocks[0]
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasCode implements ContractVerifier.HasCode, checking whether there is any
|
// CodeAt returns the code associated with a certain account in the blockchain.
|
||||||
// code associated with a certain account in the blockchain.
|
func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||||
func (b *SimulatedBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) {
|
b.mu.Lock()
|
||||||
if pending {
|
defer b.mu.Unlock()
|
||||||
return len(b.pendingState.GetCode(contract)) > 0, nil
|
|
||||||
|
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
||||||
|
return nil, errBlockNumberUnsupported
|
||||||
}
|
}
|
||||||
statedb, _ := b.blockchain.State()
|
statedb, _ := b.blockchain.State()
|
||||||
return len(statedb.GetCode(contract)) > 0, nil
|
return statedb.GetCode(contract), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractCall implements ContractCaller.ContractCall, executing the specified
|
// BalanceAt returns the wei balance of a certain account in the blockchain.
|
||||||
// contract with the given input data.
|
func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||||
func (b *SimulatedBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) {
|
b.mu.Lock()
|
||||||
// Create a copy of the current state db to screw around with
|
defer b.mu.Unlock()
|
||||||
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)
|
|
||||||
|
|
||||||
// Assemble the call invocation to measure the gas usage
|
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
||||||
msg := callmsg{
|
return nil, errBlockNumberUnsupported
|
||||||
from: from,
|
|
||||||
to: &contract,
|
|
||||||
gasPrice: new(big.Int),
|
|
||||||
gasLimit: common.MaxBig,
|
|
||||||
value: new(big.Int),
|
|
||||||
data: data,
|
|
||||||
}
|
}
|
||||||
// Execute the call and return
|
statedb, _ := b.blockchain.State()
|
||||||
vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
|
return statedb.GetBalance(contract), nil
|
||||||
gaspool := new(core.GasPool).AddGas(common.MaxBig)
|
|
||||||
|
|
||||||
out, _, err := core.ApplyMessage(vmenv, msg, gaspool)
|
|
||||||
return out, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingAccountNonce implements ContractTransactor.PendingAccountNonce, retrieving
|
// 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()
|
||||||
|
|
||||||
|
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
||||||
|
return 0, errBlockNumberUnsupported
|
||||||
|
}
|
||||||
|
statedb, _ := b.blockchain.State()
|
||||||
|
return statedb.GetNonce(contract), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// StorageAt returns the value of key in the storage of an account in the blockchain.
|
||||||
|
func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
||||||
|
return nil, errBlockNumberUnsupported
|
||||||
|
}
|
||||||
|
statedb, _ := b.blockchain.State()
|
||||||
|
if obj := statedb.GetStateObject(contract); obj != nil {
|
||||||
|
val := obj.GetState(key)
|
||||||
|
return val[:], nil
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransactionReceipt returns the receipt of a transaction.
|
||||||
|
func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
||||||
|
return core.GetReceipt(b.database, txHash), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingCodeAt returns the code associated with an account in the pending state.
|
||||||
|
func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
return b.pendingState.GetCode(contract), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallContract executes a contract call.
|
||||||
|
func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
||||||
|
return nil, errBlockNumberUnsupported
|
||||||
|
}
|
||||||
|
state, err := b.blockchain.State()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rval, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
|
||||||
|
return rval, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingCallContract executes a contract call on the pending state.
|
||||||
|
func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
rval, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState.Copy())
|
||||||
|
return rval, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
|
||||||
// the nonce currently pending for the account.
|
// 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) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
return b.pendingState.GetOrNewStateObject(account).Nonce(), nil
|
return b.pendingState.GetOrNewStateObject(account).Nonce(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,45 +195,55 @@ func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error
|
|||||||
return big.NewInt(1), nil
|
return big.NewInt(1), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// EstimateGasLimit implements ContractTransactor.EstimateGasLimit, executing the
|
// EstimateGas executes the requested code against the currently pending block/state and
|
||||||
// requested code against the currently pending block/state and returning the used
|
// returns the used amount of gas.
|
||||||
// gas.
|
func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (*big.Int, error) {
|
||||||
func (b *SimulatedBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) {
|
b.mu.Lock()
|
||||||
// Create a copy of the currently pending state db to screw around with
|
defer b.mu.Unlock()
|
||||||
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)
|
|
||||||
|
|
||||||
// Assemble the call invocation to measure the gas usage
|
_, gas, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState.Copy())
|
||||||
msg := callmsg{
|
|
||||||
from: from,
|
|
||||||
to: contract,
|
|
||||||
gasPrice: new(big.Int),
|
|
||||||
gasLimit: common.MaxBig,
|
|
||||||
value: value,
|
|
||||||
data: data,
|
|
||||||
}
|
|
||||||
// Execute the call and return
|
|
||||||
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
|
return gas, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendTransaction implements ContractTransactor.SendTransaction, delegating the raw
|
// callContract implemens common code between normal and pending contract calls.
|
||||||
// transaction injection to the remote node.
|
// 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)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
|
||||||
|
return ret, gasUsed, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
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) {
|
blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||||
for _, tx := range b.pendingBlock.Transactions() {
|
for _, tx := range b.pendingBlock.Transactions() {
|
||||||
block.AddTx(tx)
|
block.AddTx(tx)
|
||||||
@ -187,26 +252,20 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
|
|||||||
})
|
})
|
||||||
b.pendingBlock = blocks[0]
|
b.pendingBlock = blocks[0]
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// callmsg implements core.Message to allow passing it as a transaction simulator.
|
// callmsg implements core.Message to allow passing it as a transaction simulator.
|
||||||
type callmsg struct {
|
type callmsg struct {
|
||||||
from *state.StateObject
|
ethereum.CallMsg
|
||||||
to *common.Address
|
|
||||||
gasLimit *big.Int
|
|
||||||
gasPrice *big.Int
|
|
||||||
value *big.Int
|
|
||||||
data []byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m callmsg) From() (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.from.Address(), nil }
|
func (m callmsg) FromFrontier() (common.Address, error) { return m.CallMsg.From, nil }
|
||||||
func (m callmsg) Nonce() uint64 { return 0 }
|
func (m callmsg) Nonce() uint64 { return 0 }
|
||||||
func (m callmsg) CheckNonce() bool { return false }
|
func (m callmsg) CheckNonce() bool { return false }
|
||||||
func (m callmsg) To() *common.Address { return m.to }
|
func (m callmsg) To() *common.Address { return m.CallMsg.To }
|
||||||
func (m callmsg) GasPrice() *big.Int { return m.gasPrice }
|
func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
||||||
func (m callmsg) Gas() *big.Int { return m.gasLimit }
|
func (m callmsg) Gas() *big.Int { return m.CallMsg.Gas }
|
||||||
func (m callmsg) Value() *big.Int { return m.value }
|
func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
|
||||||
func (m callmsg) Data() []byte { return m.data }
|
func (m callmsg) Data() []byte { return m.CallMsg.Data }
|
||||||
|
@ -20,8 +20,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
@ -62,9 +62,6 @@ type BoundContract struct {
|
|||||||
abi abi.ABI // Reflect based ABI to access the correct Ethereum methods
|
abi abi.ABI // Reflect based ABI to access the correct Ethereum methods
|
||||||
caller ContractCaller // Read interface to interact with the blockchain
|
caller ContractCaller // Read interface to interact with the blockchain
|
||||||
transactor ContractTransactor // Write interface to interact with the blockchain
|
transactor ContractTransactor // Write interface to interact with the blockchain
|
||||||
|
|
||||||
latestHasCode uint32 // Cached verification that the latest state contains code for this contract
|
|
||||||
pendingHasCode uint32 // Cached verification that the pending state contains code for this contract
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBoundContract creates a low level contract interface through which calls
|
// NewBoundContract creates a low level contract interface through which calls
|
||||||
@ -105,25 +102,42 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string,
|
|||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = new(CallOpts)
|
opts = new(CallOpts)
|
||||||
}
|
}
|
||||||
// Make sure we have a contract to operate on, and bail out otherwise
|
|
||||||
if (opts.Pending && atomic.LoadUint32(&c.pendingHasCode) == 0) || (!opts.Pending && atomic.LoadUint32(&c.latestHasCode) == 0) {
|
|
||||||
if code, err := c.caller.HasCode(opts.Context, c.address, opts.Pending); err != nil {
|
|
||||||
return err
|
|
||||||
} else if !code {
|
|
||||||
return ErrNoCode
|
|
||||||
}
|
|
||||||
if opts.Pending {
|
|
||||||
atomic.StoreUint32(&c.pendingHasCode, 1)
|
|
||||||
} else {
|
|
||||||
atomic.StoreUint32(&c.latestHasCode, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Pack the input, call and unpack the results
|
// Pack the input, call and unpack the results
|
||||||
input, err := c.abi.Pack(method, params...)
|
input, err := c.abi.Pack(method, params...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
output, err := c.caller.ContractCall(opts.Context, c.address, input, opts.Pending)
|
var (
|
||||||
|
msg = ethereum.CallMsg{To: &c.address, Data: input}
|
||||||
|
ctx = ensureContext(opts.Context)
|
||||||
|
code []byte
|
||||||
|
output []byte
|
||||||
|
)
|
||||||
|
if opts.Pending {
|
||||||
|
pb, ok := c.caller.(PendingContractCaller)
|
||||||
|
if !ok {
|
||||||
|
return ErrNoPendingState
|
||||||
|
}
|
||||||
|
output, err = pb.PendingCallContract(ctx, msg)
|
||||||
|
if err == nil && len(output) == 0 {
|
||||||
|
// Make sure we have a contract to operate on, and bail out otherwise.
|
||||||
|
if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
|
||||||
|
return err
|
||||||
|
} else if len(code) == 0 {
|
||||||
|
return ErrNoCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output, err = c.caller.CallContract(ctx, msg, nil)
|
||||||
|
if err == nil && len(output) == 0 {
|
||||||
|
// Make sure we have a contract to operate on, and bail out otherwise.
|
||||||
|
if code, err = c.caller.CodeAt(ctx, c.address, nil); err != nil {
|
||||||
|
return err
|
||||||
|
} else if len(code) == 0 {
|
||||||
|
return ErrNoCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -158,7 +172,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
|||||||
}
|
}
|
||||||
nonce := uint64(0)
|
nonce := uint64(0)
|
||||||
if opts.Nonce == nil {
|
if opts.Nonce == nil {
|
||||||
nonce, err = c.transactor.PendingAccountNonce(opts.Context, opts.From)
|
nonce, err = c.transactor.PendingNonceAt(ensureContext(opts.Context), opts.From)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to retrieve account nonce: %v", err)
|
return nil, fmt.Errorf("failed to retrieve account nonce: %v", err)
|
||||||
}
|
}
|
||||||
@ -168,7 +182,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
|||||||
// Figure out the gas allowance and gas price values
|
// Figure out the gas allowance and gas price values
|
||||||
gasPrice := opts.GasPrice
|
gasPrice := opts.GasPrice
|
||||||
if gasPrice == nil {
|
if gasPrice == nil {
|
||||||
gasPrice, err = c.transactor.SuggestGasPrice(opts.Context)
|
gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to suggest gas price: %v", err)
|
return nil, fmt.Errorf("failed to suggest gas price: %v", err)
|
||||||
}
|
}
|
||||||
@ -176,18 +190,18 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
|||||||
gasLimit := opts.GasLimit
|
gasLimit := opts.GasLimit
|
||||||
if gasLimit == nil {
|
if gasLimit == nil {
|
||||||
// Gas estimation cannot succeed without code for method invocations
|
// Gas estimation cannot succeed without code for method invocations
|
||||||
if contract != nil && atomic.LoadUint32(&c.pendingHasCode) == 0 {
|
if contract != nil {
|
||||||
if code, err := c.transactor.HasCode(opts.Context, c.address, true); err != nil {
|
if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if !code {
|
} else if len(code) == 0 {
|
||||||
return nil, ErrNoCode
|
return nil, ErrNoCode
|
||||||
}
|
}
|
||||||
atomic.StoreUint32(&c.pendingHasCode, 1)
|
|
||||||
}
|
}
|
||||||
// If the contract surely has code (or code is not needed), estimate the transaction
|
// If the contract surely has code (or code is not needed), estimate the transaction
|
||||||
gasLimit, err = c.transactor.EstimateGasLimit(opts.Context, opts.From, contract, value, input)
|
msg := ethereum.CallMsg{From: opts.From, To: contract, Value: value, Data: input}
|
||||||
|
gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to exstimate gas needed: %v", err)
|
return nil, fmt.Errorf("failed to estimate gas needed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Create the transaction, sign it and schedule it for execution
|
// Create the transaction, sign it and schedule it for execution
|
||||||
@ -204,8 +218,15 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := c.transactor.SendTransaction(opts.Context, signedTx); err != nil {
|
if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return signedTx, nil
|
return signedTx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ensureContext(ctx context.Context) context.Context {
|
||||||
|
if ctx == nil {
|
||||||
|
return context.TODO()
|
||||||
|
}
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -127,7 +127,7 @@ package {{.Package}}
|
|||||||
|
|
||||||
// New{{.Type}} creates a new instance of {{.Type}}, bound to a specific deployed contract.
|
// New{{.Type}} creates a new instance of {{.Type}}, bound to a specific deployed contract.
|
||||||
func New{{.Type}}(address common.Address, backend bind.ContractBackend) (*{{.Type}}, error) {
|
func New{{.Type}}(address common.Address, backend bind.ContractBackend) (*{{.Type}}, error) {
|
||||||
contract, err := bind{{.Type}}(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor))
|
contract, err := bind{{.Type}}(address, backend, backend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
76
accounts/abi/bind/util.go
Normal file
76
accounts/abi/bind/util.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// Copyright 2016 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 bind
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WaitMined waits for tx to be mined on the blockchain.
|
||||||
|
// It stops waiting when the context is canceled.
|
||||||
|
func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*types.Receipt, error) {
|
||||||
|
queryTicker := time.NewTicker(1 * time.Second)
|
||||||
|
defer queryTicker.Stop()
|
||||||
|
loghash := tx.Hash().Hex()[:8]
|
||||||
|
for {
|
||||||
|
receipt, err := b.TransactionReceipt(ctx, tx.Hash())
|
||||||
|
if receipt != nil {
|
||||||
|
return receipt, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
glog.V(logger.Detail).Infof("tx %x error: %v", loghash, err)
|
||||||
|
} else {
|
||||||
|
glog.V(logger.Detail).Infof("tx %x not yet mined...", loghash)
|
||||||
|
}
|
||||||
|
// Wait for the next round.
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case <-queryTicker.C:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WaitDeployed waits for a contract deployment transaction and returns the on-chain
|
||||||
|
// contract address when it is mined. It stops waiting when ctx is canceled.
|
||||||
|
func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (common.Address, error) {
|
||||||
|
if tx.To() != nil {
|
||||||
|
return common.Address{}, fmt.Errorf("tx is not contract creation")
|
||||||
|
}
|
||||||
|
receipt, err := WaitMined(ctx, b, tx)
|
||||||
|
if err != nil {
|
||||||
|
return common.Address{}, err
|
||||||
|
}
|
||||||
|
if receipt.ContractAddress == (common.Address{}) {
|
||||||
|
return common.Address{}, fmt.Errorf("zero address")
|
||||||
|
}
|
||||||
|
// Check that code has indeed been deployed at the address.
|
||||||
|
// This matters on pre-Homestead chains: OOG in the constructor
|
||||||
|
// could leave an empty account behind.
|
||||||
|
code, err := b.CodeAt(ctx, receipt.ContractAddress, nil)
|
||||||
|
if err == nil && len(code) == 0 {
|
||||||
|
err = ErrNoCodeAfterDeploy
|
||||||
|
}
|
||||||
|
return receipt.ContractAddress, err
|
||||||
|
}
|
93
accounts/abi/bind/util_test.go
Normal file
93
accounts/abi/bind/util_test.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// Copyright 2016 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 bind_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
|
||||||
|
var waitDeployedTests = map[string]struct {
|
||||||
|
code string
|
||||||
|
gas *big.Int
|
||||||
|
wantAddress common.Address
|
||||||
|
wantErr error
|
||||||
|
}{
|
||||||
|
"successful deploy": {
|
||||||
|
code: `6060604052600a8060106000396000f360606040526008565b00`,
|
||||||
|
gas: big.NewInt(3000000),
|
||||||
|
wantAddress: common.HexToAddress("0x3a220f351252089d385b29beca14e27f204c296a"),
|
||||||
|
},
|
||||||
|
"empty code": {
|
||||||
|
code: ``,
|
||||||
|
gas: big.NewInt(300000),
|
||||||
|
wantErr: bind.ErrNoCodeAfterDeploy,
|
||||||
|
wantAddress: common.HexToAddress("0x3a220f351252089d385b29beca14e27f204c296a"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWaitDeployed(t *testing.T) {
|
||||||
|
for name, test := range waitDeployedTests {
|
||||||
|
backend := backends.NewSimulatedBackend(core.GenesisAccount{
|
||||||
|
Address: crypto.PubkeyToAddress(testKey.PublicKey),
|
||||||
|
Balance: big.NewInt(10000000000),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create the transaction.
|
||||||
|
tx := types.NewContractCreation(0, big.NewInt(0), test.gas, big.NewInt(1), common.FromHex(test.code))
|
||||||
|
tx, _ = tx.SignECDSA(testKey)
|
||||||
|
|
||||||
|
// Wait for it to get mined in the background.
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
address common.Address
|
||||||
|
mined = make(chan struct{})
|
||||||
|
ctx = context.Background()
|
||||||
|
)
|
||||||
|
go func() {
|
||||||
|
address, err = bind.WaitDeployed(ctx, backend, tx)
|
||||||
|
close(mined)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Send and mine the transaction.
|
||||||
|
backend.SendTransaction(ctx, tx)
|
||||||
|
backend.Commit()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-mined:
|
||||||
|
if err != test.wantErr {
|
||||||
|
t.Errorf("test %q: error mismatch: got %q, want %q", name, err, test.wantErr)
|
||||||
|
}
|
||||||
|
if address != test.wantAddress {
|
||||||
|
t.Errorf("test %q: unexpected contract address %s", name, address.Hex())
|
||||||
|
}
|
||||||
|
case <-time.After(2 * time.Second):
|
||||||
|
t.Errorf("test %q: timeout", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -18,9 +18,9 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
@ -33,25 +33,53 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
EmptyRootHash = DeriveSha(Transactions{})
|
||||||
|
EmptyUncleHash = CalcUncleHash(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errMissingHeaderMixDigest = errors.New("missing mixHash in JSON block header")
|
||||||
|
errMissingHeaderFields = errors.New("missing required JSON block header fields")
|
||||||
|
errBadNonceSize = errors.New("invalid block nonce size, want 8 bytes")
|
||||||
|
)
|
||||||
|
|
||||||
// A BlockNonce is a 64-bit hash which proves (combined with the
|
// A BlockNonce is a 64-bit hash which proves (combined with the
|
||||||
// mix-hash) that a sufficient amount of computation has been carried
|
// mix-hash) that a sufficient amount of computation has been carried
|
||||||
// out on a block.
|
// out on a block.
|
||||||
type BlockNonce [8]byte
|
type BlockNonce [8]byte
|
||||||
|
|
||||||
|
// EncodeNonce converts the given integer to a block nonce.
|
||||||
func EncodeNonce(i uint64) BlockNonce {
|
func EncodeNonce(i uint64) BlockNonce {
|
||||||
var n BlockNonce
|
var n BlockNonce
|
||||||
binary.BigEndian.PutUint64(n[:], i)
|
binary.BigEndian.PutUint64(n[:], i)
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uint64 returns the integer value of a block nonce.
|
||||||
func (n BlockNonce) Uint64() uint64 {
|
func (n BlockNonce) Uint64() uint64 {
|
||||||
return binary.BigEndian.Uint64(n[:])
|
return binary.BigEndian.Uint64(n[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler
|
||||||
func (n BlockNonce) MarshalJSON() ([]byte, error) {
|
func (n BlockNonce) MarshalJSON() ([]byte, error) {
|
||||||
return []byte(fmt.Sprintf(`"0x%x"`, n)), nil
|
return []byte(fmt.Sprintf(`"0x%x"`, n)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements json.Unmarshaler
|
||||||
|
func (n *BlockNonce) UnmarshalJSON(input []byte) error {
|
||||||
|
var b hexBytes
|
||||||
|
if err := b.UnmarshalJSON(input); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(b) != 8 {
|
||||||
|
return errBadNonceSize
|
||||||
|
}
|
||||||
|
copy((*n)[:], b)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Header represents Ethereum block headers.
|
||||||
type Header struct {
|
type Header struct {
|
||||||
ParentHash common.Hash // Hash to the previous block
|
ParentHash common.Hash // Hash to the previous block
|
||||||
UncleHash common.Hash // Uncles of this block
|
UncleHash common.Hash // Uncles of this block
|
||||||
@ -70,10 +98,31 @@ type Header struct {
|
|||||||
Nonce BlockNonce
|
Nonce BlockNonce
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type jsonHeader struct {
|
||||||
|
ParentHash *common.Hash `json:"parentHash"`
|
||||||
|
UncleHash *common.Hash `json:"sha3Uncles"`
|
||||||
|
Coinbase *common.Address `json:"miner"`
|
||||||
|
Root *common.Hash `json:"stateRoot"`
|
||||||
|
TxHash *common.Hash `json:"transactionsRoot"`
|
||||||
|
ReceiptHash *common.Hash `json:"receiptRoot"`
|
||||||
|
Bloom *Bloom `json:"logsBloom"`
|
||||||
|
Difficulty *hexBig `json:"difficulty"`
|
||||||
|
Number *hexBig `json:"number"`
|
||||||
|
GasLimit *hexBig `json:"gasLimit"`
|
||||||
|
GasUsed *hexBig `json:"gasUsed"`
|
||||||
|
Time *hexBig `json:"timestamp"`
|
||||||
|
Extra *hexBytes `json:"extraData"`
|
||||||
|
MixDigest *common.Hash `json:"mixHash"`
|
||||||
|
Nonce *BlockNonce `json:"nonce"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash returns the block hash of the header, which is simply the keccak256 hash of its
|
||||||
|
// RLP encoding.
|
||||||
func (h *Header) Hash() common.Hash {
|
func (h *Header) Hash() common.Hash {
|
||||||
return rlpHash(h)
|
return rlpHash(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HashNoNonce returns the hash which is used as input for the proof-of-work search.
|
||||||
func (h *Header) HashNoNonce() common.Hash {
|
func (h *Header) HashNoNonce() common.Hash {
|
||||||
return rlpHash([]interface{}{
|
return rlpHash([]interface{}{
|
||||||
h.ParentHash,
|
h.ParentHash,
|
||||||
@ -92,48 +141,63 @@ func (h *Header) HashNoNonce() common.Hash {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Header) UnmarshalJSON(data []byte) error {
|
// MarshalJSON encodes headers into the web3 RPC response block format.
|
||||||
var ext struct {
|
func (h *Header) MarshalJSON() ([]byte, error) {
|
||||||
ParentHash string
|
return json.Marshal(&jsonHeader{
|
||||||
Coinbase string
|
ParentHash: &h.ParentHash,
|
||||||
Difficulty string
|
UncleHash: &h.UncleHash,
|
||||||
GasLimit string
|
Coinbase: &h.Coinbase,
|
||||||
Time *big.Int
|
Root: &h.Root,
|
||||||
Extra string
|
TxHash: &h.TxHash,
|
||||||
}
|
ReceiptHash: &h.ReceiptHash,
|
||||||
dec := json.NewDecoder(bytes.NewReader(data))
|
Bloom: &h.Bloom,
|
||||||
if err := dec.Decode(&ext); err != nil {
|
Difficulty: (*hexBig)(h.Difficulty),
|
||||||
return err
|
Number: (*hexBig)(h.Number),
|
||||||
}
|
GasLimit: (*hexBig)(h.GasLimit),
|
||||||
|
GasUsed: (*hexBig)(h.GasUsed),
|
||||||
h.ParentHash = common.HexToHash(ext.ParentHash)
|
Time: (*hexBig)(h.Time),
|
||||||
h.Coinbase = common.HexToAddress(ext.Coinbase)
|
Extra: (*hexBytes)(&h.Extra),
|
||||||
h.Difficulty = common.String2Big(ext.Difficulty)
|
MixDigest: &h.MixDigest,
|
||||||
h.Time = ext.Time
|
Nonce: &h.Nonce,
|
||||||
h.Extra = []byte(ext.Extra)
|
})
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Header) MarshalJSON() ([]byte, error) {
|
// UnmarshalJSON decodes headers from the web3 RPC response block format.
|
||||||
fields := map[string]interface{}{
|
func (h *Header) UnmarshalJSON(input []byte) error {
|
||||||
"hash": h.Hash(),
|
var dec jsonHeader
|
||||||
"parentHash": h.ParentHash,
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
"number": fmt.Sprintf("%#x", h.Number),
|
return err
|
||||||
"nonce": h.Nonce,
|
|
||||||
"receiptRoot": h.ReceiptHash,
|
|
||||||
"logsBloom": h.Bloom,
|
|
||||||
"sha3Uncles": h.UncleHash,
|
|
||||||
"stateRoot": h.Root,
|
|
||||||
"miner": h.Coinbase,
|
|
||||||
"difficulty": fmt.Sprintf("%#x", h.Difficulty),
|
|
||||||
"extraData": fmt.Sprintf("0x%x", h.Extra),
|
|
||||||
"gasLimit": fmt.Sprintf("%#x", h.GasLimit),
|
|
||||||
"gasUsed": fmt.Sprintf("%#x", h.GasUsed),
|
|
||||||
"timestamp": fmt.Sprintf("%#x", h.Time),
|
|
||||||
"transactionsRoot": h.TxHash,
|
|
||||||
}
|
}
|
||||||
|
// Ensure that all fields are set. MixDigest is checked separately because
|
||||||
return json.Marshal(fields)
|
// it is a recent addition to the spec (as of August 2016) and older RPC server
|
||||||
|
// implementations might not provide it.
|
||||||
|
if dec.MixDigest == nil {
|
||||||
|
return errMissingHeaderMixDigest
|
||||||
|
}
|
||||||
|
if dec.ParentHash == nil || dec.UncleHash == nil || dec.Coinbase == nil ||
|
||||||
|
dec.Root == nil || dec.TxHash == nil || dec.ReceiptHash == nil ||
|
||||||
|
dec.Bloom == nil || dec.Difficulty == nil || dec.Number == nil ||
|
||||||
|
dec.GasLimit == nil || dec.GasUsed == nil || dec.Time == nil ||
|
||||||
|
dec.Extra == nil || dec.Nonce == nil {
|
||||||
|
return errMissingHeaderFields
|
||||||
|
}
|
||||||
|
// Assign all values.
|
||||||
|
h.ParentHash = *dec.ParentHash
|
||||||
|
h.UncleHash = *dec.UncleHash
|
||||||
|
h.Coinbase = *dec.Coinbase
|
||||||
|
h.Root = *dec.Root
|
||||||
|
h.TxHash = *dec.TxHash
|
||||||
|
h.ReceiptHash = *dec.ReceiptHash
|
||||||
|
h.Bloom = *dec.Bloom
|
||||||
|
h.Difficulty = (*big.Int)(dec.Difficulty)
|
||||||
|
h.Number = (*big.Int)(dec.Number)
|
||||||
|
h.GasLimit = (*big.Int)(dec.GasLimit)
|
||||||
|
h.GasUsed = (*big.Int)(dec.GasUsed)
|
||||||
|
h.Time = (*big.Int)(dec.Time)
|
||||||
|
h.Extra = *dec.Extra
|
||||||
|
h.MixDigest = *dec.MixDigest
|
||||||
|
h.Nonce = *dec.Nonce
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func rlpHash(x interface{}) (h common.Hash) {
|
func rlpHash(x interface{}) (h common.Hash) {
|
||||||
@ -150,6 +214,7 @@ type Body struct {
|
|||||||
Uncles []*Header
|
Uncles []*Header
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Block represents a block in the Ethereum blockchain.
|
||||||
type Block struct {
|
type Block struct {
|
||||||
header *Header
|
header *Header
|
||||||
uncles []*Header
|
uncles []*Header
|
||||||
@ -198,11 +263,6 @@ type storageblock struct {
|
|||||||
TD *big.Int
|
TD *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
EmptyRootHash = DeriveSha(Transactions{})
|
|
||||||
EmptyUncleHash = CalcUncleHash(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewBlock creates a new block. The input data is copied,
|
// NewBlock creates a new block. The input data is copied,
|
||||||
// changes to header and to the field values will not affect the
|
// changes to header and to the field values will not affect the
|
||||||
// block.
|
// block.
|
||||||
@ -275,23 +335,7 @@ func CopyHeader(h *Header) *Header {
|
|||||||
return &cpy
|
return &cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Block) ValidateFields() error {
|
// DecodeRLP decodes the Ethereum
|
||||||
if b.header == nil {
|
|
||||||
return fmt.Errorf("header is nil")
|
|
||||||
}
|
|
||||||
for i, transaction := range b.transactions {
|
|
||||||
if transaction == nil {
|
|
||||||
return fmt.Errorf("transaction %d is nil", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i, uncle := range b.uncles {
|
|
||||||
if uncle == nil {
|
|
||||||
return fmt.Errorf("uncle %d is nil", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *Block) DecodeRLP(s *rlp.Stream) error {
|
func (b *Block) DecodeRLP(s *rlp.Stream) error {
|
||||||
var eb extblock
|
var eb extblock
|
||||||
_, size, _ := s.Kind()
|
_, size, _ := s.Kind()
|
||||||
@ -303,6 +347,7 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EncodeRLP serializes b into the Ethereum RLP block format.
|
||||||
func (b *Block) EncodeRLP(w io.Writer) error {
|
func (b *Block) EncodeRLP(w io.Writer) error {
|
||||||
return rlp.Encode(w, extblock{
|
return rlp.Encode(w, extblock{
|
||||||
Header: b.header,
|
Header: b.header,
|
||||||
@ -322,6 +367,7 @@ func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: copies
|
// TODO: copies
|
||||||
|
|
||||||
func (b *Block) Uncles() []*Header { return b.uncles }
|
func (b *Block) Uncles() []*Header { return b.uncles }
|
||||||
func (b *Block) Transactions() Transactions { return b.transactions }
|
func (b *Block) Transactions() Transactions { return b.transactions }
|
||||||
|
|
||||||
@ -409,8 +455,8 @@ func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block {
|
|||||||
return block
|
return block
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement pow.Block
|
// Hash returns the keccak256 hash of b's header.
|
||||||
|
// The hash is computed on the first call and cached thereafter.
|
||||||
func (b *Block) Hash() common.Hash {
|
func (b *Block) Hash() common.Hash {
|
||||||
if hash := b.hash.Load(); hash != nil {
|
if hash := b.hash.Load(); hash != nil {
|
||||||
return hash.(common.Hash)
|
return hash.(common.Hash)
|
||||||
|
@ -31,28 +31,34 @@ type bytesBacked interface {
|
|||||||
|
|
||||||
const bloomLength = 256
|
const bloomLength = 256
|
||||||
|
|
||||||
|
// Bloom represents a 256 bit bloom filter.
|
||||||
type Bloom [bloomLength]byte
|
type Bloom [bloomLength]byte
|
||||||
|
|
||||||
|
// BytesToBloom converts a byte slice to a bloom filter.
|
||||||
|
// It panics if b is not of suitable size.
|
||||||
func BytesToBloom(b []byte) Bloom {
|
func BytesToBloom(b []byte) Bloom {
|
||||||
var bloom Bloom
|
var bloom Bloom
|
||||||
bloom.SetBytes(b)
|
bloom.SetBytes(b)
|
||||||
return bloom
|
return bloom
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetBytes sets the content of b to the given bytes.
|
||||||
|
// It panics if d is not of suitable size.
|
||||||
func (b *Bloom) SetBytes(d []byte) {
|
func (b *Bloom) SetBytes(d []byte) {
|
||||||
if len(b) < len(d) {
|
if len(b) < len(d) {
|
||||||
panic(fmt.Sprintf("bloom bytes too big %d %d", len(b), len(d)))
|
panic(fmt.Sprintf("bloom bytes too big %d %d", len(b), len(d)))
|
||||||
}
|
}
|
||||||
|
|
||||||
copy(b[bloomLength-len(d):], d)
|
copy(b[bloomLength-len(d):], d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add adds d to the filter. Future calls of Test(d) will return true.
|
||||||
func (b *Bloom) Add(d *big.Int) {
|
func (b *Bloom) Add(d *big.Int) {
|
||||||
bin := new(big.Int).SetBytes(b[:])
|
bin := new(big.Int).SetBytes(b[:])
|
||||||
bin.Or(bin, bloom9(d.Bytes()))
|
bin.Or(bin, bloom9(d.Bytes()))
|
||||||
b.SetBytes(bin.Bytes())
|
b.SetBytes(bin.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Big converts b to a big integer.
|
||||||
func (b Bloom) Big() *big.Int {
|
func (b Bloom) Big() *big.Int {
|
||||||
return common.Bytes2Big(b[:])
|
return common.Bytes2Big(b[:])
|
||||||
}
|
}
|
||||||
@ -69,8 +75,22 @@ func (b Bloom) TestBytes(test []byte) bool {
|
|||||||
return b.Test(common.BytesToBig(test))
|
return b.Test(common.BytesToBig(test))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON encodes b as a hex string with 0x prefix.
|
||||||
func (b Bloom) MarshalJSON() ([]byte, error) {
|
func (b Bloom) MarshalJSON() ([]byte, error) {
|
||||||
return []byte(fmt.Sprintf(`"%#x"`, b.Bytes())), nil
|
return []byte(fmt.Sprintf(`"%#x"`, b[:])), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON b as a hex string with 0x prefix.
|
||||||
|
func (b *Bloom) UnmarshalJSON(input []byte) error {
|
||||||
|
var dec hexBytes
|
||||||
|
if err := dec.UnmarshalJSON(input); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(dec) != bloomLength {
|
||||||
|
return fmt.Errorf("invalid bloom size, want %d bytes", bloomLength)
|
||||||
|
}
|
||||||
|
copy((*b)[:], dec)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateBloom(receipts Receipts) Bloom {
|
func CreateBloom(receipts Receipts) Bloom {
|
||||||
|
87
core/types/json.go
Normal file
87
core/types/json.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
// Copyright 2016 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 types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
// JSON unmarshaling utilities.
|
||||||
|
|
||||||
|
type hexBytes []byte
|
||||||
|
|
||||||
|
func (b *hexBytes) UnmarshalJSON(input []byte) error {
|
||||||
|
if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' {
|
||||||
|
return fmt.Errorf("cannot unmarshal non-string into hexBytes")
|
||||||
|
}
|
||||||
|
input = input[1 : len(input)-1]
|
||||||
|
if len(input) < 2 || input[0] != '0' || input[1] != 'x' {
|
||||||
|
return fmt.Errorf("missing 0x prefix in hexBytes input %q", input)
|
||||||
|
}
|
||||||
|
dec := make(hexBytes, (len(input)-2)/2)
|
||||||
|
if _, err := hex.Decode(dec, input[2:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*b = dec
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type hexBig big.Int
|
||||||
|
|
||||||
|
func (b *hexBig) UnmarshalJSON(input []byte) error {
|
||||||
|
raw, err := checkHexNumber(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dec, ok := new(big.Int).SetString(string(raw), 16)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid hex number")
|
||||||
|
}
|
||||||
|
*b = (hexBig)(*dec)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type hexUint64 uint64
|
||||||
|
|
||||||
|
func (b *hexUint64) UnmarshalJSON(input []byte) error {
|
||||||
|
raw, err := checkHexNumber(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = fmt.Sscanf(string(raw), "%x", b)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkHexNumber(input []byte) (raw []byte, err error) {
|
||||||
|
if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' {
|
||||||
|
return nil, fmt.Errorf("cannot unmarshal non-string into hex number")
|
||||||
|
}
|
||||||
|
input = input[1 : len(input)-1]
|
||||||
|
if len(input) < 2 || input[0] != '0' || input[1] != 'x' {
|
||||||
|
return nil, fmt.Errorf("missing 0x prefix in hex number input %q", input)
|
||||||
|
}
|
||||||
|
if len(input) == 2 {
|
||||||
|
return nil, fmt.Errorf("empty hex number")
|
||||||
|
}
|
||||||
|
raw = input[2:]
|
||||||
|
if len(raw)%2 != 0 {
|
||||||
|
raw = append([]byte{'0'}, raw...)
|
||||||
|
}
|
||||||
|
return raw, nil
|
||||||
|
}
|
136
core/types/json_test.go
Normal file
136
core/types/json_test.go
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
var unmarshalHeaderTests = map[string]struct {
|
||||||
|
input string
|
||||||
|
wantHash common.Hash
|
||||||
|
wantError error
|
||||||
|
}{
|
||||||
|
"block 0x1e2200": {
|
||||||
|
input: `{"difficulty":"0x311ca98cebfe","extraData":"0x7777772e62772e636f6d","gasLimit":"0x47db3d","gasUsed":"0x43760c","hash":"0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","mixHash":"0x1ccfddb506dac5afc09b6f92eb09a043ffc8e08f7592250af57b9c64c20f9b25","nonce":"0x670bd98c79585197","number":"0x1e2200","parentHash":"0xd3e13296d064e7344f20c57c57b67a022f6bf7741fa42428c2db77e91abdf1f8","receiptRoot":"0xeeab1776c1fafbe853a8ee0c1bafe2e775a1b6fdb6ff3e9f9410ddd4514889ff","sha3Uncles":"0x5fbfa4ec8b089678c53b6798cc0d9260ea40a529e06d5300aae35596262e0eb3","size":"0x57f","stateRoot":"0x62ad2007e4a3f31ea98e5d2fd150d894887bafde36eeac7331a60ae12053ec76","timestamp":"0x579b82f2","totalDifficulty":"0x24fe813c101d00f97","transactions":["0xb293408e85735bfc78b35aa89de8b48e49641e3d82e3d52ea2d44ec42a4e88cf","0x124acc383ff2da6faa0357829084dae64945221af6f6f09da1d11688b779f939","0xee090208b6051c442ccdf9ec19f66389e604d342a6d71144c7227ce995bef46f"],"transactionsRoot":"0xce0042dd9af0c1923dd7f58ca6faa156d39d4ef39fdb65c5bcd1d4b4720096db","uncles":["0x6818a31d1f204cf640c952082940b68b8db6d1b39ee71f7efe0e3629ed5d7eb3"]}`,
|
||||||
|
wantHash: common.HexToHash("0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48"),
|
||||||
|
},
|
||||||
|
"bad nonce": {
|
||||||
|
input: `{"difficulty":"0x311ca98cebfe","extraData":"0x7777772e62772e636f6d","gasLimit":"0x47db3d","gasUsed":"0x43760c","hash":"0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","mixHash":"0x1ccfddb506dac5afc09b6f92eb09a043ffc8e08f7592250af57b9c64c20f9b25","nonce":"0x670bd98c7958","number":"0x1e2200","parentHash":"0xd3e13296d064e7344f20c57c57b67a022f6bf7741fa42428c2db77e91abdf1f8","receiptRoot":"0xeeab1776c1fafbe853a8ee0c1bafe2e775a1b6fdb6ff3e9f9410ddd4514889ff","sha3Uncles":"0x5fbfa4ec8b089678c53b6798cc0d9260ea40a529e06d5300aae35596262e0eb3","size":"0x57f","stateRoot":"0x62ad2007e4a3f31ea98e5d2fd150d894887bafde36eeac7331a60ae12053ec76","timestamp":"0x579b82f2","totalDifficulty":"0x24fe813c101d00f97","transactions":["0xb293408e85735bfc78b35aa89de8b48e49641e3d82e3d52ea2d44ec42a4e88cf","0x124acc383ff2da6faa0357829084dae64945221af6f6f09da1d11688b779f939","0xee090208b6051c442ccdf9ec19f66389e604d342a6d71144c7227ce995bef46f"],"transactionsRoot":"0xce0042dd9af0c1923dd7f58ca6faa156d39d4ef39fdb65c5bcd1d4b4720096db","uncles":["0x6818a31d1f204cf640c952082940b68b8db6d1b39ee71f7efe0e3629ed5d7eb3"]}`,
|
||||||
|
wantError: errBadNonceSize,
|
||||||
|
},
|
||||||
|
"missing mixHash": {
|
||||||
|
input: `{"difficulty":"0x311ca98cebfe","extraData":"0x7777772e62772e636f6d","gasLimit":"0x47db3d","gasUsed":"0x43760c","hash":"0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","nonce":"0x670bd98c79585197","number":"0x1e2200","parentHash":"0xd3e13296d064e7344f20c57c57b67a022f6bf7741fa42428c2db77e91abdf1f8","receiptRoot":"0xeeab1776c1fafbe853a8ee0c1bafe2e775a1b6fdb6ff3e9f9410ddd4514889ff","sha3Uncles":"0x5fbfa4ec8b089678c53b6798cc0d9260ea40a529e06d5300aae35596262e0eb3","size":"0x57f","stateRoot":"0x62ad2007e4a3f31ea98e5d2fd150d894887bafde36eeac7331a60ae12053ec76","timestamp":"0x579b82f2","totalDifficulty":"0x24fe813c101d00f97","transactions":["0xb293408e85735bfc78b35aa89de8b48e49641e3d82e3d52ea2d44ec42a4e88cf","0x124acc383ff2da6faa0357829084dae64945221af6f6f09da1d11688b779f939","0xee090208b6051c442ccdf9ec19f66389e604d342a6d71144c7227ce995bef46f"],"transactionsRoot":"0xce0042dd9af0c1923dd7f58ca6faa156d39d4ef39fdb65c5bcd1d4b4720096db","uncles":["0x6818a31d1f204cf640c952082940b68b8db6d1b39ee71f7efe0e3629ed5d7eb3"]}`,
|
||||||
|
wantError: errMissingHeaderMixDigest,
|
||||||
|
},
|
||||||
|
"missing fields": {
|
||||||
|
input: `{"gasLimit":"0x47db3d","gasUsed":"0x43760c","hash":"0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","mixHash":"0x1ccfddb506dac5afc09b6f92eb09a043ffc8e08f7592250af57b9c64c20f9b25","nonce":"0x670bd98c79585197","number":"0x1e2200","parentHash":"0xd3e13296d064e7344f20c57c57b67a022f6bf7741fa42428c2db77e91abdf1f8","receiptRoot":"0xeeab1776c1fafbe853a8ee0c1bafe2e775a1b6fdb6ff3e9f9410ddd4514889ff","sha3Uncles":"0x5fbfa4ec8b089678c53b6798cc0d9260ea40a529e06d5300aae35596262e0eb3","size":"0x57f","stateRoot":"0x62ad2007e4a3f31ea98e5d2fd150d894887bafde36eeac7331a60ae12053ec76","timestamp":"0x579b82f2","totalDifficulty":"0x24fe813c101d00f97","transactions":["0xb293408e85735bfc78b35aa89de8b48e49641e3d82e3d52ea2d44ec42a4e88cf","0x124acc383ff2da6faa0357829084dae64945221af6f6f09da1d11688b779f939","0xee090208b6051c442ccdf9ec19f66389e604d342a6d71144c7227ce995bef46f"],"transactionsRoot":"0xce0042dd9af0c1923dd7f58ca6faa156d39d4ef39fdb65c5bcd1d4b4720096db","uncles":["0x6818a31d1f204cf640c952082940b68b8db6d1b39ee71f7efe0e3629ed5d7eb3"]}`,
|
||||||
|
wantError: errMissingHeaderFields,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalHeader(t *testing.T) {
|
||||||
|
for name, test := range unmarshalHeaderTests {
|
||||||
|
var head *Header
|
||||||
|
err := json.Unmarshal([]byte(test.input), &head)
|
||||||
|
if !checkError(t, name, err, test.wantError) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if head.Hash() != test.wantHash {
|
||||||
|
t.Errorf("test %q: got hash %x, want %x", name, head.Hash(), test.wantHash)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var unmarshalTransactionTests = map[string]struct {
|
||||||
|
input string
|
||||||
|
wantHash common.Hash
|
||||||
|
wantFrom common.Address
|
||||||
|
wantError error
|
||||||
|
}{
|
||||||
|
"value transfer": {
|
||||||
|
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","v":"0x1c","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
|
||||||
|
wantHash: common.HexToHash("0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9"),
|
||||||
|
wantFrom: common.HexToAddress("0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689"),
|
||||||
|
},
|
||||||
|
"bad signature fields": {
|
||||||
|
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","v":"0x58","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
|
||||||
|
wantError: ErrInvalidSig,
|
||||||
|
},
|
||||||
|
"missing signature v": {
|
||||||
|
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
|
||||||
|
wantError: errMissingTxSignatureFields,
|
||||||
|
},
|
||||||
|
"missing signature fields": {
|
||||||
|
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00"}`,
|
||||||
|
wantError: errMissingTxSignatureFields,
|
||||||
|
},
|
||||||
|
"missing fields": {
|
||||||
|
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","v":"0x1c","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
|
||||||
|
wantError: errMissingTxFields,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalTransaction(t *testing.T) {
|
||||||
|
for name, test := range unmarshalTransactionTests {
|
||||||
|
var tx *Transaction
|
||||||
|
err := json.Unmarshal([]byte(test.input), &tx)
|
||||||
|
if !checkError(t, name, err, test.wantError) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tx.Hash() != test.wantHash {
|
||||||
|
t.Errorf("test %q: got hash %x, want %x", name, tx.Hash(), test.wantHash)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
from, err := tx.From()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("test %q: From error %v", name, err)
|
||||||
|
}
|
||||||
|
if from != test.wantFrom {
|
||||||
|
t.Errorf("test %q: sender mismatch: got %x, want %x", name, from, test.wantFrom)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var unmarshalReceiptTests = map[string]struct {
|
||||||
|
input string
|
||||||
|
wantError error
|
||||||
|
}{
|
||||||
|
"ok": {
|
||||||
|
input: `{"blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","blockNumber":"0x1e773b","contractAddress":null,"cumulativeGasUsed":"0x10cea","from":"0xdf21fa922215b1a56f5a6d6294e6e36c85a0acfb","gasUsed":"0xbae2","logs":[{"address":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000df21fa922215b1a56f5a6d6294e6e36c85a0acfb","0x00000000000000000000000032be343b94f860124dc4fee278fdcbd38c102d88"],"data":"0x0000000000000000000000000000000000000000000000027cfefc4f3f392700","blockNumber":"0x1e773b","transactionIndex":"0x1","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","logIndex":"0x0"}],"logsBloom":"0x00000000000000020000000000020000000000000000000000000000000000000000000000000000000000000000000000040000000000000100000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000010000000000000000000000000000000000000000000000010000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000002002000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000","root":"0x6e8a06b2dac39ac5c9d4db5fb2a2a94ef7a6e5ec1c554079112112caf162998a","to":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","transactionIndex":"0x1"}`,
|
||||||
|
},
|
||||||
|
"missing post state": {
|
||||||
|
input: `{"blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","blockNumber":"0x1e773b","contractAddress":null,"cumulativeGasUsed":"0x10cea","from":"0xdf21fa922215b1a56f5a6d6294e6e36c85a0acfb","gasUsed":"0xbae2","logs":[{"address":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000df21fa922215b1a56f5a6d6294e6e36c85a0acfb","0x00000000000000000000000032be343b94f860124dc4fee278fdcbd38c102d88"],"data":"0x0000000000000000000000000000000000000000000000027cfefc4f3f392700","blockNumber":"0x1e773b","transactionIndex":"0x1","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","logIndex":"0x0"}],"logsBloom":"0x00000000000000020000000000020000000000000000000000000000000000000000000000000000000000000000000000040000000000000100000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000010000000000000000000000000000000000000000000000010000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000002002000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000","to":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","transactionIndex":"0x1"}`,
|
||||||
|
wantError: errMissingReceiptPostState,
|
||||||
|
},
|
||||||
|
"missing fields": {
|
||||||
|
input: `{"blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","blockNumber":"0x1e773b","contractAddress":null,"cumulativeGasUsed":"0x10cea","from":"0xdf21fa922215b1a56f5a6d6294e6e36c85a0acfb","gasUsed":"0xbae2","logs":[{"address":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000df21fa922215b1a56f5a6d6294e6e36c85a0acfb","0x00000000000000000000000032be343b94f860124dc4fee278fdcbd38c102d88"],"data":"0x0000000000000000000000000000000000000000000000027cfefc4f3f392700","blockNumber":"0x1e773b","transactionIndex":"0x1","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","logIndex":"0x0"}],"logsBloom":"0x00000000000000020000000000020000000000000000000000000000000000000000000000000000000000000000000000040000000000000100000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000010000000000000000000000000000000000000000000000010000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000002002000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000","root":"0x6e8a06b2dac39ac5c9d4db5fb2a2a94ef7a6e5ec1c554079112112caf162998a","to":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413"}`,
|
||||||
|
wantError: errMissingReceiptFields,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalReceipt(t *testing.T) {
|
||||||
|
for name, test := range unmarshalReceiptTests {
|
||||||
|
var r *Receipt
|
||||||
|
err := json.Unmarshal([]byte(test.input), &r)
|
||||||
|
checkError(t, name, err, test.wantError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkError(t *testing.T, testname string, got, want error) bool {
|
||||||
|
if got == nil {
|
||||||
|
if want != nil {
|
||||||
|
t.Errorf("test %q: got no error, want %q", testname, want)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if want == nil {
|
||||||
|
t.Errorf("test %q: unexpected error %q", testname, got)
|
||||||
|
} else if got.Error() != want.Error() {
|
||||||
|
t.Errorf("test %q: got error %q, want %q", testname, got, want)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
@ -17,6 +17,8 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
@ -26,6 +28,11 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errMissingReceiptPostState = errors.New("missing post state root in JSON receipt")
|
||||||
|
errMissingReceiptFields = errors.New("missing required JSON receipt fields")
|
||||||
|
)
|
||||||
|
|
||||||
// Receipt represents the results of a transaction.
|
// Receipt represents the results of a transaction.
|
||||||
type Receipt struct {
|
type Receipt struct {
|
||||||
// Consensus fields
|
// Consensus fields
|
||||||
@ -34,12 +41,22 @@ type Receipt struct {
|
|||||||
Bloom Bloom
|
Bloom Bloom
|
||||||
Logs vm.Logs
|
Logs vm.Logs
|
||||||
|
|
||||||
// Implementation fields
|
// Implementation fields (don't reorder!)
|
||||||
TxHash common.Hash
|
TxHash common.Hash
|
||||||
ContractAddress common.Address
|
ContractAddress common.Address
|
||||||
GasUsed *big.Int
|
GasUsed *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type jsonReceipt struct {
|
||||||
|
PostState *common.Hash `json:"root"`
|
||||||
|
CumulativeGasUsed *hexBig `json:"cumulativeGasUsed"`
|
||||||
|
Bloom *Bloom `json:"logsBloom"`
|
||||||
|
Logs *vm.Logs `json:"logs"`
|
||||||
|
TxHash *common.Hash `json:"transactionHash"`
|
||||||
|
ContractAddress *common.Address `json:"contractAddress"`
|
||||||
|
GasUsed *hexBig `json:"gasUsed"`
|
||||||
|
}
|
||||||
|
|
||||||
// NewReceipt creates a barebone transaction receipt, copying the init fields.
|
// NewReceipt creates a barebone transaction receipt, copying the init fields.
|
||||||
func NewReceipt(root []byte, cumulativeGasUsed *big.Int) *Receipt {
|
func NewReceipt(root []byte, cumulativeGasUsed *big.Int) *Receipt {
|
||||||
return &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: new(big.Int).Set(cumulativeGasUsed)}
|
return &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: new(big.Int).Set(cumulativeGasUsed)}
|
||||||
@ -67,13 +84,34 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RlpEncode implements common.RlpEncode required for SHA3 derivation.
|
// UnmarshalJSON decodes the web3 RPC receipt format.
|
||||||
func (r *Receipt) RlpEncode() []byte {
|
func (r *Receipt) UnmarshalJSON(input []byte) error {
|
||||||
bytes, err := rlp.EncodeToBytes(r)
|
var dec jsonReceipt
|
||||||
if err != nil {
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
return bytes
|
// Ensure that all fields are set. PostState is checked separately because it is a
|
||||||
|
// recent addition to the RPC spec (as of August 2016) and older implementations might
|
||||||
|
// not provide it. Note that ContractAddress is not checked because it can be null.
|
||||||
|
if dec.PostState == nil {
|
||||||
|
return errMissingReceiptPostState
|
||||||
|
}
|
||||||
|
if dec.CumulativeGasUsed == nil || dec.Bloom == nil ||
|
||||||
|
dec.Logs == nil || dec.TxHash == nil || dec.GasUsed == nil {
|
||||||
|
return errMissingReceiptFields
|
||||||
|
}
|
||||||
|
*r = Receipt{
|
||||||
|
PostState: (*dec.PostState)[:],
|
||||||
|
CumulativeGasUsed: (*big.Int)(dec.CumulativeGasUsed),
|
||||||
|
Bloom: *dec.Bloom,
|
||||||
|
Logs: *dec.Logs,
|
||||||
|
TxHash: *dec.TxHash,
|
||||||
|
GasUsed: (*big.Int)(dec.GasUsed),
|
||||||
|
}
|
||||||
|
if dec.ContractAddress != nil {
|
||||||
|
r.ContractAddress = *dec.ContractAddress
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// String implements the Stringer interface.
|
// String implements the Stringer interface.
|
||||||
@ -122,7 +160,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Receipts is a wrapper around a Receipt array to implement types.DerivableList.
|
// Receipts is a wrapper around a Receipt array to implement DerivableList.
|
||||||
type Receipts []*Receipt
|
type Receipts []*Receipt
|
||||||
|
|
||||||
// Len returns the number of receipts in this list.
|
// Len returns the number of receipts in this list.
|
||||||
|
@ -19,6 +19,7 @@ package types
|
|||||||
import (
|
import (
|
||||||
"container/heap"
|
"container/heap"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
@ -28,12 +29,15 @@ import (
|
|||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrInvalidSig = errors.New("invalid v, r, s values")
|
var ErrInvalidSig = errors.New("invalid transaction v, r, s values")
|
||||||
|
|
||||||
|
var (
|
||||||
|
errMissingTxSignatureFields = errors.New("missing required JSON transaction signature fields")
|
||||||
|
errMissingTxFields = errors.New("missing required JSON transaction fields")
|
||||||
|
)
|
||||||
|
|
||||||
type Transaction struct {
|
type Transaction struct {
|
||||||
data txdata
|
data txdata
|
||||||
@ -53,6 +57,20 @@ type txdata struct {
|
|||||||
R, S *big.Int // signature
|
R, S *big.Int // signature
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type jsonTransaction struct {
|
||||||
|
Hash *common.Hash `json:"hash"`
|
||||||
|
AccountNonce *hexUint64 `json:"nonce"`
|
||||||
|
Price *hexBig `json:"gasPrice"`
|
||||||
|
GasLimit *hexBig `json:"gas"`
|
||||||
|
Recipient *common.Address `json:"to"`
|
||||||
|
Amount *hexBig `json:"value"`
|
||||||
|
Payload *hexBytes `json:"input"`
|
||||||
|
V *hexUint64 `json:"v"`
|
||||||
|
R *hexBig `json:"r"`
|
||||||
|
S *hexBig `json:"s"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContractCreation creates a new transaction with no recipient.
|
||||||
func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
|
func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
|
||||||
if len(data) > 0 {
|
if len(data) > 0 {
|
||||||
data = common.CopyBytes(data)
|
data = common.CopyBytes(data)
|
||||||
@ -69,6 +87,7 @@ func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, data
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewTransaction creates a new transaction with the given fields.
|
||||||
func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
|
func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
|
||||||
if len(data) > 0 {
|
if len(data) > 0 {
|
||||||
data = common.CopyBytes(data)
|
data = common.CopyBytes(data)
|
||||||
@ -95,10 +114,12 @@ func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice
|
|||||||
return &Transaction{data: d}
|
return &Transaction{data: d}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DecodeRLP implements rlp.Encoder
|
||||||
func (tx *Transaction) EncodeRLP(w io.Writer) error {
|
func (tx *Transaction) EncodeRLP(w io.Writer) error {
|
||||||
return rlp.Encode(w, &tx.data)
|
return rlp.Encode(w, &tx.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DecodeRLP implements rlp.Decoder
|
||||||
func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
||||||
_, size, _ := s.Kind()
|
_, size, _ := s.Kind()
|
||||||
err := s.Decode(&tx.data)
|
err := s.Decode(&tx.data)
|
||||||
@ -108,6 +129,42 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON decodes the web3 RPC transaction format.
|
||||||
|
func (tx *Transaction) UnmarshalJSON(input []byte) error {
|
||||||
|
var dec jsonTransaction
|
||||||
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Ensure that all fields are set. V, R, S are checked separately because they're a
|
||||||
|
// recent addition to the RPC spec (as of August 2016) and older implementations might
|
||||||
|
// not provide them. Note that Recipient is not checked because it can be missing for
|
||||||
|
// contract creations.
|
||||||
|
if dec.V == nil || dec.R == nil || dec.S == nil {
|
||||||
|
return errMissingTxSignatureFields
|
||||||
|
}
|
||||||
|
if !crypto.ValidateSignatureValues(byte(*dec.V), (*big.Int)(dec.R), (*big.Int)(dec.S), false) {
|
||||||
|
return ErrInvalidSig
|
||||||
|
}
|
||||||
|
if dec.AccountNonce == nil || dec.Price == nil || dec.GasLimit == nil || dec.Amount == nil || dec.Payload == nil {
|
||||||
|
return errMissingTxFields
|
||||||
|
}
|
||||||
|
// Assign the fields. This is not atomic but reusing transactions
|
||||||
|
// for decoding isn't thread safe anyway.
|
||||||
|
*tx = Transaction{}
|
||||||
|
tx.data = txdata{
|
||||||
|
AccountNonce: uint64(*dec.AccountNonce),
|
||||||
|
Recipient: dec.Recipient,
|
||||||
|
Amount: (*big.Int)(dec.Amount),
|
||||||
|
GasLimit: (*big.Int)(dec.GasLimit),
|
||||||
|
Price: (*big.Int)(dec.Price),
|
||||||
|
Payload: *dec.Payload,
|
||||||
|
V: byte(*dec.V),
|
||||||
|
R: (*big.Int)(dec.R),
|
||||||
|
S: (*big.Int)(dec.S),
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
|
func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
|
||||||
func (tx *Transaction) Gas() *big.Int { return new(big.Int).Set(tx.data.GasLimit) }
|
func (tx *Transaction) Gas() *big.Int { return new(big.Int).Set(tx.data.GasLimit) }
|
||||||
func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) }
|
func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) }
|
||||||
@ -215,6 +272,7 @@ func (tx *Transaction) Cost() *big.Int {
|
|||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SignatureValues returns the ECDSA signature values contained in the transaction.
|
||||||
func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int) {
|
func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int) {
|
||||||
return tx.data.V, new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S)
|
return tx.data.V, new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S)
|
||||||
}
|
}
|
||||||
@ -235,7 +293,6 @@ func (tx *Transaction) publicKey(homestead bool) ([]byte, error) {
|
|||||||
hash := tx.SigHash()
|
hash := tx.SigHash()
|
||||||
pub, err := crypto.Ecrecover(hash[:], sig)
|
pub, err := crypto.Ecrecover(hash[:], sig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Error).Infof("Could not get pubkey from signature: ", err)
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(pub) == 0 || pub[0] != 4 {
|
if len(pub) == 0 || pub[0] != 4 {
|
||||||
|
@ -18,6 +18,7 @@ package vm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
@ -25,18 +26,33 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Log struct {
|
var errMissingLogFields = errors.New("missing required JSON log fields")
|
||||||
// Consensus fields
|
|
||||||
Address common.Address
|
|
||||||
Topics []common.Hash
|
|
||||||
Data []byte
|
|
||||||
|
|
||||||
// Derived fields (don't reorder!)
|
// Log represents a contract log event. These events are generated by the LOG
|
||||||
BlockNumber uint64
|
// opcode and stored/indexed by the node.
|
||||||
TxHash common.Hash
|
type Log struct {
|
||||||
TxIndex uint
|
// Consensus fields.
|
||||||
BlockHash common.Hash
|
Address common.Address // address of the contract that generated the event
|
||||||
Index uint
|
Topics []common.Hash // list of topics provided by the contract.
|
||||||
|
Data []byte // supplied by the contract, usually ABI-encoded
|
||||||
|
|
||||||
|
// Derived fields (don't reorder!).
|
||||||
|
BlockNumber uint64 // block in which the transaction was included
|
||||||
|
TxHash common.Hash // hash of the transaction
|
||||||
|
TxIndex uint // index of the transaction in the block
|
||||||
|
BlockHash common.Hash // hash of the block in which the transaction was included
|
||||||
|
Index uint // index of the log in the receipt
|
||||||
|
}
|
||||||
|
|
||||||
|
type jsonLog struct {
|
||||||
|
Address *common.Address `json:"address"`
|
||||||
|
Topics *[]common.Hash `json:"topics"`
|
||||||
|
Data string `json:"data"`
|
||||||
|
BlockNumber string `json:"blockNumber"`
|
||||||
|
TxIndex string `json:"transactionIndex"`
|
||||||
|
TxHash *common.Hash `json:"transactionHash"`
|
||||||
|
BlockHash *common.Hash `json:"blockHash"`
|
||||||
|
Index string `json:"logIndex"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLog(address common.Address, topics []common.Hash, data []byte, number uint64) *Log {
|
func NewLog(address common.Address, topics []common.Hash, data []byte, number uint64) *Log {
|
||||||
@ -64,19 +80,50 @@ func (l *Log) String() string {
|
|||||||
return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, l.Address, l.Topics, l.Data, l.TxHash, l.TxIndex, l.BlockHash, l.Index)
|
return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, l.Address, l.Topics, l.Data, l.TxHash, l.TxIndex, l.BlockHash, l.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements json.Marshaler.
|
||||||
func (r *Log) MarshalJSON() ([]byte, error) {
|
func (r *Log) MarshalJSON() ([]byte, error) {
|
||||||
fields := map[string]interface{}{
|
return json.Marshal(&jsonLog{
|
||||||
"address": r.Address,
|
Address: &r.Address,
|
||||||
"data": fmt.Sprintf("%#x", r.Data),
|
Topics: &r.Topics,
|
||||||
"blockNumber": fmt.Sprintf("%#x", r.BlockNumber),
|
Data: fmt.Sprintf("0x%x", r.Data),
|
||||||
"logIndex": fmt.Sprintf("%#x", r.Index),
|
BlockNumber: fmt.Sprintf("0x%x", r.BlockNumber),
|
||||||
"blockHash": r.BlockHash,
|
TxIndex: fmt.Sprintf("0x%x", r.TxIndex),
|
||||||
"transactionHash": r.TxHash,
|
TxHash: &r.TxHash,
|
||||||
"transactionIndex": fmt.Sprintf("%#x", r.TxIndex),
|
BlockHash: &r.BlockHash,
|
||||||
"topics": r.Topics,
|
Index: fmt.Sprintf("0x%x", r.Index),
|
||||||
}
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return json.Marshal(fields)
|
// UnmarshalJSON implements json.Umarshaler.
|
||||||
|
func (r *Log) UnmarshalJSON(input []byte) error {
|
||||||
|
var dec jsonLog
|
||||||
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if dec.Address == nil || dec.Topics == nil || dec.Data == "" || dec.BlockNumber == "" ||
|
||||||
|
dec.TxIndex == "" || dec.TxHash == nil || dec.BlockHash == nil || dec.Index == "" {
|
||||||
|
return errMissingLogFields
|
||||||
|
}
|
||||||
|
declog := Log{
|
||||||
|
Address: *dec.Address,
|
||||||
|
Topics: *dec.Topics,
|
||||||
|
TxHash: *dec.TxHash,
|
||||||
|
BlockHash: *dec.BlockHash,
|
||||||
|
}
|
||||||
|
if _, err := fmt.Sscanf(dec.Data, "0x%x", &declog.Data); err != nil {
|
||||||
|
return fmt.Errorf("invalid hex log data")
|
||||||
|
}
|
||||||
|
if _, err := fmt.Sscanf(dec.BlockNumber, "0x%x", &declog.BlockNumber); err != nil {
|
||||||
|
return fmt.Errorf("invalid hex log block number")
|
||||||
|
}
|
||||||
|
if _, err := fmt.Sscanf(dec.TxIndex, "0x%x", &declog.TxIndex); err != nil {
|
||||||
|
return fmt.Errorf("invalid hex log tx index")
|
||||||
|
}
|
||||||
|
if _, err := fmt.Sscanf(dec.Index, "0x%x", &declog.Index); err != nil {
|
||||||
|
return fmt.Errorf("invalid hex log index")
|
||||||
|
}
|
||||||
|
*r = declog
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Logs []*Log
|
type Logs []*Log
|
||||||
|
59
core/vm/log_test.go
Normal file
59
core/vm/log_test.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Copyright 2016 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 vm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var unmarshalLogTests = map[string]struct {
|
||||||
|
input string
|
||||||
|
wantError error
|
||||||
|
}{
|
||||||
|
"ok": {
|
||||||
|
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x000000000000000000000000000000000000000000000001a055690d9db80000","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
|
||||||
|
},
|
||||||
|
"missing data": {
|
||||||
|
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
|
||||||
|
wantError: errMissingLogFields,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalLog(t *testing.T) {
|
||||||
|
for name, test := range unmarshalLogTests {
|
||||||
|
var log *Log
|
||||||
|
err := json.Unmarshal([]byte(test.input), &log)
|
||||||
|
checkError(t, name, err, test.wantError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkError(t *testing.T, testname string, got, want error) bool {
|
||||||
|
if got == nil {
|
||||||
|
if want != nil {
|
||||||
|
t.Errorf("test %q: got no error, want %q", testname, want)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if want == nil {
|
||||||
|
t.Errorf("test %q: unexpected error %q", testname, got)
|
||||||
|
} else if got.Error() != want.Error() {
|
||||||
|
t.Errorf("test %q: got error %q, want %q", testname, got, want)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
96
eth/bind.go
96
eth/bind.go
@ -19,6 +19,7 @@ package eth
|
|||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
@ -50,47 +51,62 @@ func NewContractBackend(eth *Ethereum) *ContractBackend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasCode implements bind.ContractVerifier.HasCode by retrieving any code associated
|
// CodeAt retrieves any code associated with the contract from the local API.
|
||||||
// with the contract from the local API, and checking its size.
|
func (b *ContractBackend) CodeAt(ctx context.Context, contract common.Address, blockNum *big.Int) ([]byte, error) {
|
||||||
func (b *ContractBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) {
|
out, err := b.bcapi.GetCode(ctx, contract, toBlockNumber(blockNum))
|
||||||
if ctx == nil {
|
return common.FromHex(out), err
|
||||||
ctx = context.Background()
|
}
|
||||||
}
|
|
||||||
block := rpc.LatestBlockNumber
|
// CodeAt retrieves any code associated with the contract from the local API.
|
||||||
if pending {
|
func (b *ContractBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
||||||
block = rpc.PendingBlockNumber
|
out, err := b.bcapi.GetCode(ctx, contract, rpc.PendingBlockNumber)
|
||||||
}
|
return common.FromHex(out), err
|
||||||
out, err := b.bcapi.GetCode(ctx, contract, block)
|
|
||||||
return len(common.FromHex(out)) > 0, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContractCall implements bind.ContractCaller executing an Ethereum contract
|
// ContractCall implements bind.ContractCaller executing an Ethereum contract
|
||||||
// call with the specified data as the input. The pending flag requests execution
|
// call with the specified data as the input. The pending flag requests execution
|
||||||
// against the pending block, not the stable head of the chain.
|
// against the pending block, not the stable head of the chain.
|
||||||
func (b *ContractBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) {
|
func (b *ContractBackend) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNum *big.Int) ([]byte, error) {
|
||||||
if ctx == nil {
|
out, err := b.bcapi.Call(ctx, toCallArgs(msg), toBlockNumber(blockNum))
|
||||||
ctx = context.Background()
|
|
||||||
}
|
|
||||||
// Convert the input args to the API spec
|
|
||||||
args := ethapi.CallArgs{
|
|
||||||
To: &contract,
|
|
||||||
Data: common.ToHex(data),
|
|
||||||
}
|
|
||||||
block := rpc.LatestBlockNumber
|
|
||||||
if pending {
|
|
||||||
block = rpc.PendingBlockNumber
|
|
||||||
}
|
|
||||||
// Execute the call and convert the output back to Go types
|
|
||||||
out, err := b.bcapi.Call(ctx, args, block)
|
|
||||||
return common.FromHex(out), err
|
return common.FromHex(out), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ContractCall implements bind.ContractCaller executing an Ethereum contract
|
||||||
|
// call with the specified data as the input. The pending flag requests execution
|
||||||
|
// against the pending block, not the stable head of the chain.
|
||||||
|
func (b *ContractBackend) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) {
|
||||||
|
out, err := b.bcapi.Call(ctx, toCallArgs(msg), rpc.PendingBlockNumber)
|
||||||
|
return common.FromHex(out), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func toCallArgs(msg ethereum.CallMsg) ethapi.CallArgs {
|
||||||
|
args := ethapi.CallArgs{
|
||||||
|
To: msg.To,
|
||||||
|
From: msg.From,
|
||||||
|
Data: common.ToHex(msg.Data),
|
||||||
|
}
|
||||||
|
if msg.Gas != nil {
|
||||||
|
args.Gas = *rpc.NewHexNumber(msg.Gas)
|
||||||
|
}
|
||||||
|
if msg.GasPrice != nil {
|
||||||
|
args.GasPrice = *rpc.NewHexNumber(msg.GasPrice)
|
||||||
|
}
|
||||||
|
if msg.Value != nil {
|
||||||
|
args.Value = *rpc.NewHexNumber(msg.Value)
|
||||||
|
}
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
|
||||||
|
func toBlockNumber(num *big.Int) rpc.BlockNumber {
|
||||||
|
if num == nil {
|
||||||
|
return rpc.LatestBlockNumber
|
||||||
|
}
|
||||||
|
return rpc.BlockNumber(num.Int64())
|
||||||
|
}
|
||||||
|
|
||||||
// PendingAccountNonce implements bind.ContractTransactor retrieving the current
|
// PendingAccountNonce implements bind.ContractTransactor retrieving the current
|
||||||
// pending nonce associated with an account.
|
// pending nonce associated with an account.
|
||||||
func (b *ContractBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) {
|
func (b *ContractBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
|
||||||
if ctx == nil {
|
|
||||||
ctx = context.Background()
|
|
||||||
}
|
|
||||||
out, err := b.txapi.GetTransactionCount(ctx, account, rpc.PendingBlockNumber)
|
out, err := b.txapi.GetTransactionCount(ctx, account, rpc.PendingBlockNumber)
|
||||||
return out.Uint64(), err
|
return out.Uint64(), err
|
||||||
}
|
}
|
||||||
@ -98,9 +114,6 @@ func (b *ContractBackend) PendingAccountNonce(ctx context.Context, account commo
|
|||||||
// SuggestGasPrice implements bind.ContractTransactor retrieving the currently
|
// SuggestGasPrice implements bind.ContractTransactor retrieving the currently
|
||||||
// suggested gas price to allow a timely execution of a transaction.
|
// suggested gas price to allow a timely execution of a transaction.
|
||||||
func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
||||||
if ctx == nil {
|
|
||||||
ctx = context.Background()
|
|
||||||
}
|
|
||||||
return b.eapi.GasPrice(ctx)
|
return b.eapi.GasPrice(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,25 +122,14 @@ func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error)
|
|||||||
// the backend blockchain. There is no guarantee that this is the true gas limit
|
// the backend blockchain. There is no guarantee that this is the true gas limit
|
||||||
// requirement as other transactions may be added or removed by miners, but it
|
// requirement as other transactions may be added or removed by miners, but it
|
||||||
// should provide a basis for setting a reasonable default.
|
// should provide a basis for setting a reasonable default.
|
||||||
func (b *ContractBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) {
|
func (b *ContractBackend) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (*big.Int, error) {
|
||||||
if ctx == nil {
|
out, err := b.bcapi.EstimateGas(ctx, toCallArgs(msg))
|
||||||
ctx = context.Background()
|
|
||||||
}
|
|
||||||
out, err := b.bcapi.EstimateGas(ctx, ethapi.CallArgs{
|
|
||||||
From: sender,
|
|
||||||
To: contract,
|
|
||||||
Value: *rpc.NewHexNumber(value),
|
|
||||||
Data: common.ToHex(data),
|
|
||||||
})
|
|
||||||
return out.BigInt(), err
|
return out.BigInt(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendTransaction implements bind.ContractTransactor injects the transaction
|
// SendTransaction implements bind.ContractTransactor injects the transaction
|
||||||
// into the pending pool for execution.
|
// into the pending pool for execution.
|
||||||
func (b *ContractBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
func (b *ContractBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
||||||
if ctx == nil {
|
|
||||||
ctx = context.Background()
|
|
||||||
}
|
|
||||||
raw, _ := rlp.EncodeToBytes(tx)
|
raw, _ := rlp.EncodeToBytes(tx)
|
||||||
_, err := b.txapi.SendRawTransaction(ctx, common.ToHex(raw))
|
_, err := b.txapi.SendRawTransaction(ctx, common.ToHex(raw))
|
||||||
return err
|
return err
|
||||||
|
@ -634,9 +634,6 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
|||||||
if err := msg.Decode(&request); err != nil {
|
if err := msg.Decode(&request); err != nil {
|
||||||
return errResp(ErrDecode, "%v: %v", msg, err)
|
return errResp(ErrDecode, "%v: %v", msg, err)
|
||||||
}
|
}
|
||||||
if err := request.Block.ValidateFields(); err != nil {
|
|
||||||
return errResp(ErrDecode, "block validation %v: %v", msg, err)
|
|
||||||
}
|
|
||||||
request.Block.ReceivedAt = msg.ReceivedAt
|
request.Block.ReceivedAt = msg.ReceivedAt
|
||||||
request.Block.ReceivedFrom = p
|
request.Block.ReceivedFrom = p
|
||||||
|
|
||||||
|
382
ethclient/ethclient.go
Normal file
382
ethclient/ethclient.go
Normal file
@ -0,0 +1,382 @@
|
|||||||
|
// Copyright 2016 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 ethclient provides a client for the Ethereum RPC API.
|
||||||
|
package ethclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client defines typed wrappers for the Ethereum RPC API.
|
||||||
|
type Client struct {
|
||||||
|
c *rpc.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dial connects a client to the given URL.
|
||||||
|
func Dial(rawurl string) (*Client, error) {
|
||||||
|
c, err := rpc.Dial(rawurl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewClient(c), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClient creates a client that uses the given RPC client.
|
||||||
|
func NewClient(c *rpc.Client) *Client {
|
||||||
|
return &Client{c}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blockchain Access
|
||||||
|
|
||||||
|
// BlockByHash returns the given full block.
|
||||||
|
//
|
||||||
|
// Note that loading full blocks requires two requests. Use HeaderByHash
|
||||||
|
// if you don't need all transactions or uncle headers.
|
||||||
|
func (ec *Client) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
||||||
|
return ec.getBlock(ctx, "eth_getBlockByHash", hash, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockByNumber returns a block from the current canonical chain. If number is nil, the
|
||||||
|
// latest known block is returned.
|
||||||
|
//
|
||||||
|
// Note that loading full blocks requires two requests. Use HeaderByNumber
|
||||||
|
// if you don't need all transactions or uncle headers.
|
||||||
|
func (ec *Client) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
|
||||||
|
return ec.getBlock(ctx, "eth_getBlockByNumber", toBlockNumArg(number), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
type rpcBlock struct {
|
||||||
|
Hash common.Hash `json:"hash"`
|
||||||
|
Transactions []*types.Transaction `json:"transactions"`
|
||||||
|
UncleHashes []common.Hash `json:"uncles"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *Client) getBlock(ctx context.Context, method string, args ...interface{}) (*types.Block, error) {
|
||||||
|
var raw json.RawMessage
|
||||||
|
err := ec.c.CallContext(ctx, &raw, method, args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Decode header and transactions.
|
||||||
|
var head *types.Header
|
||||||
|
var body rpcBlock
|
||||||
|
if err := json.Unmarshal(raw, &head); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(raw, &body); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Quick-verify transaction and uncle lists. This mostly helps with debugging the server.
|
||||||
|
if head.UncleHash == types.EmptyUncleHash && len(body.UncleHashes) > 0 {
|
||||||
|
return nil, fmt.Errorf("server returned non-empty uncle list but block header indicates no uncles")
|
||||||
|
}
|
||||||
|
if head.UncleHash != types.EmptyUncleHash && len(body.UncleHashes) == 0 {
|
||||||
|
return nil, fmt.Errorf("server returned empty uncle list but block header indicates uncles")
|
||||||
|
}
|
||||||
|
if head.TxHash == types.EmptyRootHash && len(body.Transactions) > 0 {
|
||||||
|
return nil, fmt.Errorf("server returned non-empty transaction list but block header indicates no transactions")
|
||||||
|
}
|
||||||
|
if head.TxHash != types.EmptyRootHash && len(body.Transactions) == 0 {
|
||||||
|
return nil, fmt.Errorf("server returned empty transaction list but block header indicates transactions")
|
||||||
|
}
|
||||||
|
// Load uncles because they are not included in the block response.
|
||||||
|
var uncles []*types.Header
|
||||||
|
if len(body.UncleHashes) > 0 {
|
||||||
|
uncles = make([]*types.Header, len(body.UncleHashes))
|
||||||
|
reqs := make([]rpc.BatchElem, len(body.UncleHashes))
|
||||||
|
for i := range reqs {
|
||||||
|
reqs[i] = rpc.BatchElem{
|
||||||
|
Method: "eth_getUncleByBlockHashAndIndex",
|
||||||
|
Args: []interface{}{body.Hash, fmt.Sprintf("%#x", i)},
|
||||||
|
Result: &uncles[i],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := ec.c.BatchCallContext(ctx, reqs); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for i := range reqs {
|
||||||
|
if reqs[i].Error != nil {
|
||||||
|
return nil, reqs[i].Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return types.NewBlockWithHeader(head).WithBody(body.Transactions, uncles), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderByHash returns the block header with the given hash.
|
||||||
|
func (ec *Client) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
||||||
|
var head *types.Header
|
||||||
|
err := ec.c.CallContext(ctx, &head, "eth_getBlockByHash", hash, false)
|
||||||
|
return head, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeaderByNumber returns a block header from the current canonical chain. If number is
|
||||||
|
// nil, the latest known header is returned.
|
||||||
|
func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
|
||||||
|
var head *types.Header
|
||||||
|
err := ec.c.CallContext(ctx, &head, "eth_getBlockByNumber", toBlockNumArg(number), false)
|
||||||
|
return head, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransactionByHash returns the transaction with the given hash.
|
||||||
|
func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, error) {
|
||||||
|
var tx *types.Transaction
|
||||||
|
err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByHash", hash, false)
|
||||||
|
if err == nil {
|
||||||
|
if _, r, _ := tx.SignatureValues(); r == nil {
|
||||||
|
return nil, fmt.Errorf("server returned transaction without signature")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tx, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransactionCount returns the total number of transactions in the given block.
|
||||||
|
func (ec *Client) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) {
|
||||||
|
var num rpc.HexNumber
|
||||||
|
err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByHash", blockHash)
|
||||||
|
return num.Uint(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransactionInBlock returns a single transaction at index in the given block.
|
||||||
|
func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) {
|
||||||
|
var tx *types.Transaction
|
||||||
|
err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByBlockHashAndIndex", blockHash, index)
|
||||||
|
if err == nil {
|
||||||
|
if _, r, _ := tx.SignatureValues(); r == nil {
|
||||||
|
return nil, fmt.Errorf("server returned transaction without signature")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tx, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransactionReceipt returns the receipt of a transaction by transaction hash.
|
||||||
|
// Note that the receipt is not available for pending transactions.
|
||||||
|
func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
||||||
|
var r *types.Receipt
|
||||||
|
err := ec.c.CallContext(ctx, &r, "eth_getTransactionReceipt", txHash)
|
||||||
|
if err == nil && r != nil && len(r.PostState) == 0 {
|
||||||
|
return nil, fmt.Errorf("server returned receipt without post state")
|
||||||
|
}
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func toBlockNumArg(number *big.Int) string {
|
||||||
|
if number == nil {
|
||||||
|
return "latest"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%#x", number)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeNewHead subscribes to notifications about the current blockchain head
|
||||||
|
// on the given channel.
|
||||||
|
func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) {
|
||||||
|
return ec.c.EthSubscribe(ctx, ch, "newBlocks", map[string]struct{}{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// State Access
|
||||||
|
|
||||||
|
// BalanceAt returns the wei balance of the given account.
|
||||||
|
// The block number can be nil, in which case the balance is taken from the latest known block.
|
||||||
|
func (ec *Client) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||||
|
var result rpc.HexNumber
|
||||||
|
err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, toBlockNumArg(blockNumber))
|
||||||
|
return (*big.Int)(&result), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// StorageAt returns the value of key in the contract storage of the given account.
|
||||||
|
// The block number can be nil, in which case the value is taken from the latest known block.
|
||||||
|
func (ec *Client) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
|
||||||
|
var result rpc.HexBytes
|
||||||
|
err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, toBlockNumArg(blockNumber))
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CodeAt returns the contract code of the given account.
|
||||||
|
// The block number can be nil, in which case the code is taken from the latest known block.
|
||||||
|
func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||||
|
var result rpc.HexBytes
|
||||||
|
err := ec.c.CallContext(ctx, &result, "eth_getCode", account, toBlockNumArg(blockNumber))
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NonceAt returns the account nonce of the given account.
|
||||||
|
// The block number can be nil, in which case the nonce is taken from the latest known block.
|
||||||
|
func (ec *Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
|
||||||
|
var result rpc.HexNumber
|
||||||
|
err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, toBlockNumArg(blockNumber))
|
||||||
|
return result.Uint64(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filters
|
||||||
|
|
||||||
|
// FilterLogs executes a filter query.
|
||||||
|
func (ec *Client) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]vm.Log, error) {
|
||||||
|
var result []vm.Log
|
||||||
|
err := ec.c.CallContext(ctx, &result, "eth_getFilterLogs", toFilterArg(q))
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscribeFilterLogs subscribes to the results of a streaming filter query.
|
||||||
|
func (ec *Client) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- vm.Log) (ethereum.Subscription, error) {
|
||||||
|
return ec.c.EthSubscribe(ctx, ch, "logs", toFilterArg(q))
|
||||||
|
}
|
||||||
|
|
||||||
|
func toFilterArg(q ethereum.FilterQuery) interface{} {
|
||||||
|
arg := map[string]interface{}{
|
||||||
|
"fromBlock": toBlockNumArg(q.FromBlock),
|
||||||
|
"endBlock": toBlockNumArg(q.ToBlock),
|
||||||
|
"addresses": q.Addresses,
|
||||||
|
"topics": q.Topics,
|
||||||
|
}
|
||||||
|
if q.FromBlock == nil {
|
||||||
|
arg["fromBlock"] = "0x0"
|
||||||
|
}
|
||||||
|
return arg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pending State
|
||||||
|
|
||||||
|
// PendingBalanceAt returns the wei balance of the given account in the pending state.
|
||||||
|
func (ec *Client) PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) {
|
||||||
|
var result rpc.HexNumber
|
||||||
|
err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, "pending")
|
||||||
|
return (*big.Int)(&result), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingStorageAt returns the value of key in the contract storage of the given account in the pending state.
|
||||||
|
func (ec *Client) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) {
|
||||||
|
var result rpc.HexBytes
|
||||||
|
err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, "pending")
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingCodeAt returns the contract code of the given account in the pending state.
|
||||||
|
func (ec *Client) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
|
||||||
|
var result rpc.HexBytes
|
||||||
|
err := ec.c.CallContext(ctx, &result, "eth_getCode", account, "pending")
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingNonceAt returns the account nonce of the given account in the pending state.
|
||||||
|
// This is the nonce that should be used for the next transaction.
|
||||||
|
func (ec *Client) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
|
||||||
|
var result rpc.HexNumber
|
||||||
|
err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, "pending")
|
||||||
|
return result.Uint64(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingTransactionCount returns the total number of transactions in the pending state.
|
||||||
|
func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) {
|
||||||
|
var num rpc.HexNumber
|
||||||
|
err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByNumber", "pending")
|
||||||
|
return num.Uint(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: SubscribePendingTransactions (needs server side)
|
||||||
|
|
||||||
|
// Contract Calling
|
||||||
|
|
||||||
|
// CallContract executes a message call transaction, which is directly executed in the VM
|
||||||
|
// of the node, but never mined into the blockchain.
|
||||||
|
//
|
||||||
|
// blockNumber selects the block height at which the call runs. It can be nil, in which
|
||||||
|
// case the code is taken from the latest known block. Note that state from very old
|
||||||
|
// blocks might not be available.
|
||||||
|
func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
||||||
|
var hex string
|
||||||
|
err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), toBlockNumArg(blockNumber))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return common.FromHex(hex), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingCallContract executes a message call transaction using the EVM.
|
||||||
|
// The state seen by the contract call is the pending state.
|
||||||
|
func (ec *Client) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) {
|
||||||
|
var hex string
|
||||||
|
err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), "pending")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return common.FromHex(hex), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
|
||||||
|
// execution of a transaction.
|
||||||
|
func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
||||||
|
var hex rpc.HexNumber
|
||||||
|
if err := ec.c.CallContext(ctx, &hex, "eth_gasPrice"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return (*big.Int)(&hex), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EstimateGas tries to estimate the gas needed to execute a specific transaction based on
|
||||||
|
// the current pending state of the backend blockchain. There is no guarantee that this is
|
||||||
|
// the true gas limit requirement as other transactions may be added or removed by miners,
|
||||||
|
// but it should provide a basis for setting a reasonable default.
|
||||||
|
func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (*big.Int, error) {
|
||||||
|
var hex rpc.HexNumber
|
||||||
|
err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return (*big.Int)(&hex), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendTransaction injects a signed transaction into the pending pool for execution.
|
||||||
|
//
|
||||||
|
// If the transaction was a contract creation use the TransactionReceipt method to get the
|
||||||
|
// contract address after the transaction has been mined.
|
||||||
|
func (ec *Client) SendTransaction(ctx context.Context, tx *types.Transaction) error {
|
||||||
|
data, err := rlp.EncodeToBytes(tx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ec.c.CallContext(ctx, nil, "eth_sendRawTransaction", common.ToHex(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
func toCallArg(msg ethereum.CallMsg) interface{} {
|
||||||
|
arg := map[string]interface{}{
|
||||||
|
"from": msg.From,
|
||||||
|
"to": msg.To,
|
||||||
|
}
|
||||||
|
if len(msg.Data) > 0 {
|
||||||
|
arg["data"] = fmt.Sprintf("%#x", msg.Data)
|
||||||
|
}
|
||||||
|
if msg.Value != nil {
|
||||||
|
arg["value"] = fmt.Sprintf("%#x", msg.Value)
|
||||||
|
}
|
||||||
|
if msg.Gas != nil {
|
||||||
|
arg["gas"] = fmt.Sprintf("%#x", msg.Gas)
|
||||||
|
}
|
||||||
|
if msg.GasPrice != nil {
|
||||||
|
arg["gasPrice"] = fmt.Sprintf("%#x", msg.GasPrice)
|
||||||
|
}
|
||||||
|
return arg
|
||||||
|
}
|
17
ethclient/ethclient_test.go
Normal file
17
ethclient/ethclient_test.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package ethclient
|
||||||
|
|
||||||
|
import "github.com/ethereum/go-ethereum"
|
||||||
|
|
||||||
|
// Verify that Client implements the ethereum interfaces.
|
||||||
|
var (
|
||||||
|
_ = ethereum.ChainReader(&Client{})
|
||||||
|
_ = ethereum.ChainStateReader(&Client{})
|
||||||
|
_ = ethereum.ChainHeadEventer(&Client{})
|
||||||
|
_ = ethereum.ContractCaller(&Client{})
|
||||||
|
_ = ethereum.GasEstimator(&Client{})
|
||||||
|
_ = ethereum.GasPricer(&Client{})
|
||||||
|
_ = ethereum.LogFilterer(&Client{})
|
||||||
|
_ = ethereum.PendingStateReader(&Client{})
|
||||||
|
// _ = ethereum.PendingStateEventer(&Client{})
|
||||||
|
_ = ethereum.PendingContractCaller(&Client{})
|
||||||
|
)
|
168
interfaces.go
Normal file
168
interfaces.go
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
// Copyright 2016 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 ethereum defines interfaces for interacting with Ethereum.
|
||||||
|
package ethereum
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: move subscription to package event
|
||||||
|
|
||||||
|
// Subscription represents an event subscription where events are
|
||||||
|
// delivered on a data channel.
|
||||||
|
type Subscription interface {
|
||||||
|
// Unsubscribe cancels the sending of events to the data channel
|
||||||
|
// and closes the error channel.
|
||||||
|
Unsubscribe()
|
||||||
|
// Err returns the subscription error channel. The error channel receives
|
||||||
|
// a value if there is an issue with the subscription (e.g. the network connection
|
||||||
|
// delivering the events has been closed). Only one value will ever be sent.
|
||||||
|
// The error channel is closed by Unsubscribe.
|
||||||
|
Err() <-chan error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainReader provides access to the blockchain. The methods in this interface access raw
|
||||||
|
// data from either the canonical chain (when requesting by block number) or any
|
||||||
|
// blockchain fork that was previously downloaded and processed by the node. The block
|
||||||
|
// number argument can be nil to select the latest canonical block. Reading block headers
|
||||||
|
// should be preferred over full blocks whenever possible.
|
||||||
|
type ChainReader interface {
|
||||||
|
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
|
||||||
|
BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error)
|
||||||
|
HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
|
||||||
|
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
|
||||||
|
TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error)
|
||||||
|
TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error)
|
||||||
|
TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, error)
|
||||||
|
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChainStateReader wraps access to the state trie of the canonical blockchain. Note that
|
||||||
|
// implementations of the interface may be unable to return state values for old blocks.
|
||||||
|
// In many cases, using CallContract can be preferable to reading raw contract storage.
|
||||||
|
type ChainStateReader interface {
|
||||||
|
BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error)
|
||||||
|
StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error)
|
||||||
|
CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error)
|
||||||
|
NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A ChainHeadEventer returns notifications whenever the canonical head block is updated.
|
||||||
|
type ChainHeadEventer interface {
|
||||||
|
SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (Subscription, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CallMsg contains parameters for contract calls.
|
||||||
|
type CallMsg struct {
|
||||||
|
From common.Address // the sender of the 'transaction'
|
||||||
|
To *common.Address // the destination contract (nil for contract creation)
|
||||||
|
Gas *big.Int // if nil, the call executes with near-infinite gas
|
||||||
|
GasPrice *big.Int // wei <-> gas exchange ratio
|
||||||
|
Value *big.Int // amount of wei sent along with the call
|
||||||
|
Data []byte // input data, usually an ABI-encoded contract method invocation
|
||||||
|
}
|
||||||
|
|
||||||
|
// A ContractCaller provides contract calls, essentially transactions that are executed by
|
||||||
|
// the EVM but not mined into the blockchain. ContractCall is a low-level method to
|
||||||
|
// execute such calls. For applications which are structured around specific contracts,
|
||||||
|
// the abigen tool provides a nicer, properly typed way to perform calls.
|
||||||
|
type ContractCaller interface {
|
||||||
|
CallContract(ctx context.Context, call CallMsg, blockNumber *big.Int) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterQuery contains options for contact log filtering.
|
||||||
|
type FilterQuery struct {
|
||||||
|
FromBlock *big.Int // beginning of the queried range, nil means genesis block
|
||||||
|
ToBlock *big.Int // end of the range, nil means latest block
|
||||||
|
Addresses []common.Address // restricts matches to events created by specific contracts
|
||||||
|
|
||||||
|
// The Topic list restricts matches to particular event topics. Each event has a list
|
||||||
|
// of topics. Topics matches a prefix of that list. An empty element slice matches any
|
||||||
|
// topic. Non-empty elements represent an alternative that matches any of the
|
||||||
|
// contained topics.
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
// {} or nil matches any topic list
|
||||||
|
// {{A}} matches topic A in first position
|
||||||
|
// {{}, {B}} matches any topic in first position, B in second position
|
||||||
|
// {{A}}, {B}} matches topic A in first position, B in second position
|
||||||
|
// {{A, B}}, {C, D}} matches topic (A OR B) in first position, (C OR D) in second position
|
||||||
|
Topics [][]common.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogFilterer provides access to contract log events using a one-off query or continuous
|
||||||
|
// event subscription.
|
||||||
|
type LogFilterer interface {
|
||||||
|
FilterLogs(ctx context.Context, q FilterQuery) ([]vm.Log, error)
|
||||||
|
SubscribeFilterLogs(ctx context.Context, q FilterQuery, ch chan<- vm.Log) (Subscription, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TransactionSender wraps transaction sending. The SendTransaction method injects a
|
||||||
|
// signed transaction into the pending transaction pool for execution. If the transaction
|
||||||
|
// was a contract creation, the TransactionReceipt method can be used to retrieve the
|
||||||
|
// contract address after the transaction has been mined.
|
||||||
|
//
|
||||||
|
// The transaction must be signed and have a valid nonce to be included. Consumers of the
|
||||||
|
// API can use package accounts to maintain local private keys and need can retrieve the
|
||||||
|
// next available nonce using PendingNonceAt.
|
||||||
|
type TransactionSender interface {
|
||||||
|
SendTransaction(ctx context.Context, tx *types.Transaction) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// GasPricer wraps the gas price oracle, which monitors the blockchain to determine the
|
||||||
|
// optimal gas price given current fee market conditions.
|
||||||
|
type GasPricer interface {
|
||||||
|
SuggestGasPrice(ctx context.Context) (*big.Int, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A PendingStateReader provides access to the pending state, which is the result of all
|
||||||
|
// known executable transactions which have not yet been included in the blockchain. It is
|
||||||
|
// commonly used to display the result of ’unconfirmed’ actions (e.g. wallet value
|
||||||
|
// transfers) initiated by the user. The PendingNonceAt operation is a good way to
|
||||||
|
// retrieve the next available transaction nonce for a specific account.
|
||||||
|
type PendingStateReader interface {
|
||||||
|
PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error)
|
||||||
|
PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error)
|
||||||
|
PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error)
|
||||||
|
PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
|
||||||
|
PendingTransactionCount(ctx context.Context) (uint, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PendingContractCaller can be used to perform calls against the pending state.
|
||||||
|
type PendingContractCaller interface {
|
||||||
|
PendingCallContract(ctx context.Context, call CallMsg) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GasEstimator wraps EstimateGas, which tries to estimate the gas needed to execute a
|
||||||
|
// specific transaction based on the pending state. There is no guarantee that this is the
|
||||||
|
// true gas limit requirement as other transactions may be added or removed by miners, but
|
||||||
|
// it should provide a basis for setting a reasonable default.
|
||||||
|
type GasEstimator interface {
|
||||||
|
EstimateGas(ctx context.Context, call CallMsg) (usedGas *big.Int, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A PendingStateEventer provides access to real time notifications about changes to the
|
||||||
|
// pending state.
|
||||||
|
type PendingStateEventer interface {
|
||||||
|
SubscribePendingTransactions(ctx context.Context, ch chan<- *types.Transaction) (Subscription, error)
|
||||||
|
}
|
@ -588,24 +588,26 @@ func FormatLogs(structLogs []vm.StructLog) []StructLogRes {
|
|||||||
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
|
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
|
||||||
// transaction hashes.
|
// transaction hashes.
|
||||||
func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) {
|
func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) {
|
||||||
|
head := b.Header() // copies the header once
|
||||||
fields := map[string]interface{}{
|
fields := map[string]interface{}{
|
||||||
"number": rpc.NewHexNumber(b.Number()),
|
"number": rpc.NewHexNumber(head.Number),
|
||||||
"hash": b.Hash(),
|
"hash": b.Hash(),
|
||||||
"parentHash": b.ParentHash(),
|
"parentHash": head.ParentHash,
|
||||||
"nonce": b.Header().Nonce,
|
"nonce": head.Nonce,
|
||||||
"sha3Uncles": b.UncleHash(),
|
"mixHash": head.MixDigest,
|
||||||
"logsBloom": b.Bloom(),
|
"sha3Uncles": head.UncleHash,
|
||||||
"stateRoot": b.Root(),
|
"logsBloom": head.Bloom,
|
||||||
"miner": b.Coinbase(),
|
"stateRoot": head.Root,
|
||||||
"difficulty": rpc.NewHexNumber(b.Difficulty()),
|
"miner": head.Coinbase,
|
||||||
|
"difficulty": rpc.NewHexNumber(head.Difficulty),
|
||||||
"totalDifficulty": rpc.NewHexNumber(s.b.GetTd(b.Hash())),
|
"totalDifficulty": rpc.NewHexNumber(s.b.GetTd(b.Hash())),
|
||||||
"extraData": fmt.Sprintf("0x%x", b.Extra()),
|
"extraData": rpc.HexBytes(head.Extra),
|
||||||
"size": rpc.NewHexNumber(b.Size().Int64()),
|
"size": rpc.NewHexNumber(b.Size().Int64()),
|
||||||
"gasLimit": rpc.NewHexNumber(b.GasLimit()),
|
"gasLimit": rpc.NewHexNumber(head.GasLimit),
|
||||||
"gasUsed": rpc.NewHexNumber(b.GasUsed()),
|
"gasUsed": rpc.NewHexNumber(head.GasUsed),
|
||||||
"timestamp": rpc.NewHexNumber(b.Time()),
|
"timestamp": rpc.NewHexNumber(head.Time),
|
||||||
"transactionsRoot": b.TxHash(),
|
"transactionsRoot": head.TxHash,
|
||||||
"receiptRoot": b.ReceiptHash(),
|
"receiptRoot": head.ReceiptHash,
|
||||||
}
|
}
|
||||||
|
|
||||||
if inclTx {
|
if inclTx {
|
||||||
@ -648,26 +650,32 @@ type RPCTransaction struct {
|
|||||||
Gas *rpc.HexNumber `json:"gas"`
|
Gas *rpc.HexNumber `json:"gas"`
|
||||||
GasPrice *rpc.HexNumber `json:"gasPrice"`
|
GasPrice *rpc.HexNumber `json:"gasPrice"`
|
||||||
Hash common.Hash `json:"hash"`
|
Hash common.Hash `json:"hash"`
|
||||||
Input string `json:"input"`
|
Input rpc.HexBytes `json:"input"`
|
||||||
Nonce *rpc.HexNumber `json:"nonce"`
|
Nonce *rpc.HexNumber `json:"nonce"`
|
||||||
To *common.Address `json:"to"`
|
To *common.Address `json:"to"`
|
||||||
TransactionIndex *rpc.HexNumber `json:"transactionIndex"`
|
TransactionIndex *rpc.HexNumber `json:"transactionIndex"`
|
||||||
Value *rpc.HexNumber `json:"value"`
|
Value *rpc.HexNumber `json:"value"`
|
||||||
|
V *rpc.HexNumber `json:"v"`
|
||||||
|
R *rpc.HexNumber `json:"r"`
|
||||||
|
S *rpc.HexNumber `json:"s"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation
|
// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation
|
||||||
func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction {
|
func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction {
|
||||||
from, _ := tx.FromFrontier()
|
from, _ := tx.FromFrontier()
|
||||||
|
v, r, s := tx.SignatureValues()
|
||||||
return &RPCTransaction{
|
return &RPCTransaction{
|
||||||
From: from,
|
From: from,
|
||||||
Gas: rpc.NewHexNumber(tx.Gas()),
|
Gas: rpc.NewHexNumber(tx.Gas()),
|
||||||
GasPrice: rpc.NewHexNumber(tx.GasPrice()),
|
GasPrice: rpc.NewHexNumber(tx.GasPrice()),
|
||||||
Hash: tx.Hash(),
|
Hash: tx.Hash(),
|
||||||
Input: fmt.Sprintf("0x%x", tx.Data()),
|
Input: rpc.HexBytes(tx.Data()),
|
||||||
Nonce: rpc.NewHexNumber(tx.Nonce()),
|
Nonce: rpc.NewHexNumber(tx.Nonce()),
|
||||||
To: tx.To(),
|
To: tx.To(),
|
||||||
Value: rpc.NewHexNumber(tx.Value()),
|
Value: rpc.NewHexNumber(tx.Value()),
|
||||||
|
V: rpc.NewHexNumber(v),
|
||||||
|
R: rpc.NewHexNumber(r),
|
||||||
|
S: rpc.NewHexNumber(s),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -679,7 +687,7 @@ func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) (*RPCTransacti
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
v, r, s := tx.SignatureValues()
|
||||||
return &RPCTransaction{
|
return &RPCTransaction{
|
||||||
BlockHash: b.Hash(),
|
BlockHash: b.Hash(),
|
||||||
BlockNumber: rpc.NewHexNumber(b.Number()),
|
BlockNumber: rpc.NewHexNumber(b.Number()),
|
||||||
@ -687,11 +695,14 @@ func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) (*RPCTransacti
|
|||||||
Gas: rpc.NewHexNumber(tx.Gas()),
|
Gas: rpc.NewHexNumber(tx.Gas()),
|
||||||
GasPrice: rpc.NewHexNumber(tx.GasPrice()),
|
GasPrice: rpc.NewHexNumber(tx.GasPrice()),
|
||||||
Hash: tx.Hash(),
|
Hash: tx.Hash(),
|
||||||
Input: fmt.Sprintf("0x%x", tx.Data()),
|
Input: rpc.HexBytes(tx.Data()),
|
||||||
Nonce: rpc.NewHexNumber(tx.Nonce()),
|
Nonce: rpc.NewHexNumber(tx.Nonce()),
|
||||||
To: tx.To(),
|
To: tx.To(),
|
||||||
TransactionIndex: rpc.NewHexNumber(txIndex),
|
TransactionIndex: rpc.NewHexNumber(txIndex),
|
||||||
Value: rpc.NewHexNumber(tx.Value()),
|
Value: rpc.NewHexNumber(tx.Value()),
|
||||||
|
V: rpc.NewHexNumber(v),
|
||||||
|
R: rpc.NewHexNumber(r),
|
||||||
|
S: rpc.NewHexNumber(s),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -861,7 +872,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma
|
|||||||
}
|
}
|
||||||
|
|
||||||
fields := map[string]interface{}{
|
fields := map[string]interface{}{
|
||||||
"root": common.Bytes2Hex(receipt.PostState),
|
"root": rpc.HexBytes(receipt.PostState),
|
||||||
"blockHash": txBlock,
|
"blockHash": txBlock,
|
||||||
"blockNumber": rpc.NewHexNumber(blockIndex),
|
"blockNumber": rpc.NewHexNumber(blockIndex),
|
||||||
"transactionHash": txHash,
|
"transactionHash": txHash,
|
||||||
@ -872,17 +883,15 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma
|
|||||||
"cumulativeGasUsed": rpc.NewHexNumber(receipt.CumulativeGasUsed),
|
"cumulativeGasUsed": rpc.NewHexNumber(receipt.CumulativeGasUsed),
|
||||||
"contractAddress": nil,
|
"contractAddress": nil,
|
||||||
"logs": receipt.Logs,
|
"logs": receipt.Logs,
|
||||||
|
"logsBloom": receipt.Bloom,
|
||||||
}
|
}
|
||||||
|
|
||||||
if receipt.Logs == nil {
|
if receipt.Logs == nil {
|
||||||
fields["logs"] = []vm.Logs{}
|
fields["logs"] = []vm.Logs{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
|
// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
|
||||||
if bytes.Compare(receipt.ContractAddress.Bytes(), bytes.Repeat([]byte{0}, 20)) != 0 {
|
if receipt.ContractAddress != (common.Address{}) {
|
||||||
fields["contractAddress"] = receipt.ContractAddress
|
fields["contractAddress"] = receipt.ContractAddress
|
||||||
}
|
}
|
||||||
|
|
||||||
return fields, nil
|
return fields, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
30
rpc/types.go
30
rpc/types.go
@ -17,6 +17,8 @@
|
|||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
@ -272,3 +274,31 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
|
|||||||
func (bn BlockNumber) Int64() int64 {
|
func (bn BlockNumber) Int64() int64 {
|
||||||
return (int64)(bn)
|
return (int64)(bn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HexBytes JSON-encodes as hex with 0x prefix.
|
||||||
|
type HexBytes []byte
|
||||||
|
|
||||||
|
func (b HexBytes) MarshalJSON() ([]byte, error) {
|
||||||
|
result := make([]byte, len(b)*2+4)
|
||||||
|
copy(result, `"0x`)
|
||||||
|
hex.Encode(result[3:], b)
|
||||||
|
result[len(result)-1] = '"'
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *HexBytes) UnmarshalJSON(input []byte) error {
|
||||||
|
if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' {
|
||||||
|
input = input[1 : len(input)-1]
|
||||||
|
}
|
||||||
|
if !bytes.HasPrefix(input, []byte("0x")) {
|
||||||
|
return fmt.Errorf("missing 0x prefix for hex byte array")
|
||||||
|
}
|
||||||
|
input = input[2:]
|
||||||
|
if len(input) == 0 {
|
||||||
|
*b = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
*b = make([]byte, len(input)/2)
|
||||||
|
_, err := hex.Decode(*b, input)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
@ -71,3 +71,25 @@ func TestHexNumberMarshalJSON(t *testing.T) {
|
|||||||
t.Fatalf("Invalid json.Marshal, expected '%s', got '%s'", exp, got)
|
t.Fatalf("Invalid json.Marshal, expected '%s', got '%s'", exp, got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var hexBytesTests = []struct{ in, out []byte }{
|
||||||
|
{in: []byte(`"0x"`), out: []byte{}},
|
||||||
|
{in: []byte(`"0x00"`), out: []byte{0}},
|
||||||
|
{in: []byte(`"0x01ff"`), out: []byte{0x01, 0xFF}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHexBytes(t *testing.T) {
|
||||||
|
for i, test := range hexBytesTests {
|
||||||
|
var dec HexBytes
|
||||||
|
if err := json.Unmarshal(test.in, &dec); err != nil {
|
||||||
|
t.Fatalf("test %d: can't decode: %v", i, err)
|
||||||
|
}
|
||||||
|
enc, _ := json.Marshal(HexBytes(test.out))
|
||||||
|
if !bytes.Equal(dec, test.out) {
|
||||||
|
t.Errorf("test %d: wrong decoded value 0x%x", i, dec)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(enc, test.in) {
|
||||||
|
t.Errorf("test %d: wrong encoded value %#q", i, enc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user