mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
Merge github.com:holiman/go-ethereum
This commit is contained in:
commit
ad6f304d80
36 changed files with 1548 additions and 725 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
1.5.4
|
||||
1.5.5
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@ func (ac *addrCache) scan() ([]Account, error) {
|
|||
buf = new(bufio.Reader)
|
||||
addrs []Account
|
||||
keyJSON struct {
|
||||
Address common.Address `json:"address"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
)
|
||||
for _, fi := range files {
|
||||
|
|
@ -241,15 +241,16 @@ func (ac *addrCache) scan() ([]Account, error) {
|
|||
}
|
||||
buf.Reset(fd)
|
||||
// Parse the address.
|
||||
keyJSON.Address = common.Address{}
|
||||
keyJSON.Address = ""
|
||||
err = json.NewDecoder(buf).Decode(&keyJSON)
|
||||
addr := common.HexToAddress(keyJSON.Address)
|
||||
switch {
|
||||
case err != nil:
|
||||
glog.V(logger.Debug).Infof("can't decode key %s: %v", path, err)
|
||||
case (keyJSON.Address == common.Address{}):
|
||||
case (addr == common.Address{}):
|
||||
glog.V(logger.Debug).Infof("can't decode key %s: missing or zero address", path)
|
||||
default:
|
||||
addrs = append(addrs, Account{Address: keyJSON.Address, File: path})
|
||||
addrs = append(addrs, Account{Address: addr, File: path})
|
||||
}
|
||||
fd.Close()
|
||||
}
|
||||
|
|
|
|||
27
build/ci.go
27
build/ci.go
|
|
@ -72,6 +72,9 @@ var (
|
|||
executablePath("abigen"),
|
||||
executablePath("evm"),
|
||||
executablePath("geth"),
|
||||
executablePath("bzzd"),
|
||||
executablePath("bzzhash"),
|
||||
executablePath("bzzup"),
|
||||
executablePath("rlpdump"),
|
||||
}
|
||||
|
||||
|
|
@ -89,6 +92,18 @@ var (
|
|||
Name: "evm",
|
||||
Description: "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode.",
|
||||
},
|
||||
{
|
||||
Name: "bzzd",
|
||||
Description: "Ethereum Swarm daemon",
|
||||
},
|
||||
{
|
||||
Name: "bzzup",
|
||||
Description: "Ethereum Swarm command line file/directory uploader",
|
||||
},
|
||||
{
|
||||
Name: "bzzhash",
|
||||
Description: "Ethereum Swarm file/directory hash calculator",
|
||||
},
|
||||
{
|
||||
Name: "abigen",
|
||||
Description: "Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages.",
|
||||
|
|
@ -811,13 +826,12 @@ func doXCodeFramework(cmdline []string) {
|
|||
// Prepare and upload a PodSpec to CocoaPods
|
||||
if *deploy != "" {
|
||||
meta := newPodMetadata(env, archive)
|
||||
build.Render("build/pod.podspec", meta.Name+".podspec", 0755, meta)
|
||||
build.MustRunCommand("pod", *deploy, "push", meta.Name+".podspec", "--allow-warnings", "--verbose")
|
||||
build.Render("build/pod.podspec", "Geth.podspec", 0755, meta)
|
||||
build.MustRunCommand("pod", *deploy, "push", "Geth.podspec", "--allow-warnings", "--verbose")
|
||||
}
|
||||
}
|
||||
|
||||
type podMetadata struct {
|
||||
Name string
|
||||
Version string
|
||||
Commit string
|
||||
Archive string
|
||||
|
|
@ -850,14 +864,13 @@ func newPodMetadata(env build.Environment, archive string) podMetadata {
|
|||
}
|
||||
}
|
||||
}
|
||||
name := "Geth"
|
||||
version := build.VERSION()
|
||||
if isUnstableBuild(env) {
|
||||
name += "Develop"
|
||||
version += "-unstable." + env.Buildnum
|
||||
}
|
||||
return podMetadata{
|
||||
Name: name,
|
||||
Archive: archive,
|
||||
Version: build.VERSION(),
|
||||
Version: version,
|
||||
Commit: env.Commit,
|
||||
Contributors: contribs,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
Pod::Spec.new do |spec|
|
||||
spec.name = '{{.Name}}'
|
||||
spec.name = 'Geth'
|
||||
spec.version = '{{.Version}}'
|
||||
spec.license = { :type => 'GNU Lesser General Public License, Version 3.0' }
|
||||
spec.homepage = 'https://github.com/ethereum/go-ethereum'
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
|
|
@ -38,6 +39,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/swarm"
|
||||
bzzapi "github.com/ethereum/go-ethereum/swarm/api"
|
||||
"github.com/ethereum/go-ethereum/swarm/network"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
|
|
@ -61,17 +63,22 @@ var (
|
|||
Name: "bzzport",
|
||||
Usage: "Swarm local http api port",
|
||||
}
|
||||
SwarmNetworkIdFlag = cli.IntFlag{
|
||||
Name: "bzznetworkid",
|
||||
Usage: "Network identifier (integer, default 322=swarm testnet)",
|
||||
Value: network.NetworkId,
|
||||
}
|
||||
SwarmConfigPathFlag = cli.StringFlag{
|
||||
Name: "bzzconfig",
|
||||
Usage: "Swarm config file path (datadir/bzz)",
|
||||
}
|
||||
SwarmSwapDisabled = cli.BoolFlag{
|
||||
Name: "bzznoswap",
|
||||
Usage: "Swarm SWAP disabled (default false)",
|
||||
SwarmSwapEnabled = cli.BoolFlag{
|
||||
Name: "swap",
|
||||
Usage: "Swarm SWAP enabled (default false)",
|
||||
}
|
||||
SwarmSyncDisabled = cli.BoolFlag{
|
||||
Name: "bzznosync",
|
||||
Usage: "Swarm Syncing disabled (default false)",
|
||||
SwarmSyncEnabled = cli.BoolTFlag{
|
||||
Name: "sync",
|
||||
Usage: "Swarm Syncing enabled (default true)",
|
||||
}
|
||||
EthAPI = cli.StringFlag{
|
||||
Name: "ethapi",
|
||||
|
|
@ -86,6 +93,7 @@ func init() {
|
|||
// Override flag defaults so bzzd can run alongside geth.
|
||||
utils.ListenPortFlag.Value = 30399
|
||||
utils.IPCPathFlag.Value = utils.DirectoryString{Value: "bzzd.ipc"}
|
||||
utils.IPCApiFlag.Value = "admin, bzz, chequebook, debug, rpc, web3"
|
||||
|
||||
// Set up the cli app.
|
||||
app.Commands = nil
|
||||
|
|
@ -96,21 +104,24 @@ func init() {
|
|||
utils.BootnodesFlag,
|
||||
utils.KeyStoreDirFlag,
|
||||
utils.ListenPortFlag,
|
||||
utils.NoDiscoverFlag,
|
||||
utils.DiscoveryV5Flag,
|
||||
utils.NetrestrictFlag,
|
||||
utils.MaxPeersFlag,
|
||||
utils.NATFlag,
|
||||
utils.NodeKeyFileFlag,
|
||||
utils.NodeKeyHexFlag,
|
||||
utils.MaxPeersFlag,
|
||||
utils.NATFlag,
|
||||
utils.IPCDisabledFlag,
|
||||
utils.IPCApiFlag,
|
||||
utils.IPCPathFlag,
|
||||
// bzzd-specific flags
|
||||
EthAPI,
|
||||
SwarmConfigPathFlag,
|
||||
SwarmSwapDisabled,
|
||||
SwarmSyncDisabled,
|
||||
SwarmSwapEnabled,
|
||||
SwarmSyncEnabled,
|
||||
SwarmPortFlag,
|
||||
SwarmAccountFlag,
|
||||
SwarmNetworkIdFlag,
|
||||
ChequebookAddrFlag,
|
||||
}
|
||||
app.Flags = append(app.Flags, debug.Flags...)
|
||||
|
|
@ -138,7 +149,8 @@ func bzzd(ctx *cli.Context) error {
|
|||
|
||||
// Add bootnodes as initial peers.
|
||||
if ctx.GlobalIsSet(utils.BootnodesFlag.Name) {
|
||||
injectBootnodes(stack.Server(), ctx.GlobalStringSlice(utils.BootnodesFlag.Name))
|
||||
bootnodes := strings.Split(ctx.GlobalString(utils.BootnodesFlag.Name), ",")
|
||||
injectBootnodes(stack.Server(), bootnodes)
|
||||
} else {
|
||||
injectBootnodes(stack.Server(), defaultBootnodes)
|
||||
}
|
||||
|
|
@ -155,7 +167,7 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) {
|
|||
if bzzdir == "" {
|
||||
bzzdir = stack.InstanceDir()
|
||||
}
|
||||
bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey)
|
||||
bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey, ctx.GlobalUint64(SwarmNetworkIdFlag.Name))
|
||||
if err != nil {
|
||||
utils.Fatalf("unable to configure swarm: %v", err)
|
||||
}
|
||||
|
|
@ -163,16 +175,18 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) {
|
|||
if len(bzzport) > 0 {
|
||||
bzzconfig.Port = bzzport
|
||||
}
|
||||
swapEnabled := !ctx.GlobalBool(SwarmSwapDisabled.Name)
|
||||
syncEnabled := !ctx.GlobalBool(SwarmSyncDisabled.Name)
|
||||
swapEnabled := ctx.GlobalBool(SwarmSwapEnabled.Name)
|
||||
syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabled.Name)
|
||||
|
||||
ethapi := ctx.GlobalString(EthAPI.Name)
|
||||
if ethapi == "" {
|
||||
utils.Fatalf("Option %q must not be empty", EthAPI.Name)
|
||||
}
|
||||
|
||||
boot := func(ctx *node.ServiceContext) (node.Service, error) {
|
||||
client, err := ethclient.Dial(ethapi)
|
||||
var client *ethclient.Client
|
||||
if ethapi == "" {
|
||||
err = fmt.Errorf("use ethapi flag to connect to a an eth client and talk to the blockchain")
|
||||
} else {
|
||||
client, err = ethclient.Dial(ethapi)
|
||||
}
|
||||
if err != nil {
|
||||
utils.Fatalf("Can't connect: %v", err)
|
||||
}
|
||||
|
|
@ -241,6 +255,7 @@ func injectBootnodes(srv *p2p.Server, nodes []string) {
|
|||
n, err := discover.ParseNode(url)
|
||||
if err != nil {
|
||||
glog.Errorf("invalid bootnode %q", err)
|
||||
continue
|
||||
}
|
||||
srv.AddPeer(n)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ var (
|
|||
}
|
||||
NetworkIdFlag = cli.IntFlag{
|
||||
Name: "networkid",
|
||||
Usage: "Network identifier (integer, 0=Olympic, 1=Frontier, 2=Morden)",
|
||||
Usage: "Network identifier (integer, 0=Olympic (disused), 1=Frontier, 2=Morden (disused), 3=Ropsten)",
|
||||
Value: eth.NetworkId,
|
||||
}
|
||||
OlympicFlag = cli.BoolFlag{
|
||||
|
|
|
|||
232
common/hexutil/hexutil.go
Normal file
232
common/hexutil/hexutil.go
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/*
|
||||
Package hexutil implements hex encoding with 0x prefix.
|
||||
This encoding is used by the Ethereum RPC API to transport binary data in JSON payloads.
|
||||
|
||||
Encoding Rules
|
||||
|
||||
All hex data must have prefix "0x".
|
||||
|
||||
For byte slices, the hex data must be of even length. An empty byte slice
|
||||
encodes as "0x".
|
||||
|
||||
Integers are encoded using the least amount of digits (no leading zero digits). Their
|
||||
encoding may be of uneven length. The number zero encodes as "0x0".
|
||||
*/
|
||||
package hexutil
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const uintBits = 32 << (uint64(^uint(0)) >> 63)
|
||||
|
||||
var (
|
||||
ErrEmptyString = errors.New("empty hex string")
|
||||
ErrMissingPrefix = errors.New("missing 0x prefix for hex data")
|
||||
ErrSyntax = errors.New("invalid hex")
|
||||
ErrEmptyNumber = errors.New("hex number has no digits after 0x")
|
||||
ErrLeadingZero = errors.New("hex number has leading zero digits after 0x")
|
||||
ErrOddLength = errors.New("hex string has odd length")
|
||||
ErrUint64Range = errors.New("hex number does not fit into 64 bits")
|
||||
ErrUintRange = fmt.Errorf("hex number does not fit into %d bits", uintBits)
|
||||
)
|
||||
|
||||
// Decode decodes a hex string with 0x prefix.
|
||||
func Decode(input string) ([]byte, error) {
|
||||
if len(input) == 0 {
|
||||
return nil, ErrEmptyString
|
||||
}
|
||||
if !has0xPrefix(input) {
|
||||
return nil, ErrMissingPrefix
|
||||
}
|
||||
return hex.DecodeString(input[2:])
|
||||
}
|
||||
|
||||
// MustDecode decodes a hex string with 0x prefix. It panics for invalid input.
|
||||
func MustDecode(input string) []byte {
|
||||
dec, err := Decode(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
// Encode encodes b as a hex string with 0x prefix.
|
||||
func Encode(b []byte) string {
|
||||
enc := make([]byte, len(b)*2+2)
|
||||
copy(enc, "0x")
|
||||
hex.Encode(enc[2:], b)
|
||||
return string(enc)
|
||||
}
|
||||
|
||||
// DecodeUint64 decodes a hex string with 0x prefix as a quantity.
|
||||
func DecodeUint64(input string) (uint64, error) {
|
||||
raw, err := checkNumber(input)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
dec, err := strconv.ParseUint(raw, 16, 64)
|
||||
if err != nil {
|
||||
err = mapError(err)
|
||||
}
|
||||
return dec, err
|
||||
}
|
||||
|
||||
// MustDecodeUint64 decodes a hex string with 0x prefix as a quantity.
|
||||
// It panics for invalid input.
|
||||
func MustDecodeUint64(input string) uint64 {
|
||||
dec, err := DecodeUint64(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
// EncodeUint64 encodes i as a hex string with 0x prefix.
|
||||
func EncodeUint64(i uint64) string {
|
||||
enc := make([]byte, 2, 10)
|
||||
copy(enc, "0x")
|
||||
return string(strconv.AppendUint(enc, i, 16))
|
||||
}
|
||||
|
||||
var bigWordNibbles int
|
||||
|
||||
func init() {
|
||||
// This is a weird way to compute the number of nibbles required for big.Word.
|
||||
// The usual way would be to use constant arithmetic but go vet can't handle that.
|
||||
b, _ := new(big.Int).SetString("FFFFFFFFFF", 16)
|
||||
switch len(b.Bits()) {
|
||||
case 1:
|
||||
bigWordNibbles = 16
|
||||
case 2:
|
||||
bigWordNibbles = 8
|
||||
default:
|
||||
panic("weird big.Word size")
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeBig decodes a hex string with 0x prefix as a quantity.
|
||||
func DecodeBig(input string) (*big.Int, error) {
|
||||
raw, err := checkNumber(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
words := make([]big.Word, len(raw)/bigWordNibbles+1)
|
||||
end := len(raw)
|
||||
for i := range words {
|
||||
start := end - bigWordNibbles
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
for ri := start; ri < end; ri++ {
|
||||
nib := decodeNibble(raw[ri])
|
||||
if nib == badNibble {
|
||||
return nil, ErrSyntax
|
||||
}
|
||||
words[i] *= 16
|
||||
words[i] += big.Word(nib)
|
||||
}
|
||||
end = start
|
||||
}
|
||||
dec := new(big.Int).SetBits(words)
|
||||
return dec, nil
|
||||
}
|
||||
|
||||
// MustDecodeBig decodes a hex string with 0x prefix as a quantity.
|
||||
// It panics for invalid input.
|
||||
func MustDecodeBig(input string) *big.Int {
|
||||
dec, err := DecodeBig(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
// EncodeBig encodes bigint as a hex string with 0x prefix.
|
||||
// The sign of the integer is ignored.
|
||||
func EncodeBig(bigint *big.Int) string {
|
||||
nbits := bigint.BitLen()
|
||||
if nbits == 0 {
|
||||
return "0x0"
|
||||
}
|
||||
enc := make([]byte, 2, (nbits/8)*2+2)
|
||||
copy(enc, "0x")
|
||||
for i := len(bigint.Bits()) - 1; i >= 0; i-- {
|
||||
enc = strconv.AppendUint(enc, uint64(bigint.Bits()[i]), 16)
|
||||
}
|
||||
return string(enc)
|
||||
}
|
||||
|
||||
func has0xPrefix(input string) bool {
|
||||
return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
|
||||
}
|
||||
|
||||
func checkNumber(input string) (raw string, err error) {
|
||||
if len(input) == 0 {
|
||||
return "", ErrEmptyString
|
||||
}
|
||||
if !has0xPrefix(input) {
|
||||
return "", ErrMissingPrefix
|
||||
}
|
||||
input = input[2:]
|
||||
if len(input) == 0 {
|
||||
return "", ErrEmptyNumber
|
||||
}
|
||||
if len(input) > 1 && input[0] == '0' {
|
||||
return "", ErrLeadingZero
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
const badNibble = ^uint64(0)
|
||||
|
||||
func decodeNibble(in byte) uint64 {
|
||||
switch {
|
||||
case in >= '0' && in <= '9':
|
||||
return uint64(in - '0')
|
||||
case in >= 'A' && in <= 'F':
|
||||
return uint64(in - 'A' + 10)
|
||||
case in >= 'a' && in <= 'f':
|
||||
return uint64(in - 'a' + 10)
|
||||
default:
|
||||
return badNibble
|
||||
}
|
||||
}
|
||||
|
||||
func mapError(err error) error {
|
||||
if err, ok := err.(*strconv.NumError); ok {
|
||||
switch err.Err {
|
||||
case strconv.ErrRange:
|
||||
return ErrUint64Range
|
||||
case strconv.ErrSyntax:
|
||||
return ErrSyntax
|
||||
}
|
||||
}
|
||||
if _, ok := err.(hex.InvalidByteError); ok {
|
||||
return ErrSyntax
|
||||
}
|
||||
if err == hex.ErrLength {
|
||||
return ErrOddLength
|
||||
}
|
||||
return err
|
||||
}
|
||||
186
common/hexutil/hexutil_test.go
Normal file
186
common/hexutil/hexutil_test.go
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package hexutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type marshalTest struct {
|
||||
input interface{}
|
||||
want string
|
||||
}
|
||||
|
||||
type unmarshalTest struct {
|
||||
input string
|
||||
want interface{}
|
||||
wantErr error
|
||||
}
|
||||
|
||||
var (
|
||||
encodeBytesTests = []marshalTest{
|
||||
{[]byte{}, "0x"},
|
||||
{[]byte{0}, "0x00"},
|
||||
{[]byte{0, 0, 1, 2}, "0x00000102"},
|
||||
}
|
||||
|
||||
encodeBigTests = []marshalTest{
|
||||
{referenceBig("0"), "0x0"},
|
||||
{referenceBig("1"), "0x1"},
|
||||
{referenceBig("ff"), "0xff"},
|
||||
{referenceBig("112233445566778899aabbccddeeff"), "0x112233445566778899aabbccddeeff"},
|
||||
}
|
||||
|
||||
encodeUint64Tests = []marshalTest{
|
||||
{uint64(0), "0x0"},
|
||||
{uint64(1), "0x1"},
|
||||
{uint64(0xff), "0xff"},
|
||||
{uint64(0x1122334455667788), "0x1122334455667788"},
|
||||
}
|
||||
|
||||
decodeBytesTests = []unmarshalTest{
|
||||
// invalid
|
||||
{input: ``, wantErr: ErrEmptyString},
|
||||
{input: `0`, wantErr: ErrMissingPrefix},
|
||||
{input: `0x0`, wantErr: hex.ErrLength},
|
||||
{input: `0x023`, wantErr: hex.ErrLength},
|
||||
{input: `0xxx`, wantErr: hex.InvalidByteError('x')},
|
||||
{input: `0x01zz01`, wantErr: hex.InvalidByteError('z')},
|
||||
// valid
|
||||
{input: `0x`, want: []byte{}},
|
||||
{input: `0X`, want: []byte{}},
|
||||
{input: `0x02`, want: []byte{0x02}},
|
||||
{input: `0X02`, want: []byte{0x02}},
|
||||
{input: `0xffffffffff`, want: []byte{0xff, 0xff, 0xff, 0xff, 0xff}},
|
||||
{
|
||||
input: `0xffffffffffffffffffffffffffffffffffff`,
|
||||
want: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||
},
|
||||
}
|
||||
|
||||
decodeBigTests = []unmarshalTest{
|
||||
// invalid
|
||||
{input: `0`, wantErr: ErrMissingPrefix},
|
||||
{input: `0x`, wantErr: ErrEmptyNumber},
|
||||
{input: `0x01`, wantErr: ErrLeadingZero},
|
||||
{input: `0xx`, wantErr: ErrSyntax},
|
||||
{input: `0x1zz01`, wantErr: ErrSyntax},
|
||||
// valid
|
||||
{input: `0x0`, want: big.NewInt(0)},
|
||||
{input: `0x2`, want: big.NewInt(0x2)},
|
||||
{input: `0x2F2`, want: big.NewInt(0x2f2)},
|
||||
{input: `0X2F2`, want: big.NewInt(0x2f2)},
|
||||
{input: `0x1122aaff`, want: big.NewInt(0x1122aaff)},
|
||||
{input: `0xbBb`, want: big.NewInt(0xbbb)},
|
||||
{input: `0xfffffffff`, want: big.NewInt(0xfffffffff)},
|
||||
{
|
||||
input: `0x112233445566778899aabbccddeeff`,
|
||||
want: referenceBig("112233445566778899aabbccddeeff"),
|
||||
},
|
||||
{
|
||||
input: `0xffffffffffffffffffffffffffffffffffff`,
|
||||
want: referenceBig("ffffffffffffffffffffffffffffffffffff"),
|
||||
},
|
||||
}
|
||||
|
||||
decodeUint64Tests = []unmarshalTest{
|
||||
// invalid
|
||||
{input: `0`, wantErr: ErrMissingPrefix},
|
||||
{input: `0x`, wantErr: ErrEmptyNumber},
|
||||
{input: `0x01`, wantErr: ErrLeadingZero},
|
||||
{input: `0xfffffffffffffffff`, wantErr: ErrUint64Range},
|
||||
{input: `0xx`, wantErr: ErrSyntax},
|
||||
{input: `0x1zz01`, wantErr: ErrSyntax},
|
||||
// valid
|
||||
{input: `0x0`, want: uint64(0)},
|
||||
{input: `0x2`, want: uint64(0x2)},
|
||||
{input: `0x2F2`, want: uint64(0x2f2)},
|
||||
{input: `0X2F2`, want: uint64(0x2f2)},
|
||||
{input: `0x1122aaff`, want: uint64(0x1122aaff)},
|
||||
{input: `0xbbb`, want: uint64(0xbbb)},
|
||||
{input: `0xffffffffffffffff`, want: uint64(0xffffffffffffffff)},
|
||||
}
|
||||
)
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
for _, test := range encodeBytesTests {
|
||||
enc := Encode(test.input.([]byte))
|
||||
if enc != test.want {
|
||||
t.Errorf("input %x: wrong encoding %s", test.input, enc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
for _, test := range decodeBytesTests {
|
||||
dec, err := Decode(test.input)
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(test.want.([]byte), dec) {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeBig(t *testing.T) {
|
||||
for _, test := range encodeBigTests {
|
||||
enc := EncodeBig(test.input.(*big.Int))
|
||||
if enc != test.want {
|
||||
t.Errorf("input %x: wrong encoding %s", test.input, enc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeBig(t *testing.T) {
|
||||
for _, test := range decodeBigTests {
|
||||
dec, err := DecodeBig(test.input)
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if dec.Cmp(test.want.(*big.Int)) != 0 {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeUint64(t *testing.T) {
|
||||
for _, test := range encodeUint64Tests {
|
||||
enc := EncodeUint64(test.input.(uint64))
|
||||
if enc != test.want {
|
||||
t.Errorf("input %x: wrong encoding %s", test.input, enc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeUint64(t *testing.T) {
|
||||
for _, test := range decodeUint64Tests {
|
||||
dec, err := DecodeUint64(test.input)
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if dec != test.want.(uint64) {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
271
common/hexutil/json.go
Normal file
271
common/hexutil/json.go
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package hexutil
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
jsonNull = []byte("null")
|
||||
jsonZero = []byte(`"0x0"`)
|
||||
errNonString = errors.New("cannot unmarshal non-string as hex data")
|
||||
errNegativeBigInt = errors.New("hexutil.Big: can't marshal negative integer")
|
||||
)
|
||||
|
||||
// Bytes marshals/unmarshals as a JSON string with 0x prefix.
|
||||
// The empty slice marshals as "0x".
|
||||
type Bytes []byte
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (b Bytes) 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
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (b *Bytes) UnmarshalJSON(input []byte) error {
|
||||
raw, err := checkJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec := make([]byte, len(raw)/2)
|
||||
if _, err = hex.Decode(dec, raw); err != nil {
|
||||
err = mapError(err)
|
||||
} else {
|
||||
*b = dec
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// String returns the hex encoding of b.
|
||||
func (b Bytes) String() string {
|
||||
return Encode(b)
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes input as a JSON string with 0x prefix. The length of out
|
||||
// determines the required input length. This function is commonly used to implement the
|
||||
// UnmarshalJSON method for fixed-size types:
|
||||
//
|
||||
// type Foo [8]byte
|
||||
//
|
||||
// func (f *Foo) UnmarshalJSON(input []byte) error {
|
||||
// return hexutil.UnmarshalJSON("Foo", input, f[:])
|
||||
// }
|
||||
func UnmarshalJSON(typname string, input, out []byte) error {
|
||||
raw, err := checkJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(raw)/2 != len(out) {
|
||||
return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname)
|
||||
}
|
||||
// Pre-verify syntax before modifying out.
|
||||
for _, b := range raw {
|
||||
if decodeNibble(b) == badNibble {
|
||||
return ErrSyntax
|
||||
}
|
||||
}
|
||||
hex.Decode(out, raw)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Big marshals/unmarshals as a JSON string with 0x prefix. The zero value marshals as
|
||||
// "0x0". Negative integers are not supported at this time. Attempting to marshal them
|
||||
// will return an error.
|
||||
type Big big.Int
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (b *Big) MarshalJSON() ([]byte, error) {
|
||||
if b == nil {
|
||||
return jsonNull, nil
|
||||
}
|
||||
bigint := (*big.Int)(b)
|
||||
if bigint.Sign() == -1 {
|
||||
return nil, errNegativeBigInt
|
||||
}
|
||||
nbits := bigint.BitLen()
|
||||
if nbits == 0 {
|
||||
return jsonZero, nil
|
||||
}
|
||||
enc := make([]byte, 3, (nbits/8)*2+4)
|
||||
copy(enc, `"0x`)
|
||||
for i := len(bigint.Bits()) - 1; i >= 0; i-- {
|
||||
enc = strconv.AppendUint(enc, uint64(bigint.Bits()[i]), 16)
|
||||
}
|
||||
enc = append(enc, '"')
|
||||
return enc, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (b *Big) UnmarshalJSON(input []byte) error {
|
||||
raw, err := checkNumberJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
words := make([]big.Word, len(raw)/bigWordNibbles+1)
|
||||
end := len(raw)
|
||||
for i := range words {
|
||||
start := end - bigWordNibbles
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
for ri := start; ri < end; ri++ {
|
||||
nib := decodeNibble(raw[ri])
|
||||
if nib == badNibble {
|
||||
return ErrSyntax
|
||||
}
|
||||
words[i] *= 16
|
||||
words[i] += big.Word(nib)
|
||||
}
|
||||
end = start
|
||||
}
|
||||
var dec big.Int
|
||||
dec.SetBits(words)
|
||||
*b = (Big)(dec)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToInt converts b to a big.Int.
|
||||
func (b *Big) ToInt() *big.Int {
|
||||
return (*big.Int)(b)
|
||||
}
|
||||
|
||||
// String returns the hex encoding of b.
|
||||
func (b *Big) String() string {
|
||||
return EncodeBig(b.ToInt())
|
||||
}
|
||||
|
||||
// Uint64 marshals/unmarshals as a JSON string with 0x prefix.
|
||||
// The zero value marshals as "0x0".
|
||||
type Uint64 uint64
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (b Uint64) MarshalJSON() ([]byte, error) {
|
||||
buf := make([]byte, 3, 12)
|
||||
copy(buf, `"0x`)
|
||||
buf = strconv.AppendUint(buf, uint64(b), 16)
|
||||
buf = append(buf, '"')
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (b *Uint64) UnmarshalJSON(input []byte) error {
|
||||
raw, err := checkNumberJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(raw) > 16 {
|
||||
return ErrUint64Range
|
||||
}
|
||||
var dec uint64
|
||||
for _, byte := range raw {
|
||||
nib := decodeNibble(byte)
|
||||
if nib == badNibble {
|
||||
return ErrSyntax
|
||||
}
|
||||
dec *= 16
|
||||
dec += uint64(nib)
|
||||
}
|
||||
*b = Uint64(dec)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the hex encoding of b.
|
||||
func (b Uint64) String() string {
|
||||
return EncodeUint64(uint64(b))
|
||||
}
|
||||
|
||||
// Uint marshals/unmarshals as a JSON string with 0x prefix.
|
||||
// The zero value marshals as "0x0".
|
||||
type Uint uint
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (b Uint) MarshalJSON() ([]byte, error) {
|
||||
return Uint64(b).MarshalJSON()
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (b *Uint) UnmarshalJSON(input []byte) error {
|
||||
var u64 Uint64
|
||||
err := u64.UnmarshalJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if u64 > Uint64(^uint(0)) {
|
||||
return ErrUintRange
|
||||
}
|
||||
*b = Uint(u64)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the hex encoding of b.
|
||||
func (b Uint) String() string {
|
||||
return EncodeUint64(uint64(b))
|
||||
}
|
||||
|
||||
func isString(input []byte) bool {
|
||||
return len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"'
|
||||
}
|
||||
|
||||
func bytesHave0xPrefix(input []byte) bool {
|
||||
return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
|
||||
}
|
||||
|
||||
func checkJSON(input []byte) (raw []byte, err error) {
|
||||
if !isString(input) {
|
||||
return nil, errNonString
|
||||
}
|
||||
if len(input) == 2 {
|
||||
return nil, ErrEmptyString
|
||||
}
|
||||
if !bytesHave0xPrefix(input[1:]) {
|
||||
return nil, ErrMissingPrefix
|
||||
}
|
||||
input = input[3 : len(input)-1]
|
||||
if len(input)%2 != 0 {
|
||||
return nil, ErrOddLength
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func checkNumberJSON(input []byte) (raw []byte, err error) {
|
||||
if !isString(input) {
|
||||
return nil, errNonString
|
||||
}
|
||||
input = input[1 : len(input)-1]
|
||||
if len(input) == 0 {
|
||||
return nil, ErrEmptyString
|
||||
}
|
||||
if !bytesHave0xPrefix(input) {
|
||||
return nil, ErrMissingPrefix
|
||||
}
|
||||
input = input[2:]
|
||||
if len(input) == 0 {
|
||||
return nil, ErrEmptyNumber
|
||||
}
|
||||
if len(input) > 1 && input[0] == '0' {
|
||||
return nil, ErrLeadingZero
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
258
common/hexutil/json_test.go
Normal file
258
common/hexutil/json_test.go
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package hexutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func checkError(t *testing.T, input string, got, want error) bool {
|
||||
if got == nil {
|
||||
if want != nil {
|
||||
t.Errorf("input %s: got no error, want %q", input, want)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if want == nil {
|
||||
t.Errorf("input %s: unexpected error %q", input, got)
|
||||
} else if got.Error() != want.Error() {
|
||||
t.Errorf("input %s: got error %q, want %q", input, got, want)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func referenceBig(s string) *big.Int {
|
||||
b, ok := new(big.Int).SetString(s, 16)
|
||||
if !ok {
|
||||
panic("invalid")
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func referenceBytes(s string) []byte {
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
var unmarshalBytesTests = []unmarshalTest{
|
||||
// invalid encoding
|
||||
{input: "", wantErr: errNonString},
|
||||
{input: "null", wantErr: errNonString},
|
||||
{input: "10", wantErr: errNonString},
|
||||
{input: `""`, wantErr: ErrEmptyString},
|
||||
{input: `"0"`, wantErr: ErrMissingPrefix},
|
||||
{input: `"0x0"`, wantErr: ErrOddLength},
|
||||
{input: `"0xxx"`, wantErr: ErrSyntax},
|
||||
{input: `"0x01zz01"`, wantErr: ErrSyntax},
|
||||
|
||||
// valid encoding
|
||||
{input: `"0x"`, want: referenceBytes("")},
|
||||
{input: `"0x02"`, want: referenceBytes("02")},
|
||||
{input: `"0X02"`, want: referenceBytes("02")},
|
||||
{input: `"0xffffffffff"`, want: referenceBytes("ffffffffff")},
|
||||
{
|
||||
input: `"0xffffffffffffffffffffffffffffffffffff"`,
|
||||
want: referenceBytes("ffffffffffffffffffffffffffffffffffff"),
|
||||
},
|
||||
}
|
||||
|
||||
func TestUnmarshalBytes(t *testing.T) {
|
||||
for _, test := range unmarshalBytesTests {
|
||||
var v Bytes
|
||||
err := v.UnmarshalJSON([]byte(test.input))
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(test.want.([]byte), []byte(v)) {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, &v, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalBytes(b *testing.B) {
|
||||
input := []byte(`"0x123456789abcdef123456789abcdef"`)
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v Bytes
|
||||
if err := v.UnmarshalJSON(input); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalBytes(t *testing.T) {
|
||||
for _, test := range encodeBytesTests {
|
||||
in := test.input.([]byte)
|
||||
out, err := Bytes(in).MarshalJSON()
|
||||
if err != nil {
|
||||
t.Errorf("%x: %v", in, err)
|
||||
continue
|
||||
}
|
||||
if want := `"` + test.want + `"`; string(out) != want {
|
||||
t.Errorf("%x: MarshalJSON output mismatch: got %q, want %q", in, out, want)
|
||||
continue
|
||||
}
|
||||
if out := Bytes(in).String(); out != test.want {
|
||||
t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unmarshalBigTests = []unmarshalTest{
|
||||
// invalid encoding
|
||||
{input: "", wantErr: errNonString},
|
||||
{input: "null", wantErr: errNonString},
|
||||
{input: "10", wantErr: errNonString},
|
||||
{input: `""`, wantErr: ErrEmptyString},
|
||||
{input: `"0"`, wantErr: ErrMissingPrefix},
|
||||
{input: `"0x"`, wantErr: ErrEmptyNumber},
|
||||
{input: `"0x01"`, wantErr: ErrLeadingZero},
|
||||
{input: `"0xx"`, wantErr: ErrSyntax},
|
||||
{input: `"0x1zz01"`, wantErr: ErrSyntax},
|
||||
|
||||
// valid encoding
|
||||
{input: `"0x0"`, want: big.NewInt(0)},
|
||||
{input: `"0x2"`, want: big.NewInt(0x2)},
|
||||
{input: `"0x2F2"`, want: big.NewInt(0x2f2)},
|
||||
{input: `"0X2F2"`, want: big.NewInt(0x2f2)},
|
||||
{input: `"0x1122aaff"`, want: big.NewInt(0x1122aaff)},
|
||||
{input: `"0xbBb"`, want: big.NewInt(0xbbb)},
|
||||
{input: `"0xfffffffff"`, want: big.NewInt(0xfffffffff)},
|
||||
{
|
||||
input: `"0x112233445566778899aabbccddeeff"`,
|
||||
want: referenceBig("112233445566778899aabbccddeeff"),
|
||||
},
|
||||
{
|
||||
input: `"0xffffffffffffffffffffffffffffffffffff"`,
|
||||
want: referenceBig("ffffffffffffffffffffffffffffffffffff"),
|
||||
},
|
||||
}
|
||||
|
||||
func TestUnmarshalBig(t *testing.T) {
|
||||
for _, test := range unmarshalBigTests {
|
||||
var v Big
|
||||
err := v.UnmarshalJSON([]byte(test.input))
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if test.want != nil && test.want.(*big.Int).Cmp((*big.Int)(&v)) != 0 {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, (*big.Int)(&v), test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalBig(b *testing.B) {
|
||||
input := []byte(`"0x123456789abcdef123456789abcdef"`)
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v Big
|
||||
if err := v.UnmarshalJSON(input); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalBig(t *testing.T) {
|
||||
for _, test := range encodeBigTests {
|
||||
in := test.input.(*big.Int)
|
||||
out, err := (*Big)(in).MarshalJSON()
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", in, err)
|
||||
continue
|
||||
}
|
||||
if want := `"` + test.want + `"`; string(out) != want {
|
||||
t.Errorf("%d: MarshalJSON output mismatch: got %q, want %q", in, out, want)
|
||||
continue
|
||||
}
|
||||
if out := (*Big)(in).String(); out != test.want {
|
||||
t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unmarshalUint64Tests = []unmarshalTest{
|
||||
// invalid encoding
|
||||
{input: "", wantErr: errNonString},
|
||||
{input: "null", wantErr: errNonString},
|
||||
{input: "10", wantErr: errNonString},
|
||||
{input: `""`, wantErr: ErrEmptyString},
|
||||
{input: `"0"`, wantErr: ErrMissingPrefix},
|
||||
{input: `"0x"`, wantErr: ErrEmptyNumber},
|
||||
{input: `"0x01"`, wantErr: ErrLeadingZero},
|
||||
{input: `"0xfffffffffffffffff"`, wantErr: ErrUint64Range},
|
||||
{input: `"0xx"`, wantErr: ErrSyntax},
|
||||
{input: `"0x1zz01"`, wantErr: ErrSyntax},
|
||||
|
||||
// valid encoding
|
||||
{input: `"0x0"`, want: uint64(0)},
|
||||
{input: `"0x2"`, want: uint64(0x2)},
|
||||
{input: `"0x2F2"`, want: uint64(0x2f2)},
|
||||
{input: `"0X2F2"`, want: uint64(0x2f2)},
|
||||
{input: `"0x1122aaff"`, want: uint64(0x1122aaff)},
|
||||
{input: `"0xbbb"`, want: uint64(0xbbb)},
|
||||
{input: `"0xffffffffffffffff"`, want: uint64(0xffffffffffffffff)},
|
||||
}
|
||||
|
||||
func TestUnmarshalUint64(t *testing.T) {
|
||||
for _, test := range unmarshalUint64Tests {
|
||||
var v Uint64
|
||||
err := v.UnmarshalJSON([]byte(test.input))
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if uint64(v) != test.want.(uint64) {
|
||||
t.Errorf("input %s: value mismatch: got %d, want %d", test.input, v, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalUint64(b *testing.B) {
|
||||
input := []byte(`"0x123456789abcdf"`)
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v Uint64
|
||||
v.UnmarshalJSON(input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUint64(t *testing.T) {
|
||||
for _, test := range encodeUint64Tests {
|
||||
in := test.input.(uint64)
|
||||
out, err := Uint64(in).MarshalJSON()
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", in, err)
|
||||
continue
|
||||
}
|
||||
if want := `"` + test.want + `"`; string(out) != want {
|
||||
t.Errorf("%d: MarshalJSON output mismatch: got %q, want %q", in, out, want)
|
||||
continue
|
||||
}
|
||||
if out := (Uint64)(in).String(); out != test.want {
|
||||
t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,14 +17,12 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -32,8 +30,6 @@ const (
|
|||
AddressLength = 20
|
||||
)
|
||||
|
||||
var hashJsonLengthErr = errors.New("common: unmarshalJSON failed: hash must be exactly 32 bytes")
|
||||
|
||||
type (
|
||||
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
|
||||
Hash [HashLength]byte
|
||||
|
|
@ -57,30 +53,16 @@ func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
|
|||
func (h Hash) Str() string { return string(h[:]) }
|
||||
func (h Hash) Bytes() []byte { return h[:] }
|
||||
func (h Hash) Big() *big.Int { return Bytes2Big(h[:]) }
|
||||
func (h Hash) Hex() string { return "0x" + Bytes2Hex(h[:]) }
|
||||
func (h Hash) Hex() string { return hexutil.Encode(h[:]) }
|
||||
|
||||
// UnmarshalJSON parses a hash in its hex from to a hash.
|
||||
func (h *Hash) UnmarshalJSON(input []byte) error {
|
||||
length := len(input)
|
||||
if length >= 2 && input[0] == '"' && input[length-1] == '"' {
|
||||
input = input[1 : length-1]
|
||||
}
|
||||
// strip "0x" for length check
|
||||
if len(input) > 1 && strings.ToLower(string(input[:2])) == "0x" {
|
||||
input = input[2:]
|
||||
}
|
||||
|
||||
// validate the length of the input hash
|
||||
if len(input) != HashLength*2 {
|
||||
return hashJsonLengthErr
|
||||
}
|
||||
h.SetBytes(FromHex(string(input)))
|
||||
return nil
|
||||
return hexutil.UnmarshalJSON("Hash", input, h[:])
|
||||
}
|
||||
|
||||
// Serialize given hash to JSON
|
||||
func (h Hash) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(h.Hex())
|
||||
return hexutil.Bytes(h[:]).MarshalJSON()
|
||||
}
|
||||
|
||||
// Sets the hash to the value of b. If b is larger than len(h) it will panic
|
||||
|
|
@ -142,7 +124,7 @@ func (a Address) Str() string { return string(a[:]) }
|
|||
func (a Address) Bytes() []byte { return a[:] }
|
||||
func (a Address) Big() *big.Int { return Bytes2Big(a[:]) }
|
||||
func (a Address) Hash() Hash { return BytesToHash(a[:]) }
|
||||
func (a Address) Hex() string { return "0x" + Bytes2Hex(a[:]) }
|
||||
func (a Address) Hex() string { return hexutil.Encode(a[:]) }
|
||||
|
||||
// Sets the address to the value of b. If b is larger than len(a) it will panic
|
||||
func (a *Address) SetBytes(b []byte) {
|
||||
|
|
@ -164,34 +146,12 @@ func (a *Address) Set(other Address) {
|
|||
|
||||
// Serialize given address to JSON
|
||||
func (a Address) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(a.Hex())
|
||||
return hexutil.Bytes(a[:]).MarshalJSON()
|
||||
}
|
||||
|
||||
// Parse address from raw json data
|
||||
func (a *Address) UnmarshalJSON(data []byte) error {
|
||||
if len(data) > 2 && data[0] == '"' && data[len(data)-1] == '"' {
|
||||
data = data[1 : len(data)-1]
|
||||
}
|
||||
|
||||
if len(data) > 2 && data[0] == '0' && data[1] == 'x' {
|
||||
data = data[2:]
|
||||
}
|
||||
|
||||
if len(data) != 2*AddressLength {
|
||||
return fmt.Errorf("Invalid address length, expected %d got %d bytes", 2*AddressLength, len(data))
|
||||
}
|
||||
|
||||
n, err := hex.Decode(a[:], data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n != AddressLength {
|
||||
return fmt.Errorf("Invalid address")
|
||||
}
|
||||
|
||||
a.Set(HexToAddress(string(data)))
|
||||
return nil
|
||||
func (a *Address) UnmarshalJSON(input []byte) error {
|
||||
return hexutil.UnmarshalJSON("Address", input, a[:])
|
||||
}
|
||||
|
||||
// PP Pretty Prints a byte slice in the following format:
|
||||
|
|
|
|||
|
|
@ -18,7 +18,10 @@ package common
|
|||
|
||||
import (
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
func TestBytesConversion(t *testing.T) {
|
||||
|
|
@ -38,19 +41,26 @@ func TestHashJsonValidation(t *testing.T) {
|
|||
var tests = []struct {
|
||||
Prefix string
|
||||
Size int
|
||||
Error error
|
||||
Error string
|
||||
}{
|
||||
{"", 2, hashJsonLengthErr},
|
||||
{"", 62, hashJsonLengthErr},
|
||||
{"", 66, hashJsonLengthErr},
|
||||
{"", 65, hashJsonLengthErr},
|
||||
{"0X", 64, nil},
|
||||
{"0x", 64, nil},
|
||||
{"0x", 62, hashJsonLengthErr},
|
||||
{"", 62, hexutil.ErrMissingPrefix.Error()},
|
||||
{"0x", 66, "hex string has length 66, want 64 for Hash"},
|
||||
{"0x", 63, hexutil.ErrOddLength.Error()},
|
||||
{"0x", 0, "hex string has length 0, want 64 for Hash"},
|
||||
{"0x", 64, ""},
|
||||
{"0X", 64, ""},
|
||||
}
|
||||
for i, test := range tests {
|
||||
if err := h.UnmarshalJSON(append([]byte(test.Prefix), make([]byte, test.Size)...)); err != test.Error {
|
||||
t.Errorf("test #%d: error mismatch: have %v, want %v", i, err, test.Error)
|
||||
for _, test := range tests {
|
||||
input := `"` + test.Prefix + strings.Repeat("0", test.Size) + `"`
|
||||
err := h.UnmarshalJSON([]byte(input))
|
||||
if err == nil {
|
||||
if test.Error != "" {
|
||||
t.Errorf("%s: error mismatch: have nil, want %q", input, test.Error)
|
||||
}
|
||||
} else {
|
||||
if err.Error() != test.Error {
|
||||
t.Errorf("%s: error mismatch: have %q, want %q", input, err, test.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
|
@ -63,20 +64,12 @@ func (n BlockNonce) Uint64() uint64 {
|
|||
|
||||
// MarshalJSON implements json.Marshaler
|
||||
func (n BlockNonce) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf(`"0x%x"`, n)), nil
|
||||
return hexutil.Bytes(n[:]).MarshalJSON()
|
||||
}
|
||||
|
||||
// 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
|
||||
return hexutil.UnmarshalJSON("BlockNonce", input, n[:])
|
||||
}
|
||||
|
||||
// Header represents a block header in the Ethereum blockchain.
|
||||
|
|
@ -106,12 +99,12 @@ type jsonHeader struct {
|
|||
TxHash *common.Hash `json:"transactionsRoot"`
|
||||
ReceiptHash *common.Hash `json:"receiptsRoot"`
|
||||
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"`
|
||||
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"`
|
||||
}
|
||||
|
|
@ -151,12 +144,12 @@ func (h *Header) MarshalJSON() ([]byte, error) {
|
|||
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),
|
||||
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,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
|
@ -77,20 +78,12 @@ func (b Bloom) TestBytes(test []byte) bool {
|
|||
|
||||
// MarshalJSON encodes b as a hex string with 0x prefix.
|
||||
func (b Bloom) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf(`"%#x"`, b[:])), nil
|
||||
return hexutil.Bytes(b[:]).MarshalJSON()
|
||||
}
|
||||
|
||||
// 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
|
||||
return hexutil.UnmarshalJSON("Bloom", input, b[:])
|
||||
}
|
||||
|
||||
func CreateBloom(receipts Receipts) Bloom {
|
||||
|
|
|
|||
|
|
@ -1,108 +0,0 @@
|
|||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// JSON unmarshaling utilities.
|
||||
|
||||
type hexBytes []byte
|
||||
|
||||
func (b *hexBytes) MarshalJSON() ([]byte, error) {
|
||||
if b != nil {
|
||||
return []byte(fmt.Sprintf(`"0x%x"`, []byte(*b))), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
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) MarshalJSON() ([]byte, error) {
|
||||
if b != nil {
|
||||
return []byte(fmt.Sprintf(`"0x%x"`, (*big.Int)(b))), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
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) MarshalJSON() ([]byte, error) {
|
||||
if b != nil {
|
||||
return []byte(fmt.Sprintf(`"0x%x"`, *(*uint64)(b))), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -1,232 +0,0 @@
|
|||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"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","receiptsRoot":"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","receiptsRoot":"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","receiptsRoot":"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","receiptsRoot":"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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalHeader(t *testing.T) {
|
||||
for name, test := range unmarshalHeaderTests {
|
||||
if test.wantError != nil {
|
||||
continue
|
||||
}
|
||||
var original *Header
|
||||
json.Unmarshal([]byte(test.input), &original)
|
||||
|
||||
blob, err := json.Marshal(original)
|
||||
if err != nil {
|
||||
t.Errorf("test %q: failed to marshal header: %v", name, err)
|
||||
continue
|
||||
}
|
||||
var proced *Header
|
||||
if err := json.Unmarshal(blob, &proced); err != nil {
|
||||
t.Errorf("Test %q: failed to unmarshal marhsalled header: %v", name, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(original, proced) {
|
||||
t.Errorf("test %q: header mismatch: have %+v, want %+v", name, proced, original)
|
||||
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"),
|
||||
},
|
||||
/* TODO skipping this test as this type can not be tested with the current signing approach
|
||||
"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 := Sender(HomesteadSigner{}, tx)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalTransaction(t *testing.T) {
|
||||
for name, test := range unmarshalTransactionTests {
|
||||
if test.wantError != nil {
|
||||
continue
|
||||
}
|
||||
var original *Transaction
|
||||
json.Unmarshal([]byte(test.input), &original)
|
||||
|
||||
blob, err := json.Marshal(original)
|
||||
if err != nil {
|
||||
t.Errorf("test %q: failed to marshal transaction: %v", name, err)
|
||||
continue
|
||||
}
|
||||
var proced *Transaction
|
||||
if err := json.Unmarshal(blob, &proced); err != nil {
|
||||
t.Errorf("Test %q: failed to unmarshal marhsalled transaction: %v", name, err)
|
||||
continue
|
||||
}
|
||||
proced.Hash() // hack private fields to pass deep equal
|
||||
if !reflect.DeepEqual(original, proced) {
|
||||
t.Errorf("test %q: transaction mismatch: have %+v, want %+v", name, proced, original)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 TestMarshalReceipt(t *testing.T) {
|
||||
for name, test := range unmarshalReceiptTests {
|
||||
if test.wantError != nil {
|
||||
continue
|
||||
}
|
||||
var original *Receipt
|
||||
json.Unmarshal([]byte(test.input), &original)
|
||||
|
||||
blob, err := json.Marshal(original)
|
||||
if err != nil {
|
||||
t.Errorf("test %q: failed to marshal receipt: %v", name, err)
|
||||
continue
|
||||
}
|
||||
var proced *Receipt
|
||||
if err := json.Unmarshal(blob, &proced); err != nil {
|
||||
t.Errorf("Test %q: failed to unmarshal marhsalled receipt: %v", name, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(original, proced) {
|
||||
t.Errorf("test %q: receipt mismatch: have %+v, want %+v", name, proced, original)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
|
@ -49,12 +50,12 @@ type Receipt struct {
|
|||
|
||||
type jsonReceipt struct {
|
||||
PostState *common.Hash `json:"root"`
|
||||
CumulativeGasUsed *hexBig `json:"cumulativeGasUsed"`
|
||||
CumulativeGasUsed *hexutil.Big `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"`
|
||||
GasUsed *hexutil.Big `json:"gasUsed"`
|
||||
}
|
||||
|
||||
// NewReceipt creates a barebone transaction receipt, copying the init fields.
|
||||
|
|
@ -90,12 +91,12 @@ func (r *Receipt) MarshalJSON() ([]byte, error) {
|
|||
|
||||
return json.Marshal(&jsonReceipt{
|
||||
PostState: &root,
|
||||
CumulativeGasUsed: (*hexBig)(r.CumulativeGasUsed),
|
||||
CumulativeGasUsed: (*hexutil.Big)(r.CumulativeGasUsed),
|
||||
Bloom: &r.Bloom,
|
||||
Logs: &r.Logs,
|
||||
TxHash: &r.TxHash,
|
||||
ContractAddress: &r.ContractAddress,
|
||||
GasUsed: (*hexBig)(r.GasUsed),
|
||||
GasUsed: (*hexutil.Big)(r.GasUsed),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
|
|
@ -69,15 +70,15 @@ type txdata struct {
|
|||
|
||||
type jsonTransaction struct {
|
||||
Hash *common.Hash `json:"hash"`
|
||||
AccountNonce *hexUint64 `json:"nonce"`
|
||||
Price *hexBig `json:"gasPrice"`
|
||||
GasLimit *hexBig `json:"gas"`
|
||||
AccountNonce *hexutil.Uint64 `json:"nonce"`
|
||||
Price *hexutil.Big `json:"gasPrice"`
|
||||
GasLimit *hexutil.Big `json:"gas"`
|
||||
Recipient *common.Address `json:"to"`
|
||||
Amount *hexBig `json:"value"`
|
||||
Payload *hexBytes `json:"input"`
|
||||
V *hexBig `json:"v"`
|
||||
R *hexBig `json:"r"`
|
||||
S *hexBig `json:"s"`
|
||||
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"`
|
||||
}
|
||||
|
||||
func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
|
||||
|
|
@ -170,15 +171,15 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
|
|||
|
||||
return json.Marshal(&jsonTransaction{
|
||||
Hash: &hash,
|
||||
AccountNonce: (*hexUint64)(&tx.data.AccountNonce),
|
||||
Price: (*hexBig)(tx.data.Price),
|
||||
GasLimit: (*hexBig)(tx.data.GasLimit),
|
||||
AccountNonce: (*hexutil.Uint64)(&tx.data.AccountNonce),
|
||||
Price: (*hexutil.Big)(tx.data.Price),
|
||||
GasLimit: (*hexutil.Big)(tx.data.GasLimit),
|
||||
Recipient: tx.data.Recipient,
|
||||
Amount: (*hexBig)(tx.data.Amount),
|
||||
Payload: (*hexBytes)(&tx.data.Payload),
|
||||
V: (*hexBig)(tx.data.V),
|
||||
R: (*hexBig)(tx.data.R),
|
||||
S: (*hexBig)(tx.data.S),
|
||||
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),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -233,6 +234,8 @@ func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amo
|
|||
func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce }
|
||||
func (tx *Transaction) CheckNonce() bool { return true }
|
||||
|
||||
// To returns the recipient address of the transaction.
|
||||
// It returns nil if the transaction is a contract creation.
|
||||
func (tx *Transaction) To() *common.Address {
|
||||
if tx.data.Recipient == nil {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"io"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
|
|
@ -47,12 +48,12 @@ type Log struct {
|
|||
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"`
|
||||
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 string `json:"logIndex"`
|
||||
Index *hexutil.Uint `json:"logIndex"`
|
||||
}
|
||||
|
||||
func NewLog(address common.Address, topics []common.Hash, data []byte, number uint64) *Log {
|
||||
|
|
@ -85,12 +86,12 @@ func (r *Log) MarshalJSON() ([]byte, error) {
|
|||
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),
|
||||
Data: (*hexutil.Bytes)(&r.Data),
|
||||
BlockNumber: (*hexutil.Uint64)(&r.BlockNumber),
|
||||
TxIndex: (*hexutil.Uint)(&r.TxIndex),
|
||||
TxHash: &r.TxHash,
|
||||
BlockHash: &r.BlockHash,
|
||||
Index: fmt.Sprintf("0x%x", r.Index),
|
||||
Index: (*hexutil.Uint)(&r.Index),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -100,29 +101,20 @@ func (r *Log) UnmarshalJSON(input []byte) error {
|
|||
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 == "" {
|
||||
if dec.Address == nil || dec.Topics == nil || dec.Data == nil || dec.BlockNumber == nil ||
|
||||
dec.TxIndex == nil || dec.TxHash == nil || dec.BlockHash == nil || dec.Index == nil {
|
||||
return errMissingLogFields
|
||||
}
|
||||
declog := Log{
|
||||
Address: *dec.Address,
|
||||
Topics: *dec.Topics,
|
||||
TxHash: *dec.TxHash,
|
||||
BlockHash: *dec.BlockHash,
|
||||
*r = Log{
|
||||
Address: *dec.Address,
|
||||
Topics: *dec.Topics,
|
||||
Data: *dec.Data,
|
||||
BlockNumber: uint64(*dec.BlockNumber),
|
||||
TxHash: *dec.TxHash,
|
||||
TxIndex: uint(*dec.TxIndex),
|
||||
BlockHash: *dec.BlockHash,
|
||||
Index: uint(*dec.Index),
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ var unmarshalLogTests = map[string]struct {
|
|||
"ok": {
|
||||
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x000000000000000000000000000000000000000000000001a055690d9db80000","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
|
||||
},
|
||||
"empty data": {
|
||||
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x","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,
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
package filters
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -28,6 +27,7 @@ import (
|
|||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
|
|
@ -239,11 +239,17 @@ func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc
|
|||
return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
|
||||
}
|
||||
|
||||
rpcSub := notifier.CreateSubscription()
|
||||
var (
|
||||
rpcSub = notifier.CreateSubscription()
|
||||
matchedLogs = make(chan []Log)
|
||||
)
|
||||
|
||||
logsSub, err := api.events.SubscribeLogs(crit, matchedLogs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
matchedLogs := make(chan []Log)
|
||||
logsSub := api.events.SubscribeLogs(crit, matchedLogs)
|
||||
|
||||
for {
|
||||
select {
|
||||
|
|
@ -276,18 +282,20 @@ type FilterCriteria struct {
|
|||
// used to retrieve logs when the state changes. This method cannot be
|
||||
// used to fetch logs that are already stored in the state.
|
||||
//
|
||||
// Default criteria for the from and to block are "latest".
|
||||
// Using "latest" as block number will return logs for mined blocks.
|
||||
// Using "pending" as block number returns logs for not yet mined (pending) blocks.
|
||||
// In case logs are removed (chain reorg) previously returned logs are returned
|
||||
// again but with the removed property set to true.
|
||||
//
|
||||
// In case "fromBlock" > "toBlock" an error is returned.
|
||||
//
|
||||
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter
|
||||
func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) rpc.ID {
|
||||
var (
|
||||
logs = make(chan []Log)
|
||||
logsSub = api.events.SubscribeLogs(crit, logs)
|
||||
)
|
||||
|
||||
if crit.FromBlock == nil {
|
||||
crit.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
|
||||
}
|
||||
if crit.ToBlock == nil {
|
||||
crit.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
|
||||
func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) {
|
||||
logs := make(chan []Log)
|
||||
logsSub, err := api.events.SubscribeLogs(crit, logs)
|
||||
if err != nil {
|
||||
return rpc.ID(""), err
|
||||
}
|
||||
|
||||
api.filtersMu.Lock()
|
||||
|
|
@ -312,7 +320,7 @@ func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) rpc.ID {
|
|||
}
|
||||
}()
|
||||
|
||||
return logsSub.ID
|
||||
return logsSub.ID, nil
|
||||
}
|
||||
|
||||
// GetLogs returns logs matching the given argument that are stored within the state.
|
||||
|
|
@ -363,28 +371,38 @@ func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]Log
|
|||
api.filtersMu.Unlock()
|
||||
|
||||
if !found || f.typ != LogsSubscription {
|
||||
return []Log{}, nil
|
||||
return nil, fmt.Errorf("filter not found")
|
||||
}
|
||||
|
||||
filter := New(api.backend, api.useMipMap)
|
||||
filter.SetBeginBlock(f.crit.FromBlock.Int64())
|
||||
filter.SetEndBlock(f.crit.ToBlock.Int64())
|
||||
if f.crit.FromBlock != nil {
|
||||
filter.SetBeginBlock(f.crit.FromBlock.Int64())
|
||||
} else {
|
||||
filter.SetBeginBlock(rpc.LatestBlockNumber.Int64())
|
||||
}
|
||||
if f.crit.ToBlock != nil {
|
||||
filter.SetEndBlock(f.crit.ToBlock.Int64())
|
||||
} else {
|
||||
filter.SetEndBlock(rpc.LatestBlockNumber.Int64())
|
||||
}
|
||||
filter.SetAddresses(f.crit.Addresses)
|
||||
filter.SetTopics(f.crit.Topics)
|
||||
|
||||
logs, err := filter.Find(ctx)
|
||||
return returnLogs(logs), err
|
||||
logs, err:= filter.Find(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return returnLogs(logs), nil
|
||||
}
|
||||
|
||||
// GetFilterChanges returns the logs for the filter with the given id since
|
||||
// last time is was called. This can be used for polling.
|
||||
//
|
||||
// For pending transaction and block filters the result is []common.Hash.
|
||||
// (pending)Log filters return []Log. If the filter could not be found
|
||||
// []interface{}{} is returned.
|
||||
// (pending)Log filters return []Log.
|
||||
//
|
||||
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges
|
||||
func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) interface{} {
|
||||
func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) {
|
||||
api.filtersMu.Lock()
|
||||
defer api.filtersMu.Unlock()
|
||||
|
||||
|
|
@ -400,15 +418,15 @@ func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) interface{} {
|
|||
case PendingTransactionsSubscription, BlocksSubscription:
|
||||
hashes := f.hashes
|
||||
f.hashes = nil
|
||||
return returnHashes(hashes)
|
||||
case PendingLogsSubscription, LogsSubscription:
|
||||
return returnHashes(hashes), nil
|
||||
case LogsSubscription:
|
||||
logs := f.logs
|
||||
f.logs = nil
|
||||
return returnLogs(logs)
|
||||
return returnLogs(logs), nil
|
||||
}
|
||||
}
|
||||
|
||||
return []interface{}{}
|
||||
return []interface{}{}, fmt.Errorf("filter not found")
|
||||
}
|
||||
|
||||
// returnHashes is a helper that will return an empty hash array case the given hash array is nil,
|
||||
|
|
@ -443,15 +461,11 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if raw.From == nil || raw.From.Int64() < 0 {
|
||||
args.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
|
||||
} else {
|
||||
if raw.From != nil {
|
||||
args.FromBlock = big.NewInt(raw.From.Int64())
|
||||
}
|
||||
|
||||
if raw.ToBlock == nil || raw.ToBlock.Int64() < 0 {
|
||||
args.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
|
||||
} else {
|
||||
if raw.ToBlock != nil {
|
||||
args.ToBlock = big.NewInt(raw.ToBlock.Int64())
|
||||
}
|
||||
|
||||
|
|
@ -459,52 +473,28 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
|
|||
|
||||
if raw.Addresses != nil {
|
||||
// raw.Address can contain a single address or an array of addresses
|
||||
var addresses []common.Address
|
||||
if strAddrs, ok := raw.Addresses.([]interface{}); ok {
|
||||
for i, addr := range strAddrs {
|
||||
switch rawAddr := raw.Addresses.(type) {
|
||||
case []interface{}:
|
||||
for i, addr := range rawAddr {
|
||||
if strAddr, ok := addr.(string); ok {
|
||||
if len(strAddr) >= 2 && strAddr[0] == '0' && (strAddr[1] == 'x' || strAddr[1] == 'X') {
|
||||
strAddr = strAddr[2:]
|
||||
}
|
||||
if decAddr, err := hex.DecodeString(strAddr); err == nil {
|
||||
addresses = append(addresses, common.BytesToAddress(decAddr))
|
||||
} else {
|
||||
return fmt.Errorf("invalid address given")
|
||||
addr, err := decodeAddress(strAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid address at index %d: %v", i, err)
|
||||
}
|
||||
args.Addresses = append(args.Addresses, addr)
|
||||
} else {
|
||||
return fmt.Errorf("invalid address on index %d", i)
|
||||
return fmt.Errorf("non-string address at index %d", i)
|
||||
}
|
||||
}
|
||||
} else if singleAddr, ok := raw.Addresses.(string); ok {
|
||||
if len(singleAddr) >= 2 && singleAddr[0] == '0' && (singleAddr[1] == 'x' || singleAddr[1] == 'X') {
|
||||
singleAddr = singleAddr[2:]
|
||||
case string:
|
||||
addr, err := decodeAddress(rawAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid address: %v", err)
|
||||
}
|
||||
if decAddr, err := hex.DecodeString(singleAddr); err == nil {
|
||||
addresses = append(addresses, common.BytesToAddress(decAddr))
|
||||
} else {
|
||||
return fmt.Errorf("invalid address given")
|
||||
}
|
||||
} else {
|
||||
return errors.New("invalid address(es) given")
|
||||
args.Addresses = []common.Address{addr}
|
||||
default:
|
||||
return errors.New("invalid addresses in query")
|
||||
}
|
||||
args.Addresses = addresses
|
||||
}
|
||||
|
||||
// helper function which parses a string to a topic hash
|
||||
topicConverter := func(raw string) (common.Hash, error) {
|
||||
if len(raw) == 0 {
|
||||
return common.Hash{}, nil
|
||||
}
|
||||
if len(raw) >= 2 && raw[0] == '0' && (raw[1] == 'x' || raw[1] == 'X') {
|
||||
raw = raw[2:]
|
||||
}
|
||||
if len(raw) != 2*common.HashLength {
|
||||
return common.Hash{}, errors.New("invalid topic(s)")
|
||||
}
|
||||
if decAddr, err := hex.DecodeString(raw); err == nil {
|
||||
return common.BytesToHash(decAddr), nil
|
||||
}
|
||||
return common.Hash{}, errors.New("invalid topic(s)")
|
||||
}
|
||||
|
||||
// topics is an array consisting of strings and/or arrays of strings.
|
||||
|
|
@ -512,20 +502,25 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
|
|||
if len(raw.Topics) > 0 {
|
||||
args.Topics = make([][]common.Hash, len(raw.Topics))
|
||||
for i, t := range raw.Topics {
|
||||
if t == nil { // ignore topic when matching logs
|
||||
switch topic := t.(type) {
|
||||
case nil:
|
||||
// ignore topic when matching logs
|
||||
args.Topics[i] = []common.Hash{common.Hash{}}
|
||||
} else if topic, ok := t.(string); ok { // match specific topic
|
||||
top, err := topicConverter(topic)
|
||||
|
||||
case string:
|
||||
// match specific topic
|
||||
top, err := decodeTopic(topic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args.Topics[i] = []common.Hash{top}
|
||||
} else if topics, ok := t.([]interface{}); ok { // or case e.g. [null, "topic0", "topic1"]
|
||||
for _, rawTopic := range topics {
|
||||
case []interface{}:
|
||||
// or case e.g. [null, "topic0", "topic1"]
|
||||
for _, rawTopic := range topic {
|
||||
if rawTopic == nil {
|
||||
args.Topics[i] = append(args.Topics[i], common.Hash{})
|
||||
} else if topic, ok := rawTopic.(string); ok {
|
||||
parsed, err := topicConverter(topic)
|
||||
parsed, err := decodeTopic(topic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -534,7 +529,7 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
|
|||
return fmt.Errorf("invalid topic(s)")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
default:
|
||||
return fmt.Errorf("invalid topic(s)")
|
||||
}
|
||||
}
|
||||
|
|
@ -542,3 +537,19 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeAddress(s string) (common.Address, error) {
|
||||
b, err := hexutil.Decode(s)
|
||||
if err == nil && len(b) != common.AddressLength {
|
||||
err = fmt.Errorf("hex has invalid length %d after decoding", len(b))
|
||||
}
|
||||
return common.BytesToAddress(b), err
|
||||
}
|
||||
|
||||
func decodeTopic(s string) (common.Hash, error) {
|
||||
b, err := hexutil.Decode(s)
|
||||
if err == nil && len(b) != common.HashLength {
|
||||
err = fmt.Errorf("hex has invalid length %d after decoding", len(b))
|
||||
}
|
||||
return common.BytesToHash(b), err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,11 +42,11 @@ func TestUnmarshalJSONNewFilterArgs(t *testing.T) {
|
|||
if err := json.Unmarshal([]byte("{}"), &test0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if test0.FromBlock.Int64() != rpc.LatestBlockNumber.Int64() {
|
||||
t.Fatalf("expected %d, got %d", rpc.LatestBlockNumber, test0.FromBlock)
|
||||
if test0.FromBlock != nil {
|
||||
t.Fatalf("expected nil, got %d", test0.FromBlock)
|
||||
}
|
||||
if test0.ToBlock.Int64() != rpc.LatestBlockNumber.Int64() {
|
||||
t.Fatalf("expected %d, got %d", rpc.LatestBlockNumber, test0.ToBlock)
|
||||
if test0.ToBlock != nil {
|
||||
t.Fatalf("expected nil, got %d", test0.ToBlock)
|
||||
}
|
||||
if len(test0.Addresses) != 0 {
|
||||
t.Fatalf("expected 0 addresses, got %d", len(test0.Addresses))
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import (
|
|||
"math"
|
||||
"time"
|
||||
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
|
|
@ -162,7 +164,7 @@ func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, er
|
|||
}
|
||||
unfiltered = append(unfiltered, rl...)
|
||||
}
|
||||
logs = append(logs, filterLogs(unfiltered, f.addresses, f.topics)...)
|
||||
logs = append(logs, filterLogs(unfiltered, nil, nil, f.addresses, f.topics)...)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -179,12 +181,18 @@ func includes(addresses []common.Address, a common.Address) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func filterLogs(logs []Log, addresses []common.Address, topics [][]common.Hash) []Log {
|
||||
func filterLogs(logs []Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []Log {
|
||||
var ret []Log
|
||||
|
||||
// Filter the logs for interesting stuff
|
||||
Logs:
|
||||
for _, log := range logs {
|
||||
if fromBlock != nil && fromBlock.Int64() >= 0 && uint64(fromBlock.Int64()) > log.BlockNumber {
|
||||
continue
|
||||
}
|
||||
if toBlock != nil && toBlock.Int64() >= 0 && uint64(toBlock.Int64()) < log.BlockNumber {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(addresses) > 0 && !includes(addresses, log.Address) {
|
||||
continue
|
||||
}
|
||||
|
|
@ -211,7 +219,6 @@ Logs:
|
|||
continue Logs
|
||||
}
|
||||
}
|
||||
|
||||
ret = append(ret, log)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,13 +43,17 @@ const (
|
|||
UnknownSubscription Type = iota
|
||||
// LogsSubscription queries for new or removed (chain reorg) logs
|
||||
LogsSubscription
|
||||
// PendingLogsSubscription queries for logs for the pending block
|
||||
// PendingLogsSubscription queries for logs in pending blocks
|
||||
PendingLogsSubscription
|
||||
// MinedAndPendingLogsSubscription queries for logs in mined and pending blocks.
|
||||
MinedAndPendingLogsSubscription
|
||||
// PendingTransactionsSubscription queries tx hashes for pending
|
||||
// transactions entering the pending state
|
||||
PendingTransactionsSubscription
|
||||
// BlocksSubscription queries hashes for blocks that are imported
|
||||
BlocksSubscription
|
||||
// LastSubscription keeps track of the last index
|
||||
LastIndexSubscription
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -63,19 +67,26 @@ type Log struct {
|
|||
Removed bool `json:"removed"`
|
||||
}
|
||||
|
||||
// MarshalJSON returns *l as the JSON encoding of l.
|
||||
func (l *Log) MarshalJSON() ([]byte, error) {
|
||||
fields := map[string]interface{}{
|
||||
"address": l.Address,
|
||||
"data": fmt.Sprintf("0x%x", l.Data),
|
||||
"blockNumber": fmt.Sprintf("%#x", l.BlockNumber),
|
||||
"blockNumber": nil,
|
||||
"logIndex": fmt.Sprintf("%#x", l.Index),
|
||||
"blockHash": l.BlockHash,
|
||||
"blockHash": nil,
|
||||
"transactionHash": l.TxHash,
|
||||
"transactionIndex": fmt.Sprintf("%#x", l.TxIndex),
|
||||
"topics": l.Topics,
|
||||
"removed": l.Removed,
|
||||
}
|
||||
|
||||
// mined logs
|
||||
if l.BlockHash != (common.Hash{}) {
|
||||
fields["blockNumber"] = fmt.Sprintf("%#x", l.BlockNumber)
|
||||
fields["blockHash"] = l.BlockHash
|
||||
}
|
||||
|
||||
return json.Marshal(fields)
|
||||
}
|
||||
|
||||
|
|
@ -169,8 +180,65 @@ func (es *EventSystem) subscribe(sub *subscription) *Subscription {
|
|||
}
|
||||
|
||||
// SubscribeLogs creates a subscription that will write all logs matching the
|
||||
// given criteria to the given logs channel. Default value for the from and to
|
||||
// block is "latest". If the fromBlock > toBlock an error is returned.
|
||||
func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []Log) (*Subscription, error) {
|
||||
var from, to rpc.BlockNumber
|
||||
if crit.FromBlock == nil {
|
||||
from = rpc.LatestBlockNumber
|
||||
} else {
|
||||
from = rpc.BlockNumber(crit.FromBlock.Int64())
|
||||
}
|
||||
if crit.ToBlock == nil {
|
||||
to = rpc.LatestBlockNumber
|
||||
} else {
|
||||
to = rpc.BlockNumber(crit.ToBlock.Int64())
|
||||
}
|
||||
|
||||
// only interested in pending logs
|
||||
if from == rpc.PendingBlockNumber && to == rpc.PendingBlockNumber {
|
||||
return es.subscribePendingLogs(crit, logs), nil
|
||||
}
|
||||
// only interested in new mined logs
|
||||
if from == rpc.LatestBlockNumber && to == rpc.LatestBlockNumber {
|
||||
return es.subscribeLogs(crit, logs), nil
|
||||
}
|
||||
// only interested in mined logs within a specific block range
|
||||
if from >= 0 && to >= 0 && to >= from {
|
||||
return es.subscribeLogs(crit, logs), nil
|
||||
}
|
||||
// interested in mined logs from a specific block number, new logs and pending logs
|
||||
if from >= rpc.LatestBlockNumber && to == rpc.PendingBlockNumber {
|
||||
return es.subscribeMinedPendingLogs(crit, logs), nil
|
||||
}
|
||||
// interested in logs from a specific block number to new mined blocks
|
||||
if from >= 0 && to == rpc.LatestBlockNumber {
|
||||
return es.subscribeLogs(crit, logs), nil
|
||||
}
|
||||
return nil, fmt.Errorf("invalid from and to block combination: from > to")
|
||||
}
|
||||
|
||||
// subscribeMinedPendingLogs creates a subscription that returned mined and
|
||||
// pending logs that match the given criteria.
|
||||
func (es *EventSystem) subscribeMinedPendingLogs(crit FilterCriteria, logs chan []Log) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: MinedAndPendingLogsSubscription,
|
||||
logsCrit: crit,
|
||||
created: time.Now(),
|
||||
logs: logs,
|
||||
hashes: make(chan common.Hash),
|
||||
headers: make(chan *types.Header),
|
||||
installed: make(chan struct{}),
|
||||
err: make(chan error),
|
||||
}
|
||||
|
||||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
// subscribeLogs creates a subscription that will write all logs matching the
|
||||
// given criteria to the given logs channel.
|
||||
func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []Log) *Subscription {
|
||||
func (es *EventSystem) subscribeLogs(crit FilterCriteria, logs chan []Log) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: LogsSubscription,
|
||||
|
|
@ -186,9 +254,9 @@ func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []Log) *Subs
|
|||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
// SubscribePendingLogs creates a subscription that will write pending logs matching the
|
||||
// given criteria to the given channel.
|
||||
func (es *EventSystem) SubscribePendingLogs(crit FilterCriteria, logs chan []Log) *Subscription {
|
||||
// subscribePendingLogs creates a subscription that writes transaction hashes for
|
||||
// transactions that enter the transaction pool.
|
||||
func (es *EventSystem) subscribePendingLogs(crit FilterCriteria, logs chan []Log) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: PendingLogsSubscription,
|
||||
|
|
@ -204,23 +272,6 @@ func (es *EventSystem) SubscribePendingLogs(crit FilterCriteria, logs chan []Log
|
|||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
// SubscribePendingTxEvents creates a sbuscription that writes transaction hashes for
|
||||
// transactions that enter the transaction pool.
|
||||
func (es *EventSystem) SubscribePendingTxEvents(hashes chan common.Hash) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: PendingTransactionsSubscription,
|
||||
created: time.Now(),
|
||||
logs: make(chan []Log),
|
||||
hashes: hashes,
|
||||
headers: make(chan *types.Header),
|
||||
installed: make(chan struct{}),
|
||||
err: make(chan error),
|
||||
}
|
||||
|
||||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
// SubscribeNewHeads creates a subscription that writes the header of a block that is
|
||||
// imported in the chain.
|
||||
func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscription {
|
||||
|
|
@ -238,6 +289,23 @@ func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscripti
|
|||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
// SubscribePendingTxEvents creates a subscription that writes transaction hashes for
|
||||
// transactions that enter the transaction pool.
|
||||
func (es *EventSystem) SubscribePendingTxEvents(hashes chan common.Hash) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: PendingTransactionsSubscription,
|
||||
created: time.Now(),
|
||||
logs: make(chan []Log),
|
||||
hashes: hashes,
|
||||
headers: make(chan *types.Header),
|
||||
installed: make(chan struct{}),
|
||||
err: make(chan error),
|
||||
}
|
||||
|
||||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
type filterIndex map[Type]map[rpc.ID]*subscription
|
||||
|
||||
// broadcast event to filters that match criteria.
|
||||
|
|
@ -251,7 +319,7 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) {
|
|||
if len(e) > 0 {
|
||||
for _, f := range filters[LogsSubscription] {
|
||||
if ev.Time.After(f.created) {
|
||||
if matchedLogs := filterLogs(convertLogs(e, false), f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
if matchedLogs := filterLogs(convertLogs(e, false), f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
f.logs <- matchedLogs
|
||||
}
|
||||
}
|
||||
|
|
@ -260,7 +328,7 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) {
|
|||
case core.RemovedLogsEvent:
|
||||
for _, f := range filters[LogsSubscription] {
|
||||
if ev.Time.After(f.created) {
|
||||
if matchedLogs := filterLogs(convertLogs(e.Logs, true), f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
if matchedLogs := filterLogs(convertLogs(e.Logs, true), f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
f.logs <- matchedLogs
|
||||
}
|
||||
}
|
||||
|
|
@ -268,7 +336,7 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) {
|
|||
case core.PendingLogsEvent:
|
||||
for _, f := range filters[PendingLogsSubscription] {
|
||||
if ev.Time.After(f.created) {
|
||||
if matchedLogs := filterLogs(convertLogs(e.Logs, false), f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
if matchedLogs := filterLogs(convertLogs(e.Logs, false), nil, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
f.logs <- matchedLogs
|
||||
}
|
||||
}
|
||||
|
|
@ -351,8 +419,8 @@ func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.
|
|||
}
|
||||
unfiltered = append(unfiltered, rl...)
|
||||
}
|
||||
logs := filterLogs(unfiltered, addresses, topics)
|
||||
//fmt.Println("found", len(logs))
|
||||
|
||||
logs := filterLogs(unfiltered, nil, nil, addresses, topics)
|
||||
return logs
|
||||
}
|
||||
return nil
|
||||
|
|
@ -364,6 +432,11 @@ func (es *EventSystem) eventLoop() {
|
|||
index = make(filterIndex)
|
||||
sub = es.mux.Subscribe(core.PendingLogsEvent{}, core.RemovedLogsEvent{}, vm.Logs{}, core.TxPreEvent{}, core.ChainEvent{})
|
||||
)
|
||||
|
||||
for i := UnknownSubscription; i < LastIndexSubscription; i++ {
|
||||
index[i] = make(map[rpc.ID]*subscription)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case ev, active := <-sub.Chan():
|
||||
|
|
@ -372,13 +445,22 @@ func (es *EventSystem) eventLoop() {
|
|||
}
|
||||
es.broadcast(index, ev)
|
||||
case f := <-es.install:
|
||||
if _, found := index[f.typ]; !found {
|
||||
index[f.typ] = make(map[rpc.ID]*subscription)
|
||||
if f.typ == MinedAndPendingLogsSubscription {
|
||||
// the type are logs and pending logs subscriptions
|
||||
index[LogsSubscription][f.id] = f
|
||||
index[PendingLogsSubscription][f.id] = f
|
||||
} else {
|
||||
index[f.typ][f.id] = f
|
||||
}
|
||||
index[f.typ][f.id] = f
|
||||
close(f.installed)
|
||||
case f := <-es.uninstall:
|
||||
delete(index[f.typ], f.id)
|
||||
if f.typ == MinedAndPendingLogsSubscription {
|
||||
// the type are logs and pending logs subscriptions
|
||||
delete(index[LogsSubscription], f.id)
|
||||
delete(index[PendingLogsSubscription], f.id)
|
||||
} else {
|
||||
delete(index[f.typ], f.id)
|
||||
}
|
||||
close(f.err)
|
||||
}
|
||||
}
|
||||
|
|
@ -386,6 +468,7 @@ func (es *EventSystem) eventLoop() {
|
|||
|
||||
// convertLogs is a helper utility that converts vm.Logs to []filter.Log.
|
||||
func convertLogs(in vm.Logs, removed bool) []Log {
|
||||
|
||||
logs := make([]Log, len(in))
|
||||
for i, l := range in {
|
||||
logs[i] = Log{l, removed}
|
||||
|
|
|
|||
|
|
@ -34,13 +34,6 @@ import (
|
|||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
)
|
||||
|
||||
type testBackend struct {
|
||||
mux *event.TypeMux
|
||||
db ethdb.Database
|
||||
|
|
@ -81,6 +74,11 @@ func TestBlockSubscription(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
|
||||
genesis = core.WriteGenesisBlockForTesting(db)
|
||||
chain, _ = core.GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *core.BlockGen) {})
|
||||
chainEvents = []core.ChainEvent{}
|
||||
|
|
@ -130,6 +128,11 @@ func TestPendingTxFilter(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
|
||||
transactions = []*types.Transaction{
|
||||
types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil),
|
||||
types.NewTransaction(1, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil),
|
||||
|
|
@ -150,9 +153,13 @@ func TestPendingTxFilter(t *testing.T) {
|
|||
}
|
||||
|
||||
for {
|
||||
h := api.GetFilterChanges(fid0).([]common.Hash)
|
||||
hashes = append(hashes, h...)
|
||||
results, err := api.GetFilterChanges(fid0)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to retrieve logs: %v", err)
|
||||
}
|
||||
|
||||
h := results.([]common.Hash)
|
||||
hashes = append(hashes, h...)
|
||||
if len(hashes) >= len(transactions) {
|
||||
break
|
||||
}
|
||||
|
|
@ -167,11 +174,86 @@ func TestPendingTxFilter(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestLogFilterCreation test whether a given filter criteria makes sense.
|
||||
// If not it must return an error.
|
||||
func TestLogFilterCreation(t *testing.T) {
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
|
||||
testCases = []struct {
|
||||
crit FilterCriteria
|
||||
success bool
|
||||
}{
|
||||
// defaults
|
||||
{FilterCriteria{}, true},
|
||||
// valid block number range
|
||||
{FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2)}, true},
|
||||
// "mined" block range to pending
|
||||
{FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, true},
|
||||
// new mined and pending blocks
|
||||
{FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, true},
|
||||
// from block "higher" than to block
|
||||
{FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(1)}, false},
|
||||
// from block "higher" than to block
|
||||
{FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false},
|
||||
// from block "higher" than to block
|
||||
{FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false},
|
||||
// from block "higher" than to block
|
||||
{FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, false},
|
||||
}
|
||||
)
|
||||
|
||||
for i, test := range testCases {
|
||||
_, err := api.NewFilter(test.crit)
|
||||
if test.success && err != nil {
|
||||
t.Errorf("expected filter creation for case %d to success, got %v", i, err)
|
||||
}
|
||||
if !test.success && err == nil {
|
||||
t.Errorf("expected testcase %d to fail with an error", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestInvalidLogFilterCreation tests whether invalid filter log criteria results in an error
|
||||
// when the filter is created.
|
||||
func TestInvalidLogFilterCreation(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
)
|
||||
|
||||
// different situations where log filter creation should fail.
|
||||
// Reason: fromBlock > toBlock
|
||||
testCases := []FilterCriteria{
|
||||
0: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())},
|
||||
1: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)},
|
||||
2: {FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)},
|
||||
}
|
||||
|
||||
for i, test := range testCases {
|
||||
if _, err := api.NewFilter(test); err == nil {
|
||||
t.Errorf("Expected NewFilter for case #%d to fail", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestLogFilter tests whether log filters match the correct logs that are posted to the event mux.
|
||||
func TestLogFilter(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
|
||||
firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111")
|
||||
secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222")
|
||||
thirdAddress = common.HexToAddress("0x3333333333333333333333333333333333333333")
|
||||
|
|
@ -180,8 +262,8 @@ func TestLogFilter(t *testing.T) {
|
|||
secondTopic = common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222")
|
||||
notUsedTopic = common.HexToHash("0x9999999999999999999999999999999999999999999999999999999999999999")
|
||||
|
||||
// posted twice, once as vm.Logs and once as core.PendingLogsEvent
|
||||
allLogs = vm.Logs{
|
||||
// Note, these are used for comparison of the test cases.
|
||||
vm.NewLog(firstAddr, []common.Hash{}, []byte(""), 0),
|
||||
vm.NewLog(firstAddr, []common.Hash{firstTopic}, []byte(""), 1),
|
||||
vm.NewLog(secondAddr, []common.Hash{firstTopic}, []byte(""), 1),
|
||||
|
|
@ -189,45 +271,64 @@ func TestLogFilter(t *testing.T) {
|
|||
vm.NewLog(thirdAddress, []common.Hash{secondTopic}, []byte(""), 3),
|
||||
}
|
||||
|
||||
expectedCase7 = vm.Logs{allLogs[3], allLogs[4], allLogs[0], allLogs[1], allLogs[2], allLogs[3], allLogs[4]}
|
||||
expectedCase11 = vm.Logs{allLogs[1], allLogs[2], allLogs[1], allLogs[2]}
|
||||
|
||||
testCases = []struct {
|
||||
crit FilterCriteria
|
||||
expected vm.Logs
|
||||
id rpc.ID
|
||||
}{
|
||||
// match all
|
||||
{FilterCriteria{}, allLogs, ""},
|
||||
0: {FilterCriteria{}, allLogs, ""},
|
||||
// match none due to no matching addresses
|
||||
{FilterCriteria{Addresses: []common.Address{common.Address{}, notUsedAddress}, Topics: [][]common.Hash{allLogs[0].Topics}}, vm.Logs{}, ""},
|
||||
1: {FilterCriteria{Addresses: []common.Address{common.Address{}, notUsedAddress}, Topics: [][]common.Hash{allLogs[0].Topics}}, vm.Logs{}, ""},
|
||||
// match logs based on addresses, ignore topics
|
||||
{FilterCriteria{Addresses: []common.Address{firstAddr}}, allLogs[:2], ""},
|
||||
2: {FilterCriteria{Addresses: []common.Address{firstAddr}}, allLogs[:2], ""},
|
||||
// match none due to no matching topics (match with address)
|
||||
{FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{[]common.Hash{notUsedTopic}}}, vm.Logs{}, ""},
|
||||
3: {FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{[]common.Hash{notUsedTopic}}}, vm.Logs{}, ""},
|
||||
// match logs based on addresses and topics
|
||||
{FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{[]common.Hash{firstTopic, secondTopic}}}, allLogs[3:5], ""},
|
||||
4: {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{[]common.Hash{firstTopic, secondTopic}}}, allLogs[3:5], ""},
|
||||
// match logs based on multiple addresses and "or" topics
|
||||
{FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{[]common.Hash{firstTopic, secondTopic}}}, allLogs[2:5], ""},
|
||||
// block numbers are ignored for filters created with New***Filter, these return all logs that match the given criterias when the state changes
|
||||
{FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(1), ToBlock: big.NewInt(2)}, allLogs[:2], ""},
|
||||
5: {FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{[]common.Hash{firstTopic, secondTopic}}}, allLogs[2:5], ""},
|
||||
// logs in the pending block
|
||||
6: {FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, allLogs[:2], ""},
|
||||
// mined logs with block num >= 2 or pending logs
|
||||
7: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, expectedCase7, ""},
|
||||
// all "mined" logs with block num >= 2
|
||||
8: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs[3:], ""},
|
||||
// all "mined" logs
|
||||
9: {FilterCriteria{ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs, ""},
|
||||
// all "mined" logs with 1>= block num <=2 and topic secondTopic
|
||||
10: {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2), Topics: [][]common.Hash{[]common.Hash{secondTopic}}}, allLogs[3:4], ""},
|
||||
// all "mined" and pending logs with topic firstTopic
|
||||
11: {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), Topics: [][]common.Hash{[]common.Hash{firstTopic}}}, expectedCase11, ""},
|
||||
}
|
||||
|
||||
err error
|
||||
)
|
||||
|
||||
// create all filters
|
||||
for i := range testCases {
|
||||
testCases[i].id = api.NewFilter(testCases[i].crit)
|
||||
testCases[i].id, _ = api.NewFilter(testCases[i].crit)
|
||||
}
|
||||
|
||||
// raise events
|
||||
time.Sleep(1 * time.Second)
|
||||
if err = mux.Post(allLogs); err != nil {
|
||||
if err := mux.Post(allLogs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := mux.Post(core.PendingLogsEvent{Logs: allLogs}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i, tt := range testCases {
|
||||
var fetched []Log
|
||||
for { // fetch all expected logs
|
||||
fetched = append(fetched, api.GetFilterChanges(tt.id).([]Log)...)
|
||||
results, err := api.GetFilterChanges(tt.id)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to fetch logs: %v", err)
|
||||
}
|
||||
|
||||
fetched = append(fetched, results.([]Log)...)
|
||||
if len(fetched) >= len(tt.expected) {
|
||||
break
|
||||
}
|
||||
|
|
@ -247,7 +348,6 @@ func TestLogFilter(t *testing.T) {
|
|||
if !reflect.DeepEqual(fetched[l].Log, tt.expected[l]) {
|
||||
t.Errorf("invalid log on index %d for case %d", l, i)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -257,6 +357,11 @@ func TestPendingLogsSubscription(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
|
||||
firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111")
|
||||
secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222")
|
||||
thirdAddress = common.HexToAddress("0x3333333333333333333333333333333333333333")
|
||||
|
|
@ -319,7 +424,7 @@ func TestPendingLogsSubscription(t *testing.T) {
|
|||
// (some) events are posted.
|
||||
for i := range testCases {
|
||||
testCases[i].c = make(chan []Log)
|
||||
testCases[i].sub = api.events.SubscribePendingLogs(testCases[i].crit, testCases[i].c)
|
||||
testCases[i].sub, _ = api.events.SubscribeLogs(testCases[i].crit, testCases[i].c)
|
||||
}
|
||||
|
||||
for n, test := range testCases {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
)
|
||||
|
||||
func makeReceipt(addr common.Address) *types.Receipt {
|
||||
|
|
@ -51,6 +52,7 @@ func BenchmarkMipmaps(b *testing.B) {
|
|||
|
||||
var (
|
||||
db, _ = ethdb.NewLDBDatabase(dir, 0, 0)
|
||||
mux = new(event.TypeMux)
|
||||
backend = &testBackend{mux, db}
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
|
|
@ -126,6 +128,7 @@ func TestFilters(t *testing.T) {
|
|||
|
||||
var (
|
||||
db, _ = ethdb.NewLDBDatabase(dir, 0, 0)
|
||||
mux = new(event.TypeMux)
|
||||
backend = &testBackend{mux, db}
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
|
|
@ -156,9 +157,9 @@ func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (*typ
|
|||
|
||||
// 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
|
||||
var num hexutil.Uint
|
||||
err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByHash", blockHash)
|
||||
return num.Uint(), err
|
||||
return uint(num), err
|
||||
}
|
||||
|
||||
// TransactionInBlock returns a single transaction at index in the given block.
|
||||
|
|
@ -196,11 +197,11 @@ func toBlockNumArg(number *big.Int) string {
|
|||
}
|
||||
|
||||
type rpcProgress struct {
|
||||
StartingBlock rpc.HexNumber
|
||||
CurrentBlock rpc.HexNumber
|
||||
HighestBlock rpc.HexNumber
|
||||
PulledStates rpc.HexNumber
|
||||
KnownStates rpc.HexNumber
|
||||
StartingBlock hexutil.Uint64
|
||||
CurrentBlock hexutil.Uint64
|
||||
HighestBlock hexutil.Uint64
|
||||
PulledStates hexutil.Uint64
|
||||
KnownStates hexutil.Uint64
|
||||
}
|
||||
|
||||
// SyncProgress retrieves the current progress of the sync algorithm. If there's
|
||||
|
|
@ -220,11 +221,11 @@ func (ec *Client) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, err
|
|||
return nil, err
|
||||
}
|
||||
return ðereum.SyncProgress{
|
||||
StartingBlock: progress.StartingBlock.Uint64(),
|
||||
CurrentBlock: progress.CurrentBlock.Uint64(),
|
||||
HighestBlock: progress.HighestBlock.Uint64(),
|
||||
PulledStates: progress.PulledStates.Uint64(),
|
||||
KnownStates: progress.KnownStates.Uint64(),
|
||||
StartingBlock: uint64(progress.StartingBlock),
|
||||
CurrentBlock: uint64(progress.CurrentBlock),
|
||||
HighestBlock: uint64(progress.HighestBlock),
|
||||
PulledStates: uint64(progress.PulledStates),
|
||||
KnownStates: uint64(progress.KnownStates),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -239,7 +240,7 @@ func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header)
|
|||
// 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
|
||||
var result hexutil.Big
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, toBlockNumArg(blockNumber))
|
||||
return (*big.Int)(&result), err
|
||||
}
|
||||
|
|
@ -247,7 +248,7 @@ func (ec *Client) BalanceAt(ctx context.Context, account common.Address, blockNu
|
|||
// 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
|
||||
var result hexutil.Bytes
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, toBlockNumArg(blockNumber))
|
||||
return result, err
|
||||
}
|
||||
|
|
@ -255,7 +256,7 @@ func (ec *Client) StorageAt(ctx context.Context, account common.Address, key com
|
|||
// 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
|
||||
var result hexutil.Bytes
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getCode", account, toBlockNumArg(blockNumber))
|
||||
return result, err
|
||||
}
|
||||
|
|
@ -263,9 +264,9 @@ func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumbe
|
|||
// 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
|
||||
var result hexutil.Uint64
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, toBlockNumArg(blockNumber))
|
||||
return result.Uint64(), err
|
||||
return uint64(result), err
|
||||
}
|
||||
|
||||
// Filters
|
||||
|
|
@ -286,7 +287,7 @@ func toFilterArg(q ethereum.FilterQuery) interface{} {
|
|||
arg := map[string]interface{}{
|
||||
"fromBlock": toBlockNumArg(q.FromBlock),
|
||||
"toBlock": toBlockNumArg(q.ToBlock),
|
||||
"addresses": q.Addresses,
|
||||
"address": q.Addresses,
|
||||
"topics": q.Topics,
|
||||
}
|
||||
if q.FromBlock == nil {
|
||||
|
|
@ -299,21 +300,21 @@ func toFilterArg(q ethereum.FilterQuery) interface{} {
|
|||
|
||||
// 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
|
||||
var result hexutil.Big
|
||||
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
|
||||
var result hexutil.Bytes
|
||||
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
|
||||
var result hexutil.Bytes
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getCode", account, "pending")
|
||||
return result, err
|
||||
}
|
||||
|
|
@ -321,16 +322,16 @@ func (ec *Client) PendingCodeAt(ctx context.Context, account common.Address) ([]
|
|||
// 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
|
||||
var result hexutil.Uint64
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, "pending")
|
||||
return result.Uint64(), err
|
||||
return uint64(result), 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
|
||||
var num hexutil.Uint
|
||||
err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByNumber", "pending")
|
||||
return num.Uint(), err
|
||||
return uint(num), err
|
||||
}
|
||||
|
||||
// TODO: SubscribePendingTransactions (needs server side)
|
||||
|
|
@ -344,29 +345,29 @@ func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) {
|
|||
// 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
|
||||
var hex hexutil.Bytes
|
||||
err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), toBlockNumArg(blockNumber))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return common.FromHex(hex), nil
|
||||
return 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
|
||||
var hex hexutil.Bytes
|
||||
err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), "pending")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return common.FromHex(hex), nil
|
||||
return 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
|
||||
var hex hexutil.Big
|
||||
if err := ec.c.CallContext(ctx, &hex, "eth_gasPrice"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -378,7 +379,7 @@ func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
|||
// 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
|
||||
var hex hexutil.Big
|
||||
err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -404,16 +405,16 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
|
|||
"to": msg.To,
|
||||
}
|
||||
if len(msg.Data) > 0 {
|
||||
arg["data"] = fmt.Sprintf("%#x", msg.Data)
|
||||
arg["data"] = hexutil.Bytes(msg.Data)
|
||||
}
|
||||
if msg.Value != nil {
|
||||
arg["value"] = fmt.Sprintf("%#x", msg.Value)
|
||||
arg["value"] = (*hexutil.Big)(msg.Value)
|
||||
}
|
||||
if msg.Gas != nil {
|
||||
arg["gas"] = fmt.Sprintf("%#x", msg.Gas)
|
||||
arg["gas"] = (*hexutil.Big)(msg.Gas)
|
||||
}
|
||||
if msg.GasPrice != nil {
|
||||
arg["gasPrice"] = fmt.Sprintf("%#x", msg.GasPrice)
|
||||
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
|
||||
}
|
||||
return arg
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,12 +37,6 @@ web3._extend({
|
|||
property: 'bzz',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'blockNetworkRead',
|
||||
call: 'bzz_blockNetworkRead',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'syncEnabled',
|
||||
call: 'bzz_syncEnabled',
|
||||
|
|
@ -68,17 +62,11 @@ web3._extend({
|
|||
inputFormatter: [null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'retrieve',
|
||||
call: 'bzz_retrieve',
|
||||
name: 'resolve',
|
||||
call: 'bzz_resolve',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'store',
|
||||
call: 'bzz_store',
|
||||
params: 2,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'get',
|
||||
call: 'bzz_get',
|
||||
|
|
|
|||
|
|
@ -201,14 +201,19 @@ func (self *StateObject) Copy() *StateObject {
|
|||
// Attribute accessors
|
||||
//
|
||||
|
||||
// empty returns whether the account is considered empty.
|
||||
func (self *StateObject) empty() bool {
|
||||
return self.nonce == 0 && self.balance.BitLen() == 0 && bytes.Equal(self.codeHash, emptyCodeHash)
|
||||
}
|
||||
|
||||
// Balance returns the account balance
|
||||
func (self *StateObject) Balance() *big.Int {
|
||||
return self.balance
|
||||
}
|
||||
|
||||
// Address returns the address of the contract/account
|
||||
func (c *StateObject) Address() common.Address {
|
||||
return c.address
|
||||
func (self *StateObject) Address() common.Address {
|
||||
return self.address
|
||||
}
|
||||
|
||||
// Code returns the contract code
|
||||
|
|
|
|||
|
|
@ -263,6 +263,13 @@ func (s *VMState) Exist(addr common.Address) bool {
|
|||
return res
|
||||
}
|
||||
|
||||
// Empty returns true if the account at the given address is considered empty
|
||||
func (s *VMState) Empty(addr common.Address) bool {
|
||||
so, err := s.state.GetStateObject(s.ctx, addr)
|
||||
s.errHandler(err)
|
||||
return so == nil || so.empty()
|
||||
}
|
||||
|
||||
// HasSuicided returns true if the given account has been marked for deletion
|
||||
// or false if the account does not exist
|
||||
func (s *VMState) HasSuicided(addr common.Address) bool {
|
||||
|
|
|
|||
|
|
@ -277,6 +277,7 @@ func newLocalMinedBlock(blockNumber uint64, prevMinedBlocks *uint64RingBuffer) (
|
|||
|
||||
func (self *worker) wait() {
|
||||
for {
|
||||
mustCommitNewWork := true
|
||||
for result := range self.recv {
|
||||
atomic.AddInt32(&self.atWork, -1)
|
||||
|
||||
|
|
@ -330,6 +331,8 @@ func (self *worker) wait() {
|
|||
core.WriteReceipts(self.chainDb, work.receipts)
|
||||
// Write map map bloom filters
|
||||
core.WriteMipmapBloom(self.chainDb, block.NumberU64(), work.receipts)
|
||||
// implicit by posting ChainHeadEvent
|
||||
mustCommitNewWork = false
|
||||
}
|
||||
|
||||
// broadcast before waiting for validation
|
||||
|
|
@ -358,7 +361,9 @@ func (self *worker) wait() {
|
|||
}
|
||||
glog.V(logger.Info).Infof("🔨 Mined %sblock (#%v / %x). %s", stale, block.Number(), block.Hash().Bytes()[:4], confirm)
|
||||
|
||||
self.commitNewWork()
|
||||
if mustCommitNewWork {
|
||||
self.commitNewWork()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -466,6 +471,7 @@ func (self *worker) commitNewWork() {
|
|||
|
||||
tstart := time.Now()
|
||||
parent := self.chain.CurrentBlock()
|
||||
|
||||
tstamp := tstart.Unix()
|
||||
if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 {
|
||||
tstamp = parent.Time().Int64() + 1
|
||||
|
|
@ -633,7 +639,16 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
|
|||
txs.Shift()
|
||||
}
|
||||
}
|
||||
|
||||
if len(coalescedLogs) > 0 || env.tcount > 0 {
|
||||
// make a copy, the state caches the logs and these logs get "upgraded" from pending to mined
|
||||
// logs by filling in the block hash when the block was mined by the local miner. This can
|
||||
// cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed.
|
||||
cpy := make(vm.Logs, len(coalescedLogs))
|
||||
for i, l := range coalescedLogs {
|
||||
cpy[i] = new(vm.Log)
|
||||
*cpy[i] = *l
|
||||
}
|
||||
go func(logs vm.Logs, tcount int) {
|
||||
if len(logs) > 0 {
|
||||
mux.Post(core.PendingLogsEvent{Logs: logs})
|
||||
|
|
@ -641,7 +656,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
|
|||
if tcount > 0 {
|
||||
mux.Post(core.PendingStateEvent{})
|
||||
}
|
||||
}(coalescedLogs, env.tcount)
|
||||
}(cpy, env.tcount)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import "fmt"
|
|||
const (
|
||||
VersionMajor = 1 // Major version component of the current release
|
||||
VersionMinor = 5 // Minor version component of the current release
|
||||
VersionPatch = 4 // Patch version component of the current release
|
||||
VersionPatch = 5 // Patch version component of the current release
|
||||
VersionMeta = "unstable" // Version metadata to append to the version string
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -54,11 +54,12 @@ type Config struct {
|
|||
PublicKey string
|
||||
BzzKey string
|
||||
EnsRoot common.Address
|
||||
NetworkId uint64
|
||||
}
|
||||
|
||||
// config is agnostic to where private key is coming from
|
||||
// so managing accounts is outside swarm and left to wrappers
|
||||
func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey) (self *Config, err error) {
|
||||
func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey, networkId uint64) (self *Config, err error) {
|
||||
address := crypto.PubkeyToAddress(prvKey.PublicKey) // default beneficiary address
|
||||
dirpath := filepath.Join(path, "bzz-"+common.Bytes2Hex(address.Bytes()))
|
||||
err = os.MkdirAll(dirpath, os.ModePerm)
|
||||
|
|
@ -82,6 +83,7 @@ func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey) (
|
|||
PublicKey: pubkeyhex,
|
||||
BzzKey: keyhex,
|
||||
EnsRoot: toyNetEnsRoot,
|
||||
NetworkId: networkId,
|
||||
}
|
||||
data, err = ioutil.ReadFile(confpath)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -83,7 +83,8 @@ var (
|
|||
"Port": "8500",
|
||||
"PublicKey": "0x045f5cfd26692e48d0017d380349bcf50982488bc11b5145f3ddf88b24924299048450542d43527fbe29a5cb32f38d62755393ac002e6bfdd71b8d7ba725ecd7a3",
|
||||
"BzzKey": "0xe861964402c0b78e2d44098329b8545726f215afa737d803714a4338552fcb81",
|
||||
"EnsRoot": "0xd344889e0be3e9ef6c26b0f60ef66a32e83c1b69"
|
||||
"EnsRoot": "0xd344889e0be3e9ef6c26b0f60ef66a32e83c1b69",
|
||||
"NetworkId": 323
|
||||
}`
|
||||
)
|
||||
|
||||
|
|
@ -95,7 +96,7 @@ func TestConfigWriteRead(t *testing.T) {
|
|||
defer os.RemoveAll(tmp)
|
||||
|
||||
prvkey := crypto.ToECDSA(common.Hex2Bytes(hexprvkey))
|
||||
orig, err := NewConfig(tmp, common.Address{}, prvkey)
|
||||
orig, err := NewConfig(tmp, common.Address{}, prvkey, 323)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
|
@ -109,7 +110,7 @@ func TestConfigWriteRead(t *testing.T) {
|
|||
t.Fatalf("default config mismatch:\nexpected: %v\ngot: %v", exp, string(data))
|
||||
}
|
||||
|
||||
conf, err := NewConfig(tmp, common.Address{}, prvkey)
|
||||
conf, err := NewConfig(tmp, common.Address{}, prvkey, 323)
|
||||
if err != nil {
|
||||
t.Fatalf("expected no error, got %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ type bzz struct {
|
|||
errors *errs.Errors // errors table
|
||||
backend chequebook.Backend
|
||||
lastActive time.Time
|
||||
NetworkId uint64
|
||||
|
||||
swap *swap.Swap // swap instance for the peer connection
|
||||
swapParams *bzzswap.SwapParams // swap settings both local and remote
|
||||
|
|
@ -126,7 +127,7 @@ on each peer connection
|
|||
The Run function of the Bzz protocol class creates a bzz instance
|
||||
which will represent the peer for the swarm hive and all peer-aware components
|
||||
*/
|
||||
func Bzz(cloud StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams) (p2p.Protocol, error) {
|
||||
func Bzz(cloud StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams, networkId uint64) (p2p.Protocol, error) {
|
||||
|
||||
// a single global request db is created for all peer connections
|
||||
// this is to persist delivery backlog and aid syncronisation
|
||||
|
|
@ -134,13 +135,15 @@ func Bzz(cloud StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess
|
|||
if err != nil {
|
||||
return p2p.Protocol{}, fmt.Errorf("error setting up request db: %v", err)
|
||||
}
|
||||
|
||||
if networkId == 0 {
|
||||
networkId = NetworkId
|
||||
}
|
||||
return p2p.Protocol{
|
||||
Name: "bzz",
|
||||
Version: Version,
|
||||
Length: ProtocolLength,
|
||||
Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||
return run(requestDb, cloud, backend, hive, dbaccess, sp, sy, p, rw)
|
||||
return run(requestDb, cloud, backend, hive, dbaccess, sp, sy, networkId, p, rw)
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -157,7 +160,7 @@ the main protocol loop that
|
|||
* whenever the loop terminates, the peer will disconnect with Subprotocol error
|
||||
* whenever handlers return an error the loop terminates
|
||||
*/
|
||||
func run(requestDb *storage.LDBDatabase, depo StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams, p *p2p.Peer, rw p2p.MsgReadWriter) (err error) {
|
||||
func run(requestDb *storage.LDBDatabase, depo StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams, networkId uint64, p *p2p.Peer, rw p2p.MsgReadWriter) (err error) {
|
||||
|
||||
self := &bzz{
|
||||
storage: depo,
|
||||
|
|
@ -175,6 +178,7 @@ func run(requestDb *storage.LDBDatabase, depo StorageHandler, backend chequebook
|
|||
syncParams: sy,
|
||||
swapEnabled: hive.swapEnabled,
|
||||
syncEnabled: true,
|
||||
NetworkId: networkId,
|
||||
}
|
||||
|
||||
// handle handshake
|
||||
|
|
@ -340,7 +344,7 @@ func (self *bzz) handleStatus() (err error) {
|
|||
Version: uint64(Version),
|
||||
ID: "honey",
|
||||
Addr: self.selfAddr(),
|
||||
NetworkId: uint64(NetworkId),
|
||||
NetworkId: uint64(self.NetworkId),
|
||||
Swap: &bzzswap.SwapProfile{
|
||||
Profile: self.swapParams.Profile,
|
||||
PayProfile: self.swapParams.PayProfile,
|
||||
|
|
@ -372,8 +376,8 @@ func (self *bzz) handleStatus() (err error) {
|
|||
return self.protoError(ErrDecode, " %v: %v", msg, err)
|
||||
}
|
||||
|
||||
if status.NetworkId != NetworkId {
|
||||
return self.protoError(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, NetworkId)
|
||||
if status.NetworkId != self.NetworkId {
|
||||
return self.protoError(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, self.NetworkId)
|
||||
}
|
||||
|
||||
if Version != status.Version {
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ func (self *Swarm) Stop() error {
|
|||
|
||||
// implements the node.Service interface
|
||||
func (self *Swarm) Protocols() []p2p.Protocol {
|
||||
proto, err := network.Bzz(self.depo, self.backend, self.hive, self.dbAccess, self.config.Swap, self.config.SyncParams)
|
||||
proto, err := network.Bzz(self.depo, self.backend, self.hive, self.dbAccess, self.config.Swap, self.config.SyncParams, self.config.NetworkId)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -279,7 +279,7 @@ func NewLocalSwarm(datadir, port string) (self *Swarm, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
config, err := api.NewConfig(datadir, common.Address{}, prvKey)
|
||||
config, err := api.NewConfig(datadir, common.Address{}, prvKey, network.NetworkId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue