Merge pull request #3756 from fjl/core-types-gencodec

core/types: use gencodec for JSON marshaling code
This commit is contained in:
Felix Lange 2017-03-22 01:36:22 +01:00 committed by GitHub
commit 06d6685eb5
16 changed files with 550 additions and 318 deletions

View File

@ -40,6 +40,15 @@ test: all
clean: clean:
rm -fr build/_workspace/pkg/ $(GOBIN)/* rm -fr build/_workspace/pkg/ $(GOBIN)/*
# The devtools target installs tools required for 'go generate'.
# You need to put $GOBIN (or $GOPATH/bin) in your PATH to use 'go generate'.
devtools:
go get -u golang.org/x/tools/cmd/stringer
go get -u github.com/jteeuwen/go-bindata/go-bindata
go get -u github.com/fjl/gencodec
go install ./cmd/abigen
# Cross Compilation Targets (xgo) # Cross Compilation Targets (xgo)
geth-cross: geth-linux geth-darwin geth-windows geth-android geth-ios geth-cross: geth-linux geth-darwin geth-windows geth-android geth-ios

View File

@ -19,8 +19,6 @@ package types
import ( import (
"encoding/binary" "encoding/binary"
"encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"math/big" "math/big"
@ -39,12 +37,6 @@ var (
EmptyUncleHash = CalcUncleHash(nil) EmptyUncleHash = CalcUncleHash(nil)
) )
var (
errMissingHeaderMixDigest = errors.New("missing mixHash in JSON block header")
errMissingHeaderFields = errors.New("missing required JSON block header fields")
errBadNonceSize = errors.New("invalid block nonce size, want 8 bytes")
)
// A BlockNonce is a 64-bit hash which proves (combined with the // A BlockNonce is a 64-bit hash which proves (combined with the
// mix-hash) that a sufficient amount of computation has been carried // mix-hash) that a sufficient amount of computation has been carried
// out on a block. // out on a block.
@ -72,41 +64,35 @@ func (n *BlockNonce) UnmarshalText(input []byte) error {
return hexutil.UnmarshalFixedText("BlockNonce", input, n[:]) return hexutil.UnmarshalFixedText("BlockNonce", input, n[:])
} }
//go:generate gencodec -type Header -field-override headerMarshaling -out gen_header_json.go
// Header represents a block header in the Ethereum blockchain. // Header represents a block header in the Ethereum blockchain.
type Header struct { type Header struct {
ParentHash common.Hash // Hash to the previous block ParentHash common.Hash `json:"parentHash"`
UncleHash common.Hash // Uncles of this block UncleHash common.Hash `json:"sha3Uncles"`
Coinbase common.Address // The coin base address Coinbase common.Address `json:"miner"`
Root common.Hash // Block Trie state Root common.Hash `json:"stateRoot"`
TxHash common.Hash // Tx sha TxHash common.Hash `json:"transactionsRoot"`
ReceiptHash common.Hash // Receipt sha ReceiptHash common.Hash `json:"receiptsRoot"`
Bloom Bloom // Bloom Bloom Bloom `json:"logsBloom"`
Difficulty *big.Int // Difficulty for the current block Difficulty *big.Int `json:"difficulty"`
Number *big.Int // The block number Number *big.Int `json:"number"`
GasLimit *big.Int // Gas limit GasLimit *big.Int `json:"gasLimit"`
GasUsed *big.Int // Gas used GasUsed *big.Int `json:"gasUsed"`
Time *big.Int // Creation time Time *big.Int `json:"timestamp"`
Extra []byte // Extra data Extra []byte `json:"extraData"`
MixDigest common.Hash // for quick difficulty verification MixDigest common.Hash `json:"mixHash"`
Nonce BlockNonce Nonce BlockNonce `json:"nonce"`
} }
type jsonHeader struct { // field type overrides for gencodec
ParentHash *common.Hash `json:"parentHash"` type headerMarshaling struct {
UncleHash *common.Hash `json:"sha3Uncles"` Difficulty *hexutil.Big
Coinbase *common.Address `json:"miner"` Number *hexutil.Big
Root *common.Hash `json:"stateRoot"` GasLimit *hexutil.Big
TxHash *common.Hash `json:"transactionsRoot"` GasUsed *hexutil.Big
ReceiptHash *common.Hash `json:"receiptsRoot"` Time *hexutil.Big
Bloom *Bloom `json:"logsBloom"` Extra hexutil.Bytes
Difficulty *hexutil.Big `json:"difficulty"`
Number *hexutil.Big `json:"number"`
GasLimit *hexutil.Big `json:"gasLimit"`
GasUsed *hexutil.Big `json:"gasUsed"`
Time *hexutil.Big `json:"timestamp"`
Extra *hexutil.Bytes `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 // Hash returns the block hash of the header, which is simply the keccak256 hash of its
@ -134,65 +120,6 @@ func (h *Header) HashNoNonce() common.Hash {
}) })
} }
// 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: (*hexutil.Big)(h.Difficulty),
Number: (*hexutil.Big)(h.Number),
GasLimit: (*hexutil.Big)(h.GasLimit),
GasUsed: (*hexutil.Big)(h.GasUsed),
Time: (*hexutil.Big)(h.Time),
Extra: (*hexutil.Bytes)(&h.Extra),
MixDigest: &h.MixDigest,
Nonce: &h.Nonce,
})
}
// 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
}
// 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) { func rlpHash(x interface{}) (h common.Hash) {
hw := sha3.NewKeccak256() hw := sha3.NewKeccak256()
rlp.Encode(hw, x) rlp.Encode(hw, x)

View File

@ -0,0 +1,136 @@
// generated by github.com/fjl/gencodec, do not edit.
package types
import (
"encoding/json"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)
func (h *Header) MarshalJSON() ([]byte, error) {
type HeaderJSON 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:"receiptsRoot"`
Bloom *Bloom `json:"logsBloom"`
Difficulty *hexutil.Big `json:"difficulty"`
Number *hexutil.Big `json:"number"`
GasLimit *hexutil.Big `json:"gasLimit"`
GasUsed *hexutil.Big `json:"gasUsed"`
Time *hexutil.Big `json:"timestamp"`
Extra hexutil.Bytes `json:"extraData"`
MixDigest *common.Hash `json:"mixHash"`
Nonce *BlockNonce `json:"nonce"`
}
var enc HeaderJSON
enc.ParentHash = &h.ParentHash
enc.UncleHash = &h.UncleHash
enc.Coinbase = &h.Coinbase
enc.Root = &h.Root
enc.TxHash = &h.TxHash
enc.ReceiptHash = &h.ReceiptHash
enc.Bloom = &h.Bloom
enc.Difficulty = (*hexutil.Big)(h.Difficulty)
enc.Number = (*hexutil.Big)(h.Number)
enc.GasLimit = (*hexutil.Big)(h.GasLimit)
enc.GasUsed = (*hexutil.Big)(h.GasUsed)
enc.Time = (*hexutil.Big)(h.Time)
enc.Extra = h.Extra
enc.MixDigest = &h.MixDigest
enc.Nonce = &h.Nonce
return json.Marshal(&enc)
}
func (h *Header) UnmarshalJSON(input []byte) error {
type HeaderJSON 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:"receiptsRoot"`
Bloom *Bloom `json:"logsBloom"`
Difficulty *hexutil.Big `json:"difficulty"`
Number *hexutil.Big `json:"number"`
GasLimit *hexutil.Big `json:"gasLimit"`
GasUsed *hexutil.Big `json:"gasUsed"`
Time *hexutil.Big `json:"timestamp"`
Extra hexutil.Bytes `json:"extraData"`
MixDigest *common.Hash `json:"mixHash"`
Nonce *BlockNonce `json:"nonce"`
}
var dec HeaderJSON
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
var x Header
if dec.ParentHash == nil {
return errors.New("missing required field 'parentHash' for Header")
}
x.ParentHash = *dec.ParentHash
if dec.UncleHash == nil {
return errors.New("missing required field 'sha3Uncles' for Header")
}
x.UncleHash = *dec.UncleHash
if dec.Coinbase == nil {
return errors.New("missing required field 'miner' for Header")
}
x.Coinbase = *dec.Coinbase
if dec.Root == nil {
return errors.New("missing required field 'stateRoot' for Header")
}
x.Root = *dec.Root
if dec.TxHash == nil {
return errors.New("missing required field 'transactionsRoot' for Header")
}
x.TxHash = *dec.TxHash
if dec.ReceiptHash == nil {
return errors.New("missing required field 'receiptsRoot' for Header")
}
x.ReceiptHash = *dec.ReceiptHash
if dec.Bloom == nil {
return errors.New("missing required field 'logsBloom' for Header")
}
x.Bloom = *dec.Bloom
if dec.Difficulty == nil {
return errors.New("missing required field 'difficulty' for Header")
}
x.Difficulty = (*big.Int)(dec.Difficulty)
if dec.Number == nil {
return errors.New("missing required field 'number' for Header")
}
x.Number = (*big.Int)(dec.Number)
if dec.GasLimit == nil {
return errors.New("missing required field 'gasLimit' for Header")
}
x.GasLimit = (*big.Int)(dec.GasLimit)
if dec.GasUsed == nil {
return errors.New("missing required field 'gasUsed' for Header")
}
x.GasUsed = (*big.Int)(dec.GasUsed)
if dec.Time == nil {
return errors.New("missing required field 'timestamp' for Header")
}
x.Time = (*big.Int)(dec.Time)
if dec.Extra == nil {
return errors.New("missing required field 'extraData' for Header")
}
x.Extra = dec.Extra
if dec.MixDigest == nil {
return errors.New("missing required field 'mixHash' for Header")
}
x.MixDigest = *dec.MixDigest
if dec.Nonce == nil {
return errors.New("missing required field 'nonce' for Header")
}
x.Nonce = *dec.Nonce
*h = x
return nil
}

View File

@ -0,0 +1,90 @@
// generated by github.com/fjl/gencodec, do not edit.
package types
import (
"encoding/json"
"errors"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)
func (l *Log) MarshalJSON() ([]byte, error) {
type LogJSON struct {
Address *common.Address `json:"address"`
Topics []common.Hash `json:"topics"`
Data hexutil.Bytes `json:"data"`
BlockNumber *hexutil.Uint64 `json:"blockNumber" optional:"yes"`
TxHash *common.Hash `json:"transactionHash"`
TxIndex *hexutil.Uint `json:"transactionIndex"`
BlockHash *common.Hash `json:"blockHash" optional:"yes"`
Index *hexutil.Uint `json:"logIndex"`
Removed *bool `json:"removed" optional:"yes"`
}
var enc LogJSON
enc.Address = &l.Address
enc.Topics = l.Topics
enc.Data = l.Data
enc.BlockNumber = (*hexutil.Uint64)(&l.BlockNumber)
enc.TxHash = &l.TxHash
enc.TxIndex = (*hexutil.Uint)(&l.TxIndex)
enc.BlockHash = &l.BlockHash
enc.Index = (*hexutil.Uint)(&l.Index)
enc.Removed = &l.Removed
return json.Marshal(&enc)
}
func (l *Log) UnmarshalJSON(input []byte) error {
type LogJSON struct {
Address *common.Address `json:"address"`
Topics []common.Hash `json:"topics"`
Data hexutil.Bytes `json:"data"`
BlockNumber *hexutil.Uint64 `json:"blockNumber" optional:"yes"`
TxHash *common.Hash `json:"transactionHash"`
TxIndex *hexutil.Uint `json:"transactionIndex"`
BlockHash *common.Hash `json:"blockHash" optional:"yes"`
Index *hexutil.Uint `json:"logIndex"`
Removed *bool `json:"removed" optional:"yes"`
}
var dec LogJSON
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
var x Log
if dec.Address == nil {
return errors.New("missing required field 'address' for Log")
}
x.Address = *dec.Address
if dec.Topics == nil {
return errors.New("missing required field 'topics' for Log")
}
x.Topics = dec.Topics
if dec.Data == nil {
return errors.New("missing required field 'data' for Log")
}
x.Data = dec.Data
if dec.BlockNumber != nil {
x.BlockNumber = uint64(*dec.BlockNumber)
}
if dec.TxHash == nil {
return errors.New("missing required field 'transactionHash' for Log")
}
x.TxHash = *dec.TxHash
if dec.TxIndex == nil {
return errors.New("missing required field 'transactionIndex' for Log")
}
x.TxIndex = uint(*dec.TxIndex)
if dec.BlockHash != nil {
x.BlockHash = *dec.BlockHash
}
if dec.Index == nil {
return errors.New("missing required field 'logIndex' for Log")
}
x.Index = uint(*dec.Index)
if dec.Removed != nil {
x.Removed = *dec.Removed
}
*l = x
return nil
}

View File

@ -0,0 +1,79 @@
// generated by github.com/fjl/gencodec, do not edit.
package types
import (
"encoding/json"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)
func (r *Receipt) MarshalJSON() ([]byte, error) {
type ReceiptJSON struct {
PostState hexutil.Bytes `json:"root"`
CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed"`
Bloom *Bloom `json:"logsBloom"`
Logs []*Log `json:"logs"`
TxHash *common.Hash `json:"transactionHash"`
ContractAddress *common.Address `json:"contractAddress" optional:"true"`
GasUsed *hexutil.Big `json:"gasUsed"`
}
var enc ReceiptJSON
enc.PostState = r.PostState
enc.CumulativeGasUsed = (*hexutil.Big)(r.CumulativeGasUsed)
enc.Bloom = &r.Bloom
enc.Logs = r.Logs
enc.TxHash = &r.TxHash
enc.ContractAddress = &r.ContractAddress
enc.GasUsed = (*hexutil.Big)(r.GasUsed)
return json.Marshal(&enc)
}
func (r *Receipt) UnmarshalJSON(input []byte) error {
type ReceiptJSON struct {
PostState hexutil.Bytes `json:"root"`
CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed"`
Bloom *Bloom `json:"logsBloom"`
Logs []*Log `json:"logs"`
TxHash *common.Hash `json:"transactionHash"`
ContractAddress *common.Address `json:"contractAddress" optional:"true"`
GasUsed *hexutil.Big `json:"gasUsed"`
}
var dec ReceiptJSON
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
var x Receipt
if dec.PostState == nil {
return errors.New("missing required field 'root' for Receipt")
}
x.PostState = dec.PostState
if dec.CumulativeGasUsed == nil {
return errors.New("missing required field 'cumulativeGasUsed' for Receipt")
}
x.CumulativeGasUsed = (*big.Int)(dec.CumulativeGasUsed)
if dec.Bloom == nil {
return errors.New("missing required field 'logsBloom' for Receipt")
}
x.Bloom = *dec.Bloom
if dec.Logs == nil {
return errors.New("missing required field 'logs' for Receipt")
}
x.Logs = dec.Logs
if dec.TxHash == nil {
return errors.New("missing required field 'transactionHash' for Receipt")
}
x.TxHash = *dec.TxHash
if dec.ContractAddress != nil {
x.ContractAddress = *dec.ContractAddress
}
if dec.GasUsed == nil {
return errors.New("missing required field 'gasUsed' for Receipt")
}
x.GasUsed = (*big.Int)(dec.GasUsed)
*r = x
return nil
}

99
core/types/gen_tx_json.go Normal file
View File

@ -0,0 +1,99 @@
// generated by github.com/fjl/gencodec, do not edit.
package types
import (
"encoding/json"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)
func (t *txdata) MarshalJSON() ([]byte, error) {
type txdataJSON struct {
AccountNonce *hexutil.Uint64 `json:"nonce"`
Price *hexutil.Big `json:"gasPrice"`
GasLimit *hexutil.Big `json:"gasLimit"`
Recipient *common.Address `json:"to" optional:"yes" rlp:"nil"`
Amount *hexutil.Big `json:"value"`
Payload hexutil.Bytes `json:"input"`
V *hexutil.Big `json:"v"`
R *hexutil.Big `json:"r"`
S *hexutil.Big `json:"s"`
Hash *common.Hash `json:"hash" optional:"yes" rlp:"-"`
}
var enc txdataJSON
enc.AccountNonce = (*hexutil.Uint64)(&t.AccountNonce)
enc.Price = (*hexutil.Big)(t.Price)
enc.GasLimit = (*hexutil.Big)(t.GasLimit)
enc.Recipient = t.Recipient
enc.Amount = (*hexutil.Big)(t.Amount)
enc.Payload = t.Payload
enc.V = (*hexutil.Big)(t.V)
enc.R = (*hexutil.Big)(t.R)
enc.S = (*hexutil.Big)(t.S)
enc.Hash = t.Hash
return json.Marshal(&enc)
}
func (t *txdata) UnmarshalJSON(input []byte) error {
type txdataJSON struct {
AccountNonce *hexutil.Uint64 `json:"nonce"`
Price *hexutil.Big `json:"gasPrice"`
GasLimit *hexutil.Big `json:"gasLimit"`
Recipient *common.Address `json:"to" optional:"yes" rlp:"nil"`
Amount *hexutil.Big `json:"value"`
Payload hexutil.Bytes `json:"input"`
V *hexutil.Big `json:"v"`
R *hexutil.Big `json:"r"`
S *hexutil.Big `json:"s"`
Hash *common.Hash `json:"hash" optional:"yes" rlp:"-"`
}
var dec txdataJSON
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
var x txdata
if dec.AccountNonce == nil {
return errors.New("missing required field 'nonce' for txdata")
}
x.AccountNonce = uint64(*dec.AccountNonce)
if dec.Price == nil {
return errors.New("missing required field 'gasPrice' for txdata")
}
x.Price = (*big.Int)(dec.Price)
if dec.GasLimit == nil {
return errors.New("missing required field 'gasLimit' for txdata")
}
x.GasLimit = (*big.Int)(dec.GasLimit)
if dec.Recipient != nil {
x.Recipient = dec.Recipient
}
if dec.Amount == nil {
return errors.New("missing required field 'value' for txdata")
}
x.Amount = (*big.Int)(dec.Amount)
if dec.Payload == nil {
return errors.New("missing required field 'input' for txdata")
}
x.Payload = dec.Payload
if dec.V == nil {
return errors.New("missing required field 'v' for txdata")
}
x.V = (*big.Int)(dec.V)
if dec.R == nil {
return errors.New("missing required field 'r' for txdata")
}
x.R = (*big.Int)(dec.R)
if dec.S == nil {
return errors.New("missing required field 's' for txdata")
}
x.S = (*big.Int)(dec.S)
if dec.Hash != nil {
x.Hash = dec.Hash
}
*t = x
return nil
}

View File

@ -17,8 +17,6 @@
package types package types
import ( import (
"encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
@ -27,27 +25,42 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
var errMissingLogFields = errors.New("missing required JSON log fields") //go:generate gencodec -type Log -field-override logMarshaling -out gen_log_json.go
// Log represents a contract log event. These events are generated by the LOG opcode and // Log represents a contract log event. These events are generated by the LOG opcode and
// stored/indexed by the node. // stored/indexed by the node.
type Log struct { type Log struct {
// Consensus fields. // Consensus fields:
Address common.Address // address of the contract that generated the event // address of the contract that generated the event
Topics []common.Hash // list of topics provided by the contract. Address common.Address `json:"address"`
Data []byte // supplied by the contract, usually ABI-encoded // list of topics provided by the contract.
Topics []common.Hash `json:"topics"`
// supplied by the contract, usually ABI-encoded
Data []byte `json:"data"`
// Derived fields. These fields are filled in by the node // Derived fields. These fields are filled in by the node
// but not secured by consensus. // but not secured by consensus.
BlockNumber uint64 // block in which the transaction was included // block in which the transaction was included
TxHash common.Hash // hash of the transaction BlockNumber uint64 `json:"blockNumber" optional:"yes"`
TxIndex uint // index of the transaction in the block // hash of the transaction
BlockHash common.Hash // hash of the block in which the transaction was included TxHash common.Hash `json:"transactionHash"`
Index uint // index of the log in the receipt // index of the transaction in the block
TxIndex uint `json:"transactionIndex"`
// hash of the block in which the transaction was included
BlockHash common.Hash `json:"blockHash" optional:"yes"`
// index of the log in the receipt
Index uint `json:"logIndex"`
// The Removed field is true if this log was reverted due to a chain reorganisation. // The Removed field is true if this log was reverted due to a chain reorganisation.
// You must pay attention to this field if you receive logs through a filter query. // You must pay attention to this field if you receive logs through a filter query.
Removed bool Removed bool `json:"removed" optional:"yes"`
}
type logMarshaling struct {
Data hexutil.Bytes
BlockNumber hexutil.Uint64
TxIndex hexutil.Uint
Index hexutil.Uint
} }
type rlpLog struct { type rlpLog struct {
@ -67,18 +80,6 @@ type rlpStorageLog struct {
Index uint Index uint
} }
type jsonLog struct {
Address *common.Address `json:"address"`
Topics *[]common.Hash `json:"topics"`
Data *hexutil.Bytes `json:"data"`
BlockNumber *hexutil.Uint64 `json:"blockNumber"`
TxIndex *hexutil.Uint `json:"transactionIndex"`
TxHash *common.Hash `json:"transactionHash"`
BlockHash *common.Hash `json:"blockHash"`
Index *hexutil.Uint `json:"logIndex"`
Removed bool `json:"removed"`
}
// EncodeRLP implements rlp.Encoder. // EncodeRLP implements rlp.Encoder.
func (l *Log) EncodeRLP(w io.Writer) error { func (l *Log) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data}) return rlp.Encode(w, rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data})
@ -98,54 +99,6 @@ func (l *Log) String() string {
return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, l.Address, l.Topics, l.Data, l.TxHash, l.TxIndex, l.BlockHash, l.Index) return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, l.Address, l.Topics, l.Data, l.TxHash, l.TxIndex, l.BlockHash, l.Index)
} }
// MarshalJSON implements json.Marshaler.
func (l *Log) MarshalJSON() ([]byte, error) {
jslog := &jsonLog{
Address: &l.Address,
Topics: &l.Topics,
Data: (*hexutil.Bytes)(&l.Data),
TxIndex: (*hexutil.Uint)(&l.TxIndex),
TxHash: &l.TxHash,
Index: (*hexutil.Uint)(&l.Index),
Removed: l.Removed,
}
// Set block information for mined logs.
if (l.BlockHash != common.Hash{}) {
jslog.BlockHash = &l.BlockHash
jslog.BlockNumber = (*hexutil.Uint64)(&l.BlockNumber)
}
return json.Marshal(jslog)
}
// UnmarshalJSON implements json.Umarshaler.
func (l *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 == nil ||
dec.TxIndex == nil || dec.TxHash == nil || dec.Index == nil {
return errMissingLogFields
}
declog := Log{
Address: *dec.Address,
Topics: *dec.Topics,
Data: *dec.Data,
TxHash: *dec.TxHash,
TxIndex: uint(*dec.TxIndex),
Index: uint(*dec.Index),
Removed: dec.Removed,
}
// Block information may be missing if the log is received through
// the pending log filter, so it's handled specially here.
if dec.BlockHash != nil && dec.BlockNumber != nil {
declog.BlockHash = *dec.BlockHash
declog.BlockNumber = uint64(*dec.BlockNumber)
}
*l = declog
return nil
}
// LogForStorage is a wrapper around a Log that flattens and parses the entire content of // LogForStorage is a wrapper around a Log that flattens and parses the entire content of
// a log including non-consensus fields. // a log including non-consensus fields.
type LogForStorage Log type LogForStorage Log

View File

@ -18,6 +18,7 @@ package types
import ( import (
"encoding/json" "encoding/json"
"fmt"
"reflect" "reflect"
"testing" "testing"
@ -96,7 +97,7 @@ var unmarshalLogTests = map[string]struct {
}, },
"missing data": { "missing data": {
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`, input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
wantError: errMissingLogFields, wantError: fmt.Errorf("missing required field 'data' for Log"),
}, },
} }

View File

@ -17,8 +17,6 @@
package types package types
import ( import (
"encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"math/big" "math/big"
@ -28,33 +26,26 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
var ( //go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go
errMissingReceiptPostState = errors.New("missing post state root in JSON receipt")
errMissingReceiptFields = errors.New("missing required JSON receipt fields")
)
// Receipt represents the results of a transaction. // Receipt represents the results of a transaction.
type Receipt struct { type Receipt struct {
// Consensus fields // Consensus fields
PostState []byte PostState []byte `json:"root"`
CumulativeGasUsed *big.Int CumulativeGasUsed *big.Int `json:"cumulativeGasUsed"`
Bloom Bloom Bloom Bloom `json:"logsBloom"`
Logs []*Log Logs []*Log `json:"logs"`
// Implementation fields (don't reorder!) // Implementation fields (don't reorder!)
TxHash common.Hash TxHash common.Hash `json:"transactionHash"`
ContractAddress common.Address ContractAddress common.Address `json:"contractAddress" optional:"true"`
GasUsed *big.Int GasUsed *big.Int `json:"gasUsed"`
} }
type jsonReceipt struct { type receiptMarshaling struct {
PostState *common.Hash `json:"root"` PostState hexutil.Bytes
CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed"` CumulativeGasUsed *hexutil.Big
Bloom *Bloom `json:"logsBloom"` GasUsed *hexutil.Big
Logs []*Log `json:"logs"`
TxHash *common.Hash `json:"transactionHash"`
ContractAddress *common.Address `json:"contractAddress"`
GasUsed *hexutil.Big `json:"gasUsed"`
} }
// NewReceipt creates a barebone transaction receipt, copying the init fields. // NewReceipt creates a barebone transaction receipt, copying the init fields.
@ -84,51 +75,6 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
return nil return nil
} }
// MarshalJSON encodes receipts into the web3 RPC response block format.
func (r *Receipt) MarshalJSON() ([]byte, error) {
root := common.BytesToHash(r.PostState)
return json.Marshal(&jsonReceipt{
PostState: &root,
CumulativeGasUsed: (*hexutil.Big)(r.CumulativeGasUsed),
Bloom: &r.Bloom,
Logs: r.Logs,
TxHash: &r.TxHash,
ContractAddress: &r.ContractAddress,
GasUsed: (*hexutil.Big)(r.GasUsed),
})
}
// 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
}
// Ensure that all fields are set. PostState is checked separately because it is a
// recent addition to the RPC spec (as of August 2016) and older implementations might
// not provide it. Note that ContractAddress is not checked because it can be null.
if dec.PostState == nil {
return errMissingReceiptPostState
}
if dec.CumulativeGasUsed == nil || dec.Bloom == nil ||
dec.Logs == nil || dec.TxHash == nil || dec.GasUsed == nil {
return errMissingReceiptFields
}
*r = Receipt{
PostState: (*dec.PostState)[:],
CumulativeGasUsed: (*big.Int)(dec.CumulativeGasUsed),
Bloom: *dec.Bloom,
Logs: dec.Logs,
TxHash: *dec.TxHash,
GasUsed: (*big.Int)(dec.GasUsed),
}
if dec.ContractAddress != nil {
r.ContractAddress = *dec.ContractAddress
}
return nil
}
// String implements the Stringer interface. // String implements the Stringer interface.
func (r *Receipt) String() string { func (r *Receipt) String() string {
return fmt.Sprintf("receipt{med=%x cgas=%v bloom=%x logs=%v}", r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs) return fmt.Sprintf("receipt{med=%x cgas=%v bloom=%x logs=%v}", r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs)

View File

@ -18,7 +18,6 @@ package types
import ( import (
"container/heap" "container/heap"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -32,12 +31,11 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
var ErrInvalidSig = errors.New("invalid transaction v, r, s values") //go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go
var ( var (
errMissingTxSignatureFields = errors.New("missing required JSON transaction signature fields") ErrInvalidSig = errors.New("invalid transaction v, r, s values")
errMissingTxFields = errors.New("missing required JSON transaction fields") errNoSigner = errors.New("missing signing methods")
errNoSigner = errors.New("missing signing methods")
) )
// deriveSigner makes a *best* guess about which signer to use. // deriveSigner makes a *best* guess about which signer to use.
@ -58,26 +56,31 @@ type Transaction struct {
} }
type txdata struct { type txdata struct {
AccountNonce uint64 AccountNonce uint64 `json:"nonce"`
Price, GasLimit *big.Int Price *big.Int `json:"gasPrice"`
Recipient *common.Address `rlp:"nil"` // nil means contract creation GasLimit *big.Int `json:"gasLimit"`
Amount *big.Int Recipient *common.Address `json:"to" optional:"yes" rlp:"nil"` // nil means contract creation
Payload []byte Amount *big.Int `json:"value"`
V *big.Int // signature Payload []byte `json:"input"`
R, S *big.Int // signature
// Signature values
V *big.Int `json:"v"`
R *big.Int `json:"r"`
S *big.Int `json:"s"`
// This is only used when marshaling to JSON.
Hash *common.Hash `json:"hash" optional:"yes" rlp:"-"`
} }
type jsonTransaction struct { type txdataMarshaling struct {
Hash *common.Hash `json:"hash"` AccountNonce hexutil.Uint64
AccountNonce *hexutil.Uint64 `json:"nonce"` Price *hexutil.Big
Price *hexutil.Big `json:"gasPrice"` GasLimit *hexutil.Big
GasLimit *hexutil.Big `json:"gas"` Amount *hexutil.Big
Recipient *common.Address `json:"to"` Payload hexutil.Bytes
Amount *hexutil.Big `json:"value"` V *hexutil.Big
Payload *hexutil.Bytes `json:"input"` R *hexutil.Big
V *hexutil.Big `json:"v"` S *hexutil.Big
R *hexutil.Big `json:"r"`
S *hexutil.Big `json:"s"`
} }
func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction { func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
@ -164,66 +167,30 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
return err return err
} }
// MarshalJSON encodes transactions into the web3 RPC response block format.
func (tx *Transaction) MarshalJSON() ([]byte, error) { func (tx *Transaction) MarshalJSON() ([]byte, error) {
hash := tx.Hash() hash := tx.Hash()
data := tx.data
return json.Marshal(&jsonTransaction{ data.Hash = &hash
Hash: &hash, return data.MarshalJSON()
AccountNonce: (*hexutil.Uint64)(&tx.data.AccountNonce),
Price: (*hexutil.Big)(tx.data.Price),
GasLimit: (*hexutil.Big)(tx.data.GasLimit),
Recipient: tx.data.Recipient,
Amount: (*hexutil.Big)(tx.data.Amount),
Payload: (*hexutil.Bytes)(&tx.data.Payload),
V: (*hexutil.Big)(tx.data.V),
R: (*hexutil.Big)(tx.data.R),
S: (*hexutil.Big)(tx.data.S),
})
} }
// UnmarshalJSON decodes the web3 RPC transaction format. // UnmarshalJSON decodes the web3 RPC transaction format.
func (tx *Transaction) UnmarshalJSON(input []byte) error { func (tx *Transaction) UnmarshalJSON(input []byte) error {
var dec jsonTransaction var dec txdata
if err := json.Unmarshal(input, &dec); err != nil { if err := dec.UnmarshalJSON(input); err != nil {
return err 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
}
var V byte var V byte
if isProtectedV((*big.Int)(dec.V)) { if isProtectedV(dec.V) {
chainId := deriveChainId((*big.Int)(dec.V)).Uint64() chainId := deriveChainId(dec.V).Uint64()
V = byte(dec.V.ToInt().Uint64() - 35 - 2*chainId) V = byte(dec.V.Uint64() - 35 - 2*chainId)
} else { } else {
V = byte(((*big.Int)(dec.V)).Uint64() - 27) V = byte(dec.V.Uint64() - 27)
} }
if !crypto.ValidateSignatureValues(V, (*big.Int)(dec.R), (*big.Int)(dec.S), false) { if !crypto.ValidateSignatureValues(V, dec.R, dec.S, false) {
return ErrInvalidSig return ErrInvalidSig
} }
*tx = Transaction{data: dec}
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: (*big.Int)(dec.V),
R: (*big.Int)(dec.R),
S: (*big.Int)(dec.S),
}
return nil return nil
} }

View File

@ -84,7 +84,7 @@ func bignumberJs() (*asset, error) {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "bignumber.js", size: 17314, mode: os.FileMode(420), modTime: time.Unix(1484232218, 0)} info := bindataFileInfo{name: "bignumber.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
@ -104,7 +104,7 @@ func web3Js() (*asset, error) {
return nil, err return nil, err
} }
info := bindataFileInfo{name: "web3.js", size: 491740, mode: os.FileMode(420), modTime: time.Unix(1484232456, 0)} info := bindataFileInfo{name: "web3.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }

View File

@ -17,4 +17,5 @@
// Package deps contains the console JavaScript dependencies Go embedded. // Package deps contains the console JavaScript dependencies Go embedded.
package deps package deps
//go:generate go-bindata -o bindata.go bignumber.js web3.js //go:generate go-bindata -nometadata -pkg deps -o bindata.go bignumber.js web3.js
//go:generate gofmt -w -s bindata.go

View File

@ -63,12 +63,16 @@ type Decoder interface {
// must contain an element for each decoded field. Decode returns an // must contain an element for each decoded field. Decode returns an
// error if there are too few or too many elements. // error if there are too few or too many elements.
// //
// The decoding of struct fields honours two struct tags, "tail" and // The decoding of struct fields honours certain struct tags, "tail",
// "nil". For an explanation of "tail", see the example. // "nil" and "-".
// The "nil" tag applies to pointer-typed fields and changes the //
// decoding rules for the field such that input values of size zero // The "-" tag ignores fields.
// decode as a nil pointer. This tag can be useful when decoding //
// recursive types. // For an explanation of "tail", see the example.
//
// The "nil" tag applies to pointer-typed fields and changes the decoding
// rules for the field such that input values of size zero decode as a nil
// pointer. This tag can be useful when decoding recursive types.
// //
// type StructWithEmptyOK struct { // type StructWithEmptyOK struct {
// Foo *[20]byte `rlp:"nil"` // Foo *[20]byte `rlp:"nil"`

View File

@ -339,6 +339,12 @@ var (
) )
) )
type hasIgnoredField struct {
A uint
B uint `rlp:"-"`
C uint
}
var decodeTests = []decodeTest{ var decodeTests = []decodeTest{
// booleans // booleans
{input: "01", ptr: new(bool), value: true}, {input: "01", ptr: new(bool), value: true},
@ -490,6 +496,13 @@ var decodeTests = []decodeTest{
value: tailRaw{A: 1, Tail: []RawValue{}}, value: tailRaw{A: 1, Tail: []RawValue{}},
}, },
// struct tag "-"
{
input: "C20102",
ptr: new(hasIgnoredField),
value: hasIgnoredField{A: 1, C: 2},
},
// RawValue // RawValue
{input: "01", ptr: new(RawValue), value: RawValue(unhex("01"))}, {input: "01", ptr: new(RawValue), value: RawValue(unhex("01"))},
{input: "82FFFF", ptr: new(RawValue), value: RawValue(unhex("82FFFF"))}, {input: "82FFFF", ptr: new(RawValue), value: RawValue(unhex("82FFFF"))},

View File

@ -218,6 +218,7 @@ var encTests = []encTest{
{val: &tailRaw{A: 1, Tail: []RawValue{unhex("02")}}, output: "C20102"}, {val: &tailRaw{A: 1, Tail: []RawValue{unhex("02")}}, output: "C20102"},
{val: &tailRaw{A: 1, Tail: []RawValue{}}, output: "C101"}, {val: &tailRaw{A: 1, Tail: []RawValue{}}, output: "C101"},
{val: &tailRaw{A: 1, Tail: nil}, output: "C101"}, {val: &tailRaw{A: 1, Tail: nil}, output: "C101"},
{val: &hasIgnoredField{A: 1, B: 2, C: 3}, output: "C20103"},
// nil // nil
{val: (*uint)(nil), output: "80"}, {val: (*uint)(nil), output: "80"},

View File

@ -37,11 +37,12 @@ type typeinfo struct {
type tags struct { type tags struct {
// rlp:"nil" controls whether empty input results in a nil pointer. // rlp:"nil" controls whether empty input results in a nil pointer.
nilOK bool nilOK bool
// rlp:"tail" controls whether this field swallows additional list // rlp:"tail" controls whether this field swallows additional list
// elements. It can only be set for the last field, which must be // elements. It can only be set for the last field, which must be
// of slice type. // of slice type.
tail bool tail bool
// rlp:"-" ignores fields.
ignored bool
} }
type typekey struct { type typekey struct {
@ -101,6 +102,9 @@ func structFields(typ reflect.Type) (fields []field, err error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if tags.ignored {
continue
}
info, err := cachedTypeInfo1(f.Type, tags) info, err := cachedTypeInfo1(f.Type, tags)
if err != nil { if err != nil {
return nil, err return nil, err
@ -117,6 +121,8 @@ func parseStructTag(typ reflect.Type, fi int) (tags, error) {
for _, t := range strings.Split(f.Tag.Get("rlp"), ",") { for _, t := range strings.Split(f.Tag.Get("rlp"), ",") {
switch t = strings.TrimSpace(t); t { switch t = strings.TrimSpace(t); t {
case "": case "":
case "-":
ts.ignored = true
case "nil": case "nil":
ts.nilOK = true ts.nilOK = true
case "tail": case "tail":