Commit 5fc032a9 authored by Péter Szilágyi's avatar Péter Szilágyi Committed by GitHub

Merge pull request #2930 from fjl/ethclient

Stable Go API, part 1
parents 806e3cd0 c97df052
......@@ -20,28 +20,52 @@ import (
"errors"
"math/big"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"golang.org/x/net/context"
)
// ErrNoCode is returned by call and transact operations for which the requested
// recipient contract to operate on does not exist in the state db or does not
// have any code associated with it (i.e. suicided).
var ErrNoCode = errors.New("no contract code at given address")
var (
// ErrNoCode is returned by call and transact operations for which the requested
// recipient contract to operate on does not exist in the state db or does not
// 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
// only basis.
type ContractCaller interface {
// 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)
// CodeAt returns the code of the given account. This is needed to differentiate
// between contract internal errors and the local chain being out of sync.
CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, 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
// 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)
// DeployBackend wraps the operations needed by WaitMined and WaitDeployed.
type DeployBackend interface {
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, 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
......@@ -49,64 +73,25 @@ type ContractCaller interface {
// used when the user does not provide some needed values, but rather leaves it up
// to the transactor to decide.
type ContractTransactor interface {
// PendingAccountNonce retrieves the current pending nonce associated with an
// account.
PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error)
// PendingCodeAt returns the code of the given account in the pending state.
PendingCodeAt(ctx context.Context, account common.Address) ([]byte, 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
// execution of a transaction.
SuggestGasPrice(ctx context.Context) (*big.Int, error)
// 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
// 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.
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(ctx context.Context, tx *types.Transaction) error
}
// ContractBackend defines the methods needed to allow operating with contract
// 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.
// ContractBackend defines the methods needed to work with contracts on a read-write basis.
type ContractBackend interface {
// 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)
// 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
ContractCaller
ContractTransactor
}
// 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))
}
This diff is collapsed.
......@@ -20,8 +20,8 @@ import (
"errors"
"fmt"
"math/big"
"sync/atomic"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"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
caller ContractCaller // Read 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
......@@ -105,25 +102,42 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string,
if opts == nil {
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
input, err := c.abi.Pack(method, params...)
if err != nil {
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 {
return err
}
......@@ -158,7 +172,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
}
nonce := uint64(0)
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 {
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
gasPrice := opts.GasPrice
if gasPrice == nil {
gasPrice, err = c.transactor.SuggestGasPrice(opts.Context)
gasPrice, err = c.transactor.SuggestGasPrice(ensureContext(opts.Context))
if err != nil {
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
if gasLimit == nil {
// Gas estimation cannot succeed without code for method invocations
if contract != nil && atomic.LoadUint32(&c.pendingHasCode) == 0 {
if code, err := c.transactor.HasCode(opts.Context, c.address, true); err != nil {
if contract != nil {
if code, err := c.transactor.PendingCodeAt(ensureContext(opts.Context), c.address); err != nil {
return nil, err
} else if !code {
} else if len(code) == 0 {
return nil, ErrNoCode
}
atomic.StoreUint32(&c.pendingHasCode, 1)
}
// 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 {
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
......@@ -204,8 +218,15 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
if err != nil {
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 signedTx, nil
}
func ensureContext(ctx context.Context) context.Context {
if ctx == nil {
return context.TODO()
}
return ctx
}
This diff is collapsed.
......@@ -127,7 +127,7 @@ package {{.Package}}
// 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) {
contract, err := bind{{.Type}}(address, backend.(bind.ContractCaller), backend.(bind.ContractTransactor))
contract, err := bind{{.Type}}(address, backend, backend)
if err != nil {
return nil, err
}
......
......@@ -14,44 +14,63 @@
// 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
package bind
import (
"math/big"
"fmt"
"time"
"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/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"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")
// 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:
}
}
}
// 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)
// 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
}
// 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
import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"math/big"
......@@ -33,25 +33,53 @@ import (
"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
// mix-hash) that a sufficient amount of computation has been carried
// out on a block.
type BlockNonce [8]byte
// EncodeNonce converts the given integer to a block nonce.
func EncodeNonce(i uint64) BlockNonce {
var n BlockNonce
binary.BigEndian.PutUint64(n[:], i)
return n
}
// Uint64 returns the integer value of a block nonce.
func (n BlockNonce) Uint64() uint64 {
return binary.BigEndian.Uint64(n[:])
}
// MarshalJSON implements json.Marshaler
func (n BlockNonce) MarshalJSON() ([]byte, error) {
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 {
ParentHash common.Hash // Hash to the previous block
UncleHash common.Hash // Uncles of this block
......@@ -70,10 +98,31 @@ type Header struct {
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 {
return rlpHash(h)
}
// HashNoNonce returns the hash which is used as input for the proof-of-work search.
func (h *Header) HashNoNonce() common.Hash {
return rlpHash([]interface{}{
h.ParentHash,
......@@ -92,48 +141,63 @@ func (h *Header) HashNoNonce() common.Hash {
})
}
func (h *Header) UnmarshalJSON(data []byte) error {
var ext struct {
ParentHash string
Coinbase string
Difficulty string
GasLimit string
Time *big.Int
Extra string
}
dec := json.NewDecoder(bytes.NewReader(data))
if err := dec.Decode(&ext); err != nil {
return err
}
h.ParentHash = common.HexToHash(ext.ParentHash)
h.Coinbase = common.HexToAddress(ext.Coinbase)
h.Difficulty = common.String2Big(ext.Difficulty)
h.Time = ext.Time
h.Extra = []byte(ext.Extra)
return nil
// MarshalJSON encodes headers into the web3 RPC response block format.
func (h *Header) MarshalJSON() ([]byte, error) {
return json.Marshal(&jsonHeader{
ParentHash: &h.ParentHash,
UncleHash: &h.UncleHash,
Coinbase: &h.Coinbase,
Root: &h.Root,
TxHash: &h.TxHash,
ReceiptHash: &h.ReceiptHash,
Bloom: &h.Bloom,
Difficulty: (*hexBig)(h.Difficulty),
Number: (*hexBig)(h.Number),
GasLimit: (*hexBig)(h.GasLimit),
GasUsed: (*hexBig)(h.GasUsed),
Time: (*hexBig)(h.Time),
Extra: (*hexBytes)(&h.Extra),
MixDigest: &h.MixDigest,
Nonce: &h.Nonce,
})
}
func (h *Header) MarshalJSON() ([]byte, error) {
fields := map[string]interface{}{
"hash": h.Hash(),
"parentHash": h.ParentHash,
"number": fmt.Sprintf("%#x", h.Number),
"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,
// UnmarshalJSON decodes headers from the web3 RPC response block format.
func (h *Header) UnmarshalJSON(input []byte) error {
var dec jsonHeader
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
return json.Marshal(fields)
// Ensure that all fields are set. MixDigest is checked separately because
// 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) {
......@@ -150,6 +214,7 @@ type Body struct {
Uncles []*Header
}
// Block represents a block in the Ethereum blockchain.
type Block struct {
header *Header
uncles []*Header
......@@ -198,11 +263,6 @@ type storageblock struct {
TD *big.Int
}
var (
EmptyRootHash = DeriveSha(Transactions{})
EmptyUncleHash = CalcUncleHash(nil)
)
// NewBlock creates a new block. The input data is copied,
// changes to header and to the field values will not affect the
// block.
......@@ -275,23 +335,7 @@ func CopyHeader(h *Header) *Header {
return &cpy
}
func (b *Block) ValidateFields() error {
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
}
// DecodeRLP decodes the Ethereum
func (b *Block) DecodeRLP(s *rlp.Stream) error {
var eb extblock
_, size, _ := s.Kind()
......@@ -303,6 +347,7 @@ func (b *Block) DecodeRLP(s *rlp.Stream) error {
return nil
}
// EncodeRLP serializes b into the Ethereum RLP block format.
func (b *Block) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, extblock{
Header: b.header,
......@@ -322,6 +367,7 @@ func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error {
}
// TODO: copies
func (b *Block) Uncles() []*Header { return b.uncles }
func (b *Block) Transactions() Transactions { return b.transactions }
......@@ -409,8 +455,8 @@ func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *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 {
if hash := b.hash.Load(); hash != nil {
return hash.(common.Hash)
......
......@@ -31,28 +31,34 @@ type bytesBacked interface {
const bloomLength = 256
// Bloom represents a 256 bit bloom filter.
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 {
var bloom Bloom
bloom.SetBytes(b)
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) {
if len(b) < len(d) {
panic(fmt.Sprintf("bloom bytes too big %d %d", len(b), len(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) {
bin := new(big.Int).SetBytes(b[:])
bin.Or(bin, bloom9(d.Bytes()))
b.SetBytes(bin.Bytes())
}
// Big converts b to a big integer.
func (b Bloom) Big() *big.Int {
return common.Bytes2Big(b[:])
}
......@@ -69,8 +75,22 @@ func (b Bloom) TestBytes(test []byte) bool {
return b.Test(common.BytesToBig(test))
}
// MarshalJSON encodes b as a hex string with 0x prefix.
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 {
......
// 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
}
This diff is collapsed.
......@@ -17,6 +17,8 @@
package types
import (
"encoding/json"
"errors"
"fmt"
"io"
"math/big"
......@@ -26,6 +28,11 @@ import (
"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.
type Receipt struct {
// Consensus fields
......@@ -34,12 +41,22 @@ type Receipt struct {
Bloom Bloom
Logs vm.Logs
// Implementation fields
// Implementation fields (don't reorder!)
TxHash common.Hash
ContractAddress common.Address
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.
func NewReceipt(root []byte, cumulativeGasUsed *big.Int) *Receipt {
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
}
// RlpEncode implements common.RlpEncode required for SHA3 derivation.
func (r *Receipt) RlpEncode() []byte {
bytes, err := rlp.EncodeToBytes(r)
if err != nil {
panic(err)
// UnmarshalJSON decodes the web3 RPC receipt format.
func (r *Receipt) UnmarshalJSON(input []byte) error {
var dec jsonReceipt
if err := json.Unmarshal(input, &dec); err != nil {
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.
......@@ -122,7 +160,7 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error {
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
// Len returns the number of receipts in this list.
......
......@@ -19,6 +19,7 @@ package types
import (
"container/heap"
"crypto/ecdsa"
"encoding/json"
"errors"
"fmt"
"io"
......@@ -28,12 +29,15 @@ import (
"github.com/ethereum/go-ethereum/common"
"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"
)
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 {
data txdata
......@@ -53,6 +57,20 @@ type txdata struct {
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 {
if len(data) > 0 {
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 {
if len(data) > 0 {
data = common.CopyBytes(data)
......@@ -95,10 +114,12 @@ func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice
return &Transaction{data: d}
}
// DecodeRLP implements rlp.Encoder
func (tx *Transaction) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, &tx.data)
}
// DecodeRLP implements rlp.Decoder
func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
_, size, _ := s.Kind()
err := s.Decode(&tx.data)
......@@ -108,6 +129,42 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
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) 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) }
......@@ -215,6 +272,7 @@ func (tx *Transaction) Cost() *big.Int {
return total
}
// SignatureValues returns the ECDSA signature values contained in the transaction.
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)
}
......@@ -235,7 +293,6 @@ func (tx *Transaction) publicKey(homestead bool) ([]byte, error) {
hash := tx.SigHash()
pub, err := crypto.Ecrecover(hash[:], sig)
if err != nil {
glog.V(logger.Error).Infof("Could not get pubkey from signature: ", err)
return nil, err
}
if len(pub) == 0 || pub[0] != 4 {
......
......@@ -18,6 +18,7 @@ package vm
import (
"encoding/json"
"errors"
"fmt"
"io"
......@@ -25,18 +26,33 @@ import (
"github.com/ethereum/go-ethereum/rlp"
)
var errMissingLogFields = errors.New("missing required JSON log fields")
// Log represents a contract log event. These events are generated by the LOG
// opcode and stored/indexed by the node.
type Log struct {
// Consensus fields
Address common.Address
Topics []common.Hash
Data []byte
// Consensus fields.
Address common.Address // address of the contract that generated the event
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
}
// Derived fields (don't reorder!)
BlockNumber uint64
TxHash common.Hash
TxIndex uint
BlockHash common.Hash
Index uint
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 {
......@@ -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)
}
// MarshalJSON implements json.Marshaler.
func (r *Log) MarshalJSON() ([]byte, error) {
fields := map[string]interface{}{
"address": r.Address,
"data": fmt.Sprintf("%#x", r.Data),
"blockNumber": fmt.Sprintf("%#x", r.BlockNumber),
"logIndex": fmt.Sprintf("%#x", r.Index),
"blockHash": r.BlockHash,
"transactionHash": r.TxHash,
"transactionIndex": fmt.Sprintf("%#x", r.TxIndex),
"topics": r.Topics,
}
return json.Marshal(&jsonLog{
Address: &r.Address,
Topics: &r.Topics,
Data: fmt.Sprintf("0x%x", r.Data),
BlockNumber: fmt.Sprintf("0x%x", r.BlockNumber),
TxIndex: fmt.Sprintf("0x%x", r.TxIndex),
TxHash: &r.TxHash,
BlockHash: &r.BlockHash,
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
......
// 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
}
......@@ -19,6 +19,7 @@ package eth
import (
"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/internal/ethapi"
......@@ -50,47 +51,62 @@ func NewContractBackend(eth *Ethereum) *ContractBackend {
}
}
// HasCode implements bind.ContractVerifier.HasCode by retrieving any code associated
// with the contract from the local API, and checking its size.
func (b *ContractBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) {
if ctx == nil {
ctx = context.Background()
}
block := rpc.LatestBlockNumber
if pending {
block = rpc.PendingBlockNumber
}
out, err := b.bcapi.GetCode(ctx, contract, block)
return len(common.FromHex(out)) > 0, err
// CodeAt retrieves any code associated with the contract from the local API.
func (b *ContractBackend) CodeAt(ctx context.Context, contract common.Address, blockNum *big.Int) ([]byte, error) {
out, err := b.bcapi.GetCode(ctx, contract, toBlockNumber(blockNum))
return common.FromHex(out), err
}
// CodeAt retrieves any code associated with the contract from the local API.
func (b *ContractBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
out, err := b.bcapi.GetCode(ctx, contract, rpc.PendingBlockNumber)
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) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) {
if ctx == nil {
ctx = context.Background()
}
// Convert the input args to the API spec
func (b *ContractBackend) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNum *big.Int) ([]byte, error) {
out, err := b.bcapi.Call(ctx, toCallArgs(msg), toBlockNumber(blockNum))
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: &contract,
Data: common.ToHex(data),
To: msg.To,
From: msg.From,
Data: common.ToHex(msg.Data),
}
block := rpc.LatestBlockNumber
if pending {
block = rpc.PendingBlockNumber
if msg.Gas != nil {
args.Gas = *rpc.NewHexNumber(msg.Gas)
}
// Execute the call and convert the output back to Go types
out, err := b.bcapi.Call(ctx, args, block)
return common.FromHex(out), err
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
// pending nonce associated with an account.
func (b *ContractBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) {
if ctx == nil {
ctx = context.Background()
}
func (b *ContractBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
out, err := b.txapi.GetTransactionCount(ctx, account, rpc.PendingBlockNumber)
return out.Uint64(), err
}
......@@ -98,9 +114,6 @@ func (b *ContractBackend) PendingAccountNonce(ctx context.Context, account commo
// SuggestGasPrice implements bind.ContractTransactor retrieving the currently
// suggested gas price to allow a timely execution of a transaction.
func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
if ctx == nil {
ctx = context.Background()
}
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
// requirement as other transactions may be added or removed by miners, but it
// 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) {
if ctx == nil {
ctx = context.Background()
}
out, err := b.bcapi.EstimateGas(ctx, ethapi.CallArgs{
From: sender,
To: contract,
Value: *rpc.NewHexNumber(value),
Data: common.ToHex(data),
})
func (b *ContractBackend) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (*big.Int, error) {
out, err := b.bcapi.EstimateGas(ctx, toCallArgs(msg))
return out.BigInt(), err
}
// SendTransaction implements bind.ContractTransactor injects the transaction
// into the pending pool for execution.
func (b *ContractBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
if ctx == nil {
ctx = context.Background()
}
raw, _ := rlp.EncodeToBytes(tx)
_, err := b.txapi.SendRawTransaction(ctx, common.ToHex(raw))
return err
......
......@@ -634,9 +634,6 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
if err := msg.Decode(&request); err != nil {
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.ReceivedFrom = p
......
This diff is collapsed.
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{})
)
// 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
// transaction hashes.
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{}{
"number": rpc.NewHexNumber(b.Number()),
"number": rpc.NewHexNumber(head.Number),
"hash": b.Hash(),
"parentHash": b.ParentHash(),
"nonce": b.Header().Nonce,
"sha3Uncles": b.UncleHash(),
"logsBloom": b.Bloom(),
"stateRoot": b.Root(),
"miner": b.Coinbase(),
"difficulty": rpc.NewHexNumber(b.Difficulty()),
"parentHash": head.ParentHash,
"nonce": head.Nonce,
"mixHash": head.MixDigest,
"sha3Uncles": head.UncleHash,
"logsBloom": head.Bloom,
"stateRoot": head.Root,
"miner": head.Coinbase,
"difficulty": rpc.NewHexNumber(head.Difficulty),
"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()),
"gasLimit": rpc.NewHexNumber(b.GasLimit()),
"gasUsed": rpc.NewHexNumber(b.GasUsed()),
"timestamp": rpc.NewHexNumber(b.Time()),
"transactionsRoot": b.TxHash(),
"receiptRoot": b.ReceiptHash(),
"gasLimit": rpc.NewHexNumber(head.GasLimit),
"gasUsed": rpc.NewHexNumber(head.GasUsed),
"timestamp": rpc.NewHexNumber(head.Time),
"transactionsRoot": head.TxHash,
"receiptRoot": head.ReceiptHash,
}
if inclTx {
......@@ -648,26 +650,32 @@ type RPCTransaction struct {
Gas *rpc.HexNumber `json:"gas"`
GasPrice *rpc.HexNumber `json:"gasPrice"`
Hash common.Hash `json:"hash"`
Input string `json:"input"`
Input rpc.HexBytes `json:"input"`
Nonce *rpc.HexNumber `json:"nonce"`
To *common.Address `json:"to"`
TransactionIndex *rpc.HexNumber `json:"transactionIndex"`
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
func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction {
from, _ := tx.FromFrontier()
v, r, s := tx.SignatureValues()
return &RPCTransaction{
From: from,
Gas: rpc.NewHexNumber(tx.Gas()),
GasPrice: rpc.NewHexNumber(tx.GasPrice()),
Hash: tx.Hash(),
Input: fmt.Sprintf("0x%x", tx.Data()),
Input: rpc.HexBytes(tx.Data()),
Nonce: rpc.NewHexNumber(tx.Nonce()),
To: tx.To(),
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 {
return nil, err
}
v, r, s := tx.SignatureValues()
return &RPCTransaction{
BlockHash: b.Hash(),
BlockNumber: rpc.NewHexNumber(b.Number()),
......@@ -687,11 +695,14 @@ func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) (*RPCTransacti
Gas: rpc.NewHexNumber(tx.Gas()),
GasPrice: rpc.NewHexNumber(tx.GasPrice()),
Hash: tx.Hash(),
Input: fmt.Sprintf("0x%x", tx.Data()),
Input: rpc.HexBytes(tx.Data()),
Nonce: rpc.NewHexNumber(tx.Nonce()),
To: tx.To(),
TransactionIndex: rpc.NewHexNumber(txIndex),
Value: rpc.NewHexNumber(tx.Value()),
V: rpc.NewHexNumber(v),
R: rpc.NewHexNumber(r),
S: rpc.NewHexNumber(s),
}, nil
}
......@@ -861,7 +872,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma
}
fields := map[string]interface{}{
"root": common.Bytes2Hex(receipt.PostState),
"root": rpc.HexBytes(receipt.PostState),
"blockHash": txBlock,
"blockNumber": rpc.NewHexNumber(blockIndex),
"transactionHash": txHash,
......@@ -872,17 +883,15 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma
"cumulativeGasUsed": rpc.NewHexNumber(receipt.CumulativeGasUsed),
"contractAddress": nil,
"logs": receipt.Logs,
"logsBloom": receipt.Bloom,
}
if receipt.Logs == nil {
fields["logs"] = []vm.Logs{}
}
// 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
}
return fields, nil
}
......
......@@ -17,6 +17,8 @@
package rpc
import (
"bytes"
"encoding/hex"
"fmt"
"math"
"math/big"
......@@ -272,3 +274,31 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
func (bn BlockNumber) Int64() int64 {
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)
}
}
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)
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment