From 0c9a858f2d8094d48371deee97493a1b1412e7de Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 4 Aug 2016 03:30:05 +0200 Subject: eth: don't call ValidateFields ValidateFields was introduced before the rlp decoder disallowed nil values. Decoding RLP will never return nil values, there is no need to check for them. --- eth/handler.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/eth/handler.go b/eth/handler.go index 886d89fd1..570c79dac 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -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 -- cgit v1.2.3 From 704fde01e8ed28877fe0f69fc8059b1f0c850d04 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 4 Aug 2016 03:55:33 +0200 Subject: core/types, core/vm: improve docs, add JSON marshaling methods In this commit, core/types's types learn how to encode and decode themselves as JSON. The encoding is very similar to what the RPC API uses. The RPC API is missing some output fields (e.g. transaction signature values) which will be added to the API in a later commit. Some fields that the API generates are ignored by the decoder methods here. --- core/types/block.go | 174 +++++++++++++++++++++++++++++----------------- core/types/bloom9.go | 24 ++++++- core/types/json.go | 87 +++++++++++++++++++++++ core/types/json_test.go | 136 ++++++++++++++++++++++++++++++++++++ core/types/receipt.go | 54 +++++++++++--- core/types/transaction.go | 65 +++++++++++++++-- core/vm/log.go | 89 ++++++++++++++++++------ core/vm/log_test.go | 59 ++++++++++++++++ 8 files changed, 589 insertions(+), 99 deletions(-) create mode 100644 core/types/json.go create mode 100644 core/types/json_test.go create mode 100644 core/vm/log_test.go diff --git a/core/types/block.go b/core/types/block.go index 599359247..559fbdd20 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -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) diff --git a/core/types/bloom9.go b/core/types/bloom9.go index ecf2bffc2..d3945a734 100644 --- a/core/types/bloom9.go +++ b/core/types/bloom9.go @@ -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 { diff --git a/core/types/json.go b/core/types/json.go new file mode 100644 index 000000000..403e79899 --- /dev/null +++ b/core/types/json.go @@ -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 . + +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 +} diff --git a/core/types/json_test.go b/core/types/json_test.go new file mode 100644 index 000000000..5f422b873 --- /dev/null +++ b/core/types/json_test.go @@ -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 +} diff --git a/core/types/receipt.go b/core/types/receipt.go index 5f847fc5c..9f820eb18 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -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. diff --git a/core/types/transaction.go b/core/types/transaction.go index c71c98aa7..d369d7772 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -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 { diff --git a/core/vm/log.go b/core/vm/log.go index e4cc6021b..b292f5f43 100644 --- a/core/vm/log.go +++ b/core/vm/log.go @@ -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 diff --git a/core/vm/log_test.go b/core/vm/log_test.go new file mode 100644 index 000000000..775016f9c --- /dev/null +++ b/core/vm/log_test.go @@ -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 . + +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 +} -- cgit v1.2.3 From b0d9f7372a04fa8f0ffc391f0997e2e062d465ef Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Thu, 4 Aug 2016 01:40:50 +0200 Subject: internal/ethapi: add missing output fields - returned headers didn't include mixHash - returned transactions didn't include signature fields - empty transaction input was returned as "", but should be "0x" - returned receipts didn't include the bloom filter - "root" in receipts was missing 0x prefix --- internal/ethapi/api.go | 57 +++++++++++++++++++++++++++++--------------------- rpc/types.go | 30 ++++++++++++++++++++++++++ rpc/types_test.go | 22 +++++++++++++++++++ 3 files changed, 85 insertions(+), 24 deletions(-) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 135f9f8e8..184b5831f 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -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 } diff --git a/rpc/types.go b/rpc/types.go index 89c5b5bc9..ebe388373 100644 --- a/rpc/types.go +++ b/rpc/types.go @@ -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 +} diff --git a/rpc/types_test.go b/rpc/types_test.go index c2c5c6db6..5482557b8 100644 --- a/rpc/types_test.go +++ b/rpc/types_test.go @@ -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) + } + } +} -- cgit v1.2.3 From 8d9141ed9a3dec321ffd184a039d1959c3257ddd Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 5 Aug 2016 13:25:52 +0200 Subject: ethereum: add new Go API interfaces --- interfaces.go | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 interfaces.go diff --git a/interfaces.go b/interfaces.go new file mode 100644 index 000000000..921d02616 --- /dev/null +++ b/interfaces.go @@ -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 . + +// 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) +} -- cgit v1.2.3 From 46621fd2c3e06d3e5932afe29a6e7e4016a16635 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Wed, 10 Aug 2016 00:01:13 +0200 Subject: ethclient: add initial implementation of the typed RPC client ethclient implements the proposed Ethereum Go API. There are no tests at the moment, a suite that excercises all implementations of the API will be added later. --- ethclient/ethclient.go | 382 ++++++++++++++++++++++++++++++++++++++++++++ ethclient/ethclient_test.go | 17 ++ 2 files changed, 399 insertions(+) create mode 100644 ethclient/ethclient.go create mode 100644 ethclient/ethclient_test.go diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go new file mode 100644 index 000000000..575a7cb44 --- /dev/null +++ b/ethclient/ethclient.go @@ -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 . + +// 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 +} diff --git a/ethclient/ethclient_test.go b/ethclient/ethclient_test.go new file mode 100644 index 000000000..47e37c0ce --- /dev/null +++ b/ethclient/ethclient_test.go @@ -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{}) +) -- cgit v1.2.3 From 056f15aa53470e6cc7b1054f72165f5646ce7e73 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Sun, 21 Aug 2016 17:06:25 +0200 Subject: accounts/abi/bind/backends: remove nil and remote backends The remote backend is superseded by ethclient. The nil backend's stated purpose was to enable testing of accounts/abi/bind. None of its methods actually worked. A much simpler way to get a crashing backend is to simply pass nil as the backend. With a one-line change to the generator (removing two explicit interface assertions), passing nil actually works. Removing these backends means that less changes are required later. --- accounts/abi/bind/backends/nil.go | 57 --------------- accounts/abi/bind/backends/remote.go | 136 ----------------------------------- accounts/abi/bind/bind_test.go | 18 ++--- accounts/abi/bind/template.go | 2 +- 4 files changed, 10 insertions(+), 203 deletions(-) delete mode 100644 accounts/abi/bind/backends/nil.go delete mode 100644 accounts/abi/bind/backends/remote.go diff --git a/accounts/abi/bind/backends/nil.go b/accounts/abi/bind/backends/nil.go deleted file mode 100644 index 54b222f1f..000000000 --- a/accounts/abi/bind/backends/nil.go +++ /dev/null @@ -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 . - -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) -} diff --git a/accounts/abi/bind/backends/remote.go b/accounts/abi/bind/backends/remote.go deleted file mode 100644 index 58edd791a..000000000 --- a/accounts/abi/bind/backends/remote.go +++ /dev/null @@ -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 . - -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)) -} diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index a80560821..67c09c3ad 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -44,13 +44,13 @@ var bindTests = []struct { `606060405260068060106000396000f3606060405200`, `[]`, ` - if b, err := NewEmpty(common.Address{}, backends.NewNilBackend()); b == nil || err != nil { + if b, err := NewEmpty(common.Address{}, nil); b == nil || err != nil { t.Fatalf("combined binding (%v) nil or error (%v) not nil", b, nil) } - if b, err := NewEmptyCaller(common.Address{}, backends.NewNilBackend()); b == nil || err != nil { + if b, err := NewEmptyCaller(common.Address{}, nil); b == nil || err != nil { t.Fatalf("caller binding (%v) nil or error (%v) not nil", b, nil) } - if b, err := NewEmptyTransactor(common.Address{}, backends.NewNilBackend()); b == nil || err != nil { + if b, err := NewEmptyTransactor(common.Address{}, nil); b == nil || err != nil { t.Fatalf("transactor binding (%v) nil or error (%v) not nil", b, nil) } `, @@ -62,7 +62,7 @@ var bindTests = []struct { `60606040526040516107fd3803806107fd83398101604052805160805160a05160c051929391820192909101600160a060020a0333166000908152600360209081526040822086905581548551838052601f6002600019610100600186161502019093169290920482018390047f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56390810193919290918801908390106100e857805160ff19168380011785555b506101189291505b8082111561017157600081556001016100b4565b50506002805460ff19168317905550505050610658806101a56000396000f35b828001600101855582156100ac579182015b828111156100ac5782518260005055916020019190600101906100fa565b50508060016000509080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061017557805160ff19168380011785555b506100c89291506100b4565b5090565b82800160010185558215610165579182015b8281111561016557825182600050559160200191906001019061018756606060405236156100775760e060020a600035046306fdde03811461007f57806323b872dd146100dc578063313ce5671461010e57806370a082311461011a57806395d89b4114610132578063a9059cbb1461018e578063cae9ca51146101bd578063dc3080f21461031c578063dd62ed3e14610341575b610365610002565b61036760008054602060026001831615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156104eb5780601f106104c0576101008083540402835291602001916104eb565b6103d5600435602435604435600160a060020a038316600090815260036020526040812054829010156104f357610002565b6103e760025460ff1681565b6103d560043560036020526000908152604090205481565b610367600180546020600282841615610100026000190190921691909104601f810182900490910260809081016040526060828152929190828280156104eb5780601f106104c0576101008083540402835291602001916104eb565b610365600435602435600160a060020a033316600090815260036020526040902054819010156103f157610002565b60806020604435600481810135601f8101849004909302840160405260608381526103d5948235946024803595606494939101919081908382808284375094965050505050505060006000836004600050600033600160a060020a03168152602001908152602001600020600050600087600160a060020a031681526020019081526020016000206000508190555084905080600160a060020a0316638f4ffcb1338630876040518560e060020a0281526004018085600160a060020a0316815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156102f25780820380516001836020036101000a031916815260200191505b50955050505050506000604051808303816000876161da5a03f11561000257505050509392505050565b6005602090815260043560009081526040808220909252602435815220546103d59081565b60046020818152903560009081526040808220909252602435815220546103d59081565b005b60405180806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156103c75780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60408051918252519081900360200190f35b6060908152602090f35b600160a060020a03821660009081526040902054808201101561041357610002565b806003600050600033600160a060020a03168152602001908152602001600020600082828250540392505081905550806003600050600084600160a060020a0316815260200190815260200160002060008282825054019250508190555081600160a060020a031633600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef836040518082815260200191505060405180910390a35050565b820191906000526020600020905b8154815290600101906020018083116104ce57829003601f168201915b505050505081565b600160a060020a03831681526040812054808301101561051257610002565b600160a060020a0380851680835260046020908152604080852033949094168086529382528085205492855260058252808520938552929052908220548301111561055c57610002565b816003600050600086600160a060020a03168152602001908152602001600020600082828250540392505081905550816003600050600085600160a060020a03168152602001908152602001600020600082828250540192505081905550816005600050600086600160a060020a03168152602001908152602001600020600050600033600160a060020a0316815260200190815260200160002060008282825054019250508190555082600160a060020a031633600160a060020a03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3939250505056`, `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"},{"name":"_extraData","type":"bytes"}],"name":"approveAndCall","outputs":[{"name":"success","type":"bool"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"spentAllowance","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"inputs":[{"name":"initialSupply","type":"uint256"},{"name":"tokenName","type":"string"},{"name":"decimalUnits","type":"uint8"},{"name":"tokenSymbol","type":"string"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]`, ` - if b, err := NewToken(common.Address{}, backends.NewNilBackend()); b == nil || err != nil { + if b, err := NewToken(common.Address{}, nil); b == nil || err != nil { t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) } `, @@ -73,7 +73,7 @@ var bindTests = []struct { `606060408190526007805460ff1916905560a0806105a883396101006040529051608051915160c05160e05160008054600160a060020a03199081169095178155670de0b6b3a7640000958602600155603c9093024201600355930260045560058054909216909217905561052f90819061007990396000f36060604052361561006c5760e060020a600035046301cb3b20811461008257806329dcb0cf1461014457806338af3eed1461014d5780636e66f6e91461015f5780637a3a0e84146101715780637b3e5e7b1461017a578063a035b1fe14610183578063dc0d3dff1461018c575b61020060075460009060ff161561032357610002565b61020060035460009042106103205760025460015490106103cb576002548154600160a060020a0316908290606082818181858883f150915460025460408051600160a060020a039390931683526020830191909152818101869052517fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf6945090819003909201919050a15b60405160008054600160a060020a039081169230909116319082818181858883f150506007805460ff1916600117905550505050565b6103a160035481565b6103ab600054600160a060020a031681565b6103ab600554600160a060020a031681565b6103a160015481565b6103a160025481565b6103a160045481565b6103be60043560068054829081101561000257506000526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f8101547ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d409190910154600160a060020a03919091169082565b005b505050815481101561000257906000526020600020906002020160005060008201518160000160006101000a815481600160a060020a030219169083021790555060208201518160010160005055905050806002600082828250540192505081905550600560009054906101000a9004600160a060020a0316600160a060020a031663a9059cbb3360046000505484046040518360e060020a0281526004018083600160a060020a03168152602001828152602001925050506000604051808303816000876161da5a03f11561000257505060408051600160a060020a03331681526020810184905260018183015290517fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf692509081900360600190a15b50565b5060a0604052336060908152346080819052600680546001810180835592939282908280158290116102025760020281600202836000526020600020918201910161020291905b8082111561039d57805473ffffffffffffffffffffffffffffffffffffffff19168155600060019190910190815561036a565b5090565b6060908152602090f35b600160a060020a03166060908152602090f35b6060918252608052604090f35b5b60065481101561010e576006805482908110156100025760009182526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0190600680549254600160a060020a0316928490811015610002576002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40015460405190915082818181858883f19350505050507fe842aea7a5f1b01049d752008c53c52890b1a6daf660cf39e8eec506112bbdf660066000508281548110156100025760008290526002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f01548154600160a060020a039190911691908490811015610002576002027ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d40015460408051600160a060020a0394909416845260208401919091526000838201525191829003606001919050a16001016103cc56`, `[{"constant":false,"inputs":[],"name":"checkGoalReached","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"deadline","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"beneficiary","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"tokenReward","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[],"name":"fundingGoal","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"amountRaised","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"price","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"funders","outputs":[{"name":"addr","type":"address"},{"name":"amount","type":"uint256"}],"type":"function"},{"inputs":[{"name":"ifSuccessfulSendTo","type":"address"},{"name":"fundingGoalInEthers","type":"uint256"},{"name":"durationInMinutes","type":"uint256"},{"name":"etherCostOfEachToken","type":"uint256"},{"name":"addressOfTokenUsedAsReward","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"backer","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"isContribution","type":"bool"}],"name":"FundTransfer","type":"event"}]`, ` - if b, err := NewCrowdsale(common.Address{}, backends.NewNilBackend()); b == nil || err != nil { + if b, err := NewCrowdsale(common.Address{}, nil); b == nil || err != nil { t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) } `, @@ -84,7 +84,7 @@ var bindTests = []struct { `606060405260405160808061145f833960e06040529051905160a05160c05160008054600160a060020a03191633179055600184815560028490556003839055600780549182018082558280158290116100b8576003028160030283600052602060002091820191016100b891906101c8565b50506060919091015160029190910155600160a060020a0381166000146100a65760008054600160a060020a031916821790555b505050506111f18061026e6000396000f35b505060408051608081018252600080825260208281018290528351908101845281815292820192909252426060820152600780549194509250811015610002579081527fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c6889050815181546020848101517401000000000000000000000000000000000000000002600160a060020a03199290921690921760a060020a60ff021916178255604083015180516001848101805460008281528690209195600293821615610100026000190190911692909204601f9081018390048201949192919091019083901061023e57805160ff19168380011785555b50610072929150610226565b5050600060028201556001015b8082111561023a578054600160a860020a031916815560018181018054600080835592600290821615610100026000190190911604601f81901061020c57506101bb565b601f0160209004906000526020600020908101906101bb91905b8082111561023a5760008155600101610226565b5090565b828001600101855582156101af579182015b828111156101af57825182600050559160200191906001019061025056606060405236156100b95760e060020a6000350463013cf08b81146100bb578063237e9492146101285780633910682114610281578063400e3949146102995780635daf08ca146102a257806369bd34361461032f5780638160f0b5146103385780638da5cb5b146103415780639644fcbd14610353578063aa02a90f146103be578063b1050da5146103c7578063bcca1fd3146104b5578063d3c0715b146104dc578063eceb29451461058d578063f2fde38b1461067b575b005b61069c6004356004805482908110156100025790600052602060002090600a02016000506005810154815460018301546003840154600485015460068601546007870154600160a060020a03959095169750929560020194919360ff828116946101009093041692919089565b60408051602060248035600481810135601f81018590048502860185019096528585526107759581359591946044949293909201918190840183828082843750949650505050505050600060006004600050848154811015610002575090527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19e600a8402908101547f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b909101904210806101e65750600481015460ff165b8061026757508060000160009054906101000a9004600160a060020a03168160010160005054846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f15090500193505050506040518091039020816007016000505414155b8061027757506001546005820154105b1561109257610002565b61077560043560066020526000908152604090205481565b61077560055481565b61078760043560078054829081101561000257506000526003026000805160206111d18339815191528101547fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c68a820154600160a060020a0382169260a060020a90920460ff16917fa66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c689019084565b61077560025481565b61077560015481565b610830600054600160a060020a031681565b604080516020604435600481810135601f81018490048402850184019095528484526100b9948135946024803595939460649492939101918190840183828082843750949650505050505050600080548190600160a060020a03908116339091161461084d57610002565b61077560035481565b604080516020604435600481810135601f8101849004840285018401909552848452610775948135946024803595939460649492939101918190840183828082843750506040805160209735808a0135601f81018a90048a0283018a019093528282529698976084979196506024909101945090925082915084018382808284375094965050505050505033600160a060020a031660009081526006602052604081205481908114806104ab5750604081205460078054909190811015610002579082526003026000805160206111d1833981519152015460a060020a900460ff16155b15610ce557610002565b6100b960043560243560443560005433600160a060020a03908116911614610b1857610002565b604080516020604435600481810135601f810184900484028501840190955284845261077594813594602480359593946064949293910191819084018382808284375094965050505050505033600160a060020a031660009081526006602052604081205481908114806105835750604081205460078054909190811015610002579082526003026000805160206111d18339815191520181505460a060020a900460ff16155b15610f1d57610002565b604080516020606435600481810135601f81018490048402850184019095528484526107759481359460248035956044359560849492019190819084018382808284375094965050505050505060006000600460005086815481101561000257908252600a027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b01815090508484846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160070160005054149150610cdc565b6100b960043560005433600160a060020a03908116911614610f0857610002565b604051808a600160a060020a031681526020018981526020018060200188815260200187815260200186815260200185815260200184815260200183815260200182810382528981815460018160011615610100020316600290048152602001915080546001816001161561010002031660029004801561075e5780601f106107335761010080835404028352916020019161075e565b820191906000526020600020905b81548152906001019060200180831161074157829003601f168201915b50509a505050505050505050505060405180910390f35b60408051918252519081900360200190f35b60408051600160a060020a038616815260208101859052606081018390526080918101828152845460026001821615610100026000190190911604928201839052909160a08301908590801561081e5780601f106107f35761010080835404028352916020019161081e565b820191906000526020600020905b81548152906001019060200180831161080157829003601f168201915b50509550505050505060405180910390f35b60408051600160a060020a03929092168252519081900360200190f35b600160a060020a03851660009081526006602052604081205414156108a957604060002060078054918290556001820180825582801582901161095c5760030281600302836000526020600020918201910161095c9190610a4f565b600160a060020a03851660009081526006602052604090205460078054919350908390811015610002575060005250600381026000805160206111d183398151915201805474ff0000000000000000000000000000000000000000191660a060020a85021781555b60408051600160a060020a03871681526020810186905281517f27b022af4a8347100c7a041ce5ccf8e14d644ff05de696315196faae8cd50c9b929181900390910190a15050505050565b505050915081506080604051908101604052808681526020018581526020018481526020014281526020015060076000508381548110156100025790600052602060002090600302016000508151815460208481015160a060020a02600160a060020a03199290921690921774ff00000000000000000000000000000000000000001916178255604083015180516001848101805460008281528690209195600293821615610100026000190190911692909204601f90810183900482019491929190910190839010610ad357805160ff19168380011785555b50610b03929150610abb565b5050600060028201556001015b80821115610acf57805474ffffffffffffffffffffffffffffffffffffffffff1916815560018181018054600080835592600290821615610100026000190190911604601f819010610aa15750610a42565b601f016020900490600052602060002090810190610a4291905b80821115610acf5760008155600101610abb565b5090565b82800160010185558215610a36579182015b82811115610a36578251826000505591602001919060010190610ae5565b50506060919091015160029190910155610911565b600183905560028290556003819055604080518481526020810184905280820183905290517fa439d3fa452be5e0e1e24a8145e715f4fd8b9c08c96a42fd82a855a85e5d57de9181900360600190a1505050565b50508585846040518084600160a060020a0316606060020a0281526014018381526020018280519060200190808383829060006004602084601f0104600f02600301f150905001935050505060405180910390208160070160005081905550600260005054603c024201816003016000508190555060008160040160006101000a81548160ff0219169083021790555060008160040160016101000a81548160ff02191690830217905550600081600501600050819055507f646fec02522b41e7125cfc859a64fd4f4cefd5dc3b6237ca0abe251ded1fa881828787876040518085815260200184600160a060020a03168152602001838152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f168015610cc45780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a1600182016005555b50949350505050565b6004805460018101808355909190828015829011610d1c57600a0281600a028360005260206000209182019101610d1c9190610db8565b505060048054929450918491508110156100025790600052602060002090600a02016000508054600160a060020a031916871781556001818101879055855160028381018054600082815260209081902096975091959481161561010002600019011691909104601f90810182900484019391890190839010610ed857805160ff19168380011785555b50610b6c929150610abb565b50506001015b80821115610acf578054600160a060020a03191681556000600182810182905560028381018054848255909281161561010002600019011604601f819010610e9c57505b5060006003830181905560048301805461ffff191690556005830181905560068301819055600783018190556008830180548282559082526020909120610db2916002028101905b80821115610acf57805474ffffffffffffffffffffffffffffffffffffffffff1916815560018181018054600080835592600290821615610100026000190190911604601f819010610eba57505b5050600101610e44565b601f016020900490600052602060002090810190610dfc9190610abb565b601f016020900490600052602060002090810190610e929190610abb565b82800160010185558215610da6579182015b82811115610da6578251826000505591602001919060010190610eea565b60008054600160a060020a0319168217905550565b600480548690811015610002576000918252600a027f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b01905033600160a060020a0316600090815260098201602052604090205490915060ff1660011415610f8457610002565b33600160a060020a031660009081526009820160205260409020805460ff1916600190811790915560058201805490910190558315610fcd576006810180546001019055610fda565b6006810180546000190190555b7fc34f869b7ff431b034b7b9aea9822dac189a685e0b015c7d1be3add3f89128e8858533866040518085815260200184815260200183600160a060020a03168152602001806020018281038252838181518152602001915080519060200190808383829060006004602084601f0104600f02600301f150905090810190601f16801561107a5780820380516001836020036101000a031916815260200191505b509550505050505060405180910390a1509392505050565b6006810154600354901315611158578060000160009054906101000a9004600160a060020a0316600160a060020a03168160010160005054670de0b6b3a76400000284604051808280519060200190808383829060006004602084601f0104600f02600301f150905090810190601f1680156111225780820380516001836020036101000a031916815260200191505b5091505060006040518083038185876185025a03f15050505060048101805460ff191660011761ff00191661010017905561116d565b60048101805460ff191660011761ff00191690555b60068101546005820154600483015460408051888152602081019490945283810192909252610100900460ff166060830152517fd220b7272a8b6d0d7d6bcdace67b936a8f175e6d5c1b3ee438b72256b32ab3af9181900360800190a1509291505056a66cc928b5edb82af9bd49922954155ab7b0942694bea4ce44661d9a8736c688`, `[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"proposals","outputs":[{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"},{"name":"description","type":"string"},{"name":"votingDeadline","type":"uint256"},{"name":"executed","type":"bool"},{"name":"proposalPassed","type":"bool"},{"name":"numberOfVotes","type":"uint256"},{"name":"currentResult","type":"int256"},{"name":"proposalHash","type":"bytes32"}],"type":"function"},{"constant":false,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"transactionBytecode","type":"bytes"}],"name":"executeProposal","outputs":[{"name":"result","type":"int256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"memberId","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"numProposals","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"members","outputs":[{"name":"member","type":"address"},{"name":"canVote","type":"bool"},{"name":"name","type":"string"},{"name":"memberSince","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"debatingPeriodInMinutes","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"minimumQuorum","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"targetMember","type":"address"},{"name":"canVote","type":"bool"},{"name":"memberName","type":"string"}],"name":"changeMembership","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"majorityMargin","outputs":[{"name":"","type":"int256"}],"type":"function"},{"constant":false,"inputs":[{"name":"beneficiary","type":"address"},{"name":"etherAmount","type":"uint256"},{"name":"JobDescription","type":"string"},{"name":"transactionBytecode","type":"bytes"}],"name":"newProposal","outputs":[{"name":"proposalID","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[{"name":"minimumQuorumForProposals","type":"uint256"},{"name":"minutesForDebate","type":"uint256"},{"name":"marginOfVotesForMajority","type":"int256"}],"name":"changeVotingRules","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"supportsProposal","type":"bool"},{"name":"justificationText","type":"string"}],"name":"vote","outputs":[{"name":"voteID","type":"uint256"}],"type":"function"},{"constant":true,"inputs":[{"name":"proposalNumber","type":"uint256"},{"name":"beneficiary","type":"address"},{"name":"etherAmount","type":"uint256"},{"name":"transactionBytecode","type":"bytes"}],"name":"checkProposalCode","outputs":[{"name":"codeChecksOut","type":"bool"}],"type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"type":"function"},{"inputs":[{"name":"minimumQuorumForProposals","type":"uint256"},{"name":"minutesForDebate","type":"uint256"},{"name":"marginOfVotesForMajority","type":"int256"},{"name":"congressLeader","type":"address"}],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"recipient","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"description","type":"string"}],"name":"ProposalAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"position","type":"bool"},{"indexed":false,"name":"voter","type":"address"},{"indexed":false,"name":"justification","type":"string"}],"name":"Voted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"proposalID","type":"uint256"},{"indexed":false,"name":"result","type":"int256"},{"indexed":false,"name":"quorum","type":"uint256"},{"indexed":false,"name":"active","type":"bool"}],"name":"ProposalTallied","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"member","type":"address"},{"indexed":false,"name":"isMember","type":"bool"}],"name":"MembershipChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"minimumQuorum","type":"uint256"},{"indexed":false,"name":"debatingPeriodInMinutes","type":"uint256"},{"indexed":false,"name":"majorityMargin","type":"int256"}],"name":"ChangeOfRules","type":"event"}]`, ` - if b, err := NewDAO(common.Address{}, backends.NewNilBackend()); b == nil || err != nil { + if b, err := NewDAO(common.Address{}, nil); b == nil || err != nil { t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) } `, @@ -102,7 +102,7 @@ var bindTests = []struct { {"type":"function","name":"mixedInputs","constant":true,"inputs":[{"name":"","type":"string"},{"name":"str","type":"string"}],"outputs":[]} ] `, - `if b, err := NewInputChecker(common.Address{}, backends.NewNilBackend()); b == nil || err != nil { + `if b, err := NewInputChecker(common.Address{}, nil); b == nil || err != nil { t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) } else if false { // Don't run, just compile and test types var err error @@ -130,7 +130,7 @@ var bindTests = []struct { {"type":"function","name":"mixedOutputs","constant":true,"inputs":[],"outputs":[{"name":"","type":"string"},{"name":"str","type":"string"}]} ] `, - `if b, err := NewOutputChecker(common.Address{}, backends.NewNilBackend()); b == nil || err != nil { + `if b, err := NewOutputChecker(common.Address{}, nil); b == nil || err != nil { t.Fatalf("binding (%v) nil or error (%v) not nil", b, nil) } else if false { // Don't run, just compile and test types var str1, str2 string @@ -376,7 +376,7 @@ func TestBindings(t *testing.T) { t.Skip("go sdk not found for testing") } // Skip the test if the go-ethereum sources are symlinked (https://github.com/golang/go/issues/14845) - linkTestCode := fmt.Sprintf("package linktest\nfunc CheckSymlinks(){\nfmt.Println(backends.NewNilBackend())\n}") + linkTestCode := fmt.Sprintf("package linktest\nfunc CheckSymlinks(){\nfmt.Println(backends.NewSimulatedBackend())\n}") linkTestDeps, err := imports.Process("", []byte(linkTestCode), nil) if err != nil { t.Fatalf("failed check for goimports symlink bug: %v", err) diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go index 72998bb6d..523122213 100644 --- a/accounts/abi/bind/template.go +++ b/accounts/abi/bind/template.go @@ -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 } -- cgit v1.2.3 From d62d5fe59aedf9635a175d663632512b1cbbf2c3 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 22 Aug 2016 14:01:28 +0200 Subject: accounts/abi/bind: use ethereum interfaces In this commit, contract bindings and their backend start using the Ethereum Go API interfaces offered by ethclient. This makes ethclient a suitable replacement for the old remote backend and gets us one step closer to the final stable Go API that is planned for go-ethereum 1.5. The changes in detail: * Pending state is optional for read only contract bindings. BoundContract attempts to discover the Pending* methods via an interface assertion. There are a couple of advantages to this: ContractCaller is just two methods and can be implemented on top of pretty much anything that provides Ethereum data. Since the backend interfaces are now disjoint, ContractBackend can simply be declared as a union of the reader and writer side. * Caching of HasCode is removed. The caching could go wrong in case of chain reorganisations and removing it simplifies the code a lot. We'll figure out a performant way of providing ErrNoCode before the 1.5 release. * BoundContract now ensures that the backend receives a non-nil context with every call. --- accounts/abi/bind/backend.go | 95 +++++++------------ accounts/abi/bind/backends/simulated.go | 162 +++++++++++++++----------------- accounts/abi/bind/base.go | 75 +++++++++------ eth/bind.go | 92 +++++++++--------- 4 files changed, 204 insertions(+), 220 deletions(-) diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go index bec742c29..3d38f87cd 100644 --- a/accounts/abi/bind/backend.go +++ b/accounts/abi/bind/backend.go @@ -20,28 +20,42 @@ 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") +) // 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) +// 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 +63,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 } diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 687a31bf1..c2542f40e 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -17,8 +17,10 @@ package backends import ( + "fmt" "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" @@ -79,58 +81,44 @@ func (b *SimulatedBackend) Rollback() { b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database) } -// HasCode implements ContractVerifier.HasCode, checking whether there is any -// code associated with a certain account in the blockchain. -func (b *SimulatedBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) { - if pending { - return len(b.pendingState.GetCode(contract)) > 0, nil +// CodeAt implements ChainStateReader.CodeAt, returning the code associated with +// a certain account at a given block number in the blockchain. +func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { + return nil, fmt.Errorf("SimulatedBackend cannot access blocks other than the latest block") } statedb, _ := b.blockchain.State() - return len(statedb.GetCode(contract)) > 0, nil + return statedb.GetCode(contract), nil } -// ContractCall implements ContractCaller.ContractCall, executing the specified -// contract with the given input data. -func (b *SimulatedBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) { - // Create a copy of the current state db to screw around with - var ( - block *types.Block - statedb *state.StateDB - ) - if pending { - block, statedb = b.pendingBlock, b.pendingState.Copy() - } else { - block = b.blockchain.CurrentBlock() - statedb, _ = b.blockchain.State() - } - // If there's no code to interact with, respond with an appropriate error - if code := statedb.GetCode(contract); len(code) == 0 { - return nil, bind.ErrNoCode - } - // Set infinite balance to the a fake caller account - from := statedb.GetOrNewStateObject(common.Address{}) - from.SetBalance(common.MaxBig) +// PendingCodeAt implements PendingStateReader.PendingCodeAt, returning the +// code associated with a certain account in the pending state of the blockchain. +func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { + return b.pendingState.GetCode(contract), nil +} - // Assemble the call invocation to measure the gas usage - msg := callmsg{ - from: from, - to: &contract, - gasPrice: new(big.Int), - gasLimit: common.MaxBig, - value: new(big.Int), - data: data, +// CallContract executes a contract call. +func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) { + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { + return nil, fmt.Errorf("SimulatedBackend cannot access blocks other than the latest block") } - // Execute the call and return - vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{}) - gaspool := new(core.GasPool).AddGas(common.MaxBig) + state, err := b.blockchain.State() + if err != nil { + return nil, err + } + rval, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state) + return rval, err +} - out, _, err := core.ApplyMessage(vmenv, msg, gaspool) - return out, err +// PendingCallContract executes a contract call on the pending state. +func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) { + rval, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState.Copy()) + return rval, err } -// PendingAccountNonce implements ContractTransactor.PendingAccountNonce, retrieving +// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving // the nonce currently pending for the account. -func (b *SimulatedBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) { +func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { return b.pendingState.GetOrNewStateObject(account).Nonce(), nil } @@ -140,45 +128,49 @@ func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error return big.NewInt(1), nil } -// EstimateGasLimit implements ContractTransactor.EstimateGasLimit, executing the -// requested code against the currently pending block/state and returning the used -// gas. -func (b *SimulatedBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) { - // Create a copy of the currently pending state db to screw around with - var ( - block = b.pendingBlock - statedb = b.pendingState.Copy() - ) - // If there's no code to interact with, respond with an appropriate error - if contract != nil { - if code := statedb.GetCode(*contract); len(code) == 0 { - return nil, bind.ErrNoCode - } - } - // Set infinite balance to the a fake caller account - from := statedb.GetOrNewStateObject(sender) - from.SetBalance(common.MaxBig) +// EstimateGas executes the requested code against the currently pending block/state and +// returns the used amount of gas. +func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (*big.Int, error) { + _, gas, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState.Copy()) + return gas, err +} - // Assemble the call invocation to measure the gas usage - msg := callmsg{ - from: from, - to: contract, - gasPrice: new(big.Int), - gasLimit: common.MaxBig, - value: value, - data: data, +// callContract implemens common code between normal and pending contract calls. +// state is modified during execution, make sure to copy it if necessary. +func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, *big.Int, error) { + // Ensure message is initialized properly. + if call.GasPrice == nil { + call.GasPrice = big.NewInt(1) + } + if call.Gas == nil || call.Gas.BitLen() == 0 { + call.Gas = big.NewInt(50000000) } - // Execute the call and return + if call.Value == nil { + call.Value = new(big.Int) + } + // Set infinite balance to the fake caller account. + from := statedb.GetOrNewStateObject(call.From) + from.SetBalance(common.MaxBig) + // Execute the call. + msg := callmsg{call} vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{}) gaspool := new(core.GasPool).AddGas(common.MaxBig) - - _, gas, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() - return gas, err + ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() + return ret, gasUsed, err } -// SendTransaction implements ContractTransactor.SendTransaction, delegating the raw -// transaction injection to the remote node. +// SendTransaction updates the pending block to include the given transaction. +// It panics if the transaction is invalid. func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { + sender, err := tx.From() + if err != nil { + panic(fmt.Errorf("invalid transaction: %v", err)) + } + nonce := b.pendingState.GetNonce(sender) + if tx.Nonce() != nonce { + panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)) + } + blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) { for _, tx := range b.pendingBlock.Transactions() { block.AddTx(tx) @@ -187,26 +179,20 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa }) b.pendingBlock = blocks[0] b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database) - return nil } // callmsg implements core.Message to allow passing it as a transaction simulator. type callmsg struct { - from *state.StateObject - to *common.Address - gasLimit *big.Int - gasPrice *big.Int - value *big.Int - data []byte + ethereum.CallMsg } -func (m callmsg) From() (common.Address, error) { return m.from.Address(), nil } -func (m callmsg) FromFrontier() (common.Address, error) { return m.from.Address(), nil } +func (m callmsg) From() (common.Address, error) { return m.CallMsg.From, nil } +func (m callmsg) FromFrontier() (common.Address, error) { return m.CallMsg.From, nil } func (m callmsg) Nonce() uint64 { return 0 } func (m callmsg) CheckNonce() bool { return false } -func (m callmsg) To() *common.Address { return m.to } -func (m callmsg) GasPrice() *big.Int { return m.gasPrice } -func (m callmsg) Gas() *big.Int { return m.gasLimit } -func (m callmsg) Value() *big.Int { return m.value } -func (m callmsg) Data() []byte { return m.data } +func (m callmsg) To() *common.Address { return m.CallMsg.To } +func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } +func (m callmsg) Gas() *big.Int { return m.CallMsg.Gas } +func (m callmsg) Value() *big.Int { return m.CallMsg.Value } +func (m callmsg) Data() []byte { return m.CallMsg.Data } diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index 80948d3f1..965f51e85 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -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 +} diff --git a/eth/bind.go b/eth/bind.go index bf7a7fb53..532e94460 100644 --- a/eth/bind.go +++ b/eth/bind.go @@ -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 -- cgit v1.2.3 From c97df052a96820742fffdbb3ef5e77dbf1397637 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Mon, 22 Aug 2016 23:20:13 +0200 Subject: accounts/abi/bind: add utilities for waiting on transactions The need for these functions comes up in code that actually deploys and uses contracts. As of this commit, they can be used with both SimulatedBackend and ethclient. SimulatedBackend gains some additional methods in the process and is now safe for concurrent use. --- accounts/abi/bind/backend.go | 10 ++++ accounts/abi/bind/backends/simulated.go | 103 +++++++++++++++++++++++++++----- accounts/abi/bind/util.go | 76 +++++++++++++++++++++++ accounts/abi/bind/util_test.go | 93 ++++++++++++++++++++++++++++ 4 files changed, 267 insertions(+), 15 deletions(-) create mode 100644 accounts/abi/bind/util.go create mode 100644 accounts/abi/bind/util_test.go diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go index 3d38f87cd..a4e90914f 100644 --- a/accounts/abi/bind/backend.go +++ b/accounts/abi/bind/backend.go @@ -35,6 +35,10 @@ var ( // 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 @@ -48,6 +52,12 @@ type ContractCaller interface { CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]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. diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index c2542f40e..29b4e8ea3 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -17,8 +17,10 @@ package backends import ( + "errors" "fmt" "math/big" + "sync" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -38,12 +40,15 @@ var chainConfig = &core.ChainConfig{HomesteadBlock: big.NewInt(0)} // This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend. 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 // the background. Its main purpose is to allow easily testing contract bindings. type SimulatedBackend struct { database ethdb.Database // In memory database to store our testing data blockchain *core.BlockChain // Ethereum blockchain to handle the consensus + mu sync.Mutex 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 } @@ -54,53 +59,109 @@ func NewSimulatedBackend(accounts ...core.GenesisAccount) *SimulatedBackend { database, _ := ethdb.NewMemDatabase() core.WriteGenesisBlockForTesting(database, accounts...) blockchain, _ := core.NewBlockChain(database, chainConfig, new(core.FakePow), new(event.TypeMux)) - - backend := &SimulatedBackend{ - database: database, - blockchain: blockchain, - } - backend.Rollback() - + backend := &SimulatedBackend{database: database, blockchain: blockchain} + backend.rollback() return backend } // Commit imports all the pending transactions as a single block and starts a // fresh new state. func (b *SimulatedBackend) Commit() { + b.mu.Lock() + defer b.mu.Unlock() + 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 } - b.Rollback() + b.rollback() } // Rollback aborts all pending transactions, reverting to the last committed state. 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.pendingState, _ = state.New(b.pendingBlock.Root(), b.database) } -// CodeAt implements ChainStateReader.CodeAt, returning the code associated with -// a certain account at a given block number in the blockchain. +// CodeAt returns the code associated with a certain account in the blockchain. func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) { + b.mu.Lock() + defer b.mu.Unlock() + if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 { - return nil, fmt.Errorf("SimulatedBackend cannot access blocks other than the latest block") + return nil, errBlockNumberUnsupported } statedb, _ := b.blockchain.State() return statedb.GetCode(contract), nil } -// PendingCodeAt implements PendingStateReader.PendingCodeAt, returning the -// code associated with a certain account in the pending state of the blockchain. +// BalanceAt returns the wei balance of a certain account in the blockchain. +func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (*big.Int, 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() + return statedb.GetBalance(contract), nil +} + +// 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, fmt.Errorf("SimulatedBackend cannot access blocks other than the latest block") + return nil, errBlockNumberUnsupported } state, err := b.blockchain.State() if err != nil { @@ -112,6 +173,9 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM // 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 } @@ -119,6 +183,9 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu // PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving // the nonce currently pending for the account. 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 } @@ -131,6 +198,9 @@ func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error // EstimateGas executes the requested code against the currently pending block/state and // returns the used amount of gas. func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (*big.Int, error) { + b.mu.Lock() + defer b.mu.Unlock() + _, gas, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState.Copy()) return gas, err } @@ -162,6 +232,9 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM // 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 { + b.mu.Lock() + defer b.mu.Unlock() + sender, err := tx.From() if err != nil { panic(fmt.Errorf("invalid transaction: %v", err)) diff --git a/accounts/abi/bind/util.go b/accounts/abi/bind/util.go new file mode 100644 index 000000000..bbb6d6a75 --- /dev/null +++ b/accounts/abi/bind/util.go @@ -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 . + +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 +} diff --git a/accounts/abi/bind/util_test.go b/accounts/abi/bind/util_test.go new file mode 100644 index 000000000..192fa4f4c --- /dev/null +++ b/accounts/abi/bind/util_test.go @@ -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 . + +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) + } + } +} -- cgit v1.2.3