aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeffrey Wilcke <geffobscura@gmail.com>2015-11-24 20:48:47 +0800
committerJeffrey Wilcke <geffobscura@gmail.com>2015-11-24 20:48:47 +0800
commit5490437942967638bcc6198035315f6811febaa8 (patch)
treeec4fbee454bacbf2b80b5a7ff402fb48dd2c10cf
parente5532154a50114d5ffb1ffd850b746cab00cb899 (diff)
parentb0fb48c389460193d9fc0a5118d79ff6dec48ce0 (diff)
downloadgo-tangerine-5490437942967638bcc6198035315f6811febaa8.tar
go-tangerine-5490437942967638bcc6198035315f6811febaa8.tar.gz
go-tangerine-5490437942967638bcc6198035315f6811febaa8.tar.bz2
go-tangerine-5490437942967638bcc6198035315f6811febaa8.tar.lz
go-tangerine-5490437942967638bcc6198035315f6811febaa8.tar.xz
go-tangerine-5490437942967638bcc6198035315f6811febaa8.tar.zst
go-tangerine-5490437942967638bcc6198035315f6811febaa8.zip
Merge branch 'develop' into release/1.3.2v1.3.2
Conflicts: VERSION cmd/geth/main.go
-rw-r--r--Makefile67
-rw-r--r--VERSION2
-rw-r--r--accounts/abi/abi.go12
-rw-r--r--accounts/abi/abi_test.go75
-rw-r--r--accounts/abi/type.go14
-rw-r--r--cmd/geth/main.go35
-rw-r--r--cmd/geth/usage.go1
-rw-r--r--cmd/utils/cmd.go10
-rw-r--r--cmd/utils/flags.go6
-rw-r--r--cmd/utils/legalese.go41
-rw-r--r--core/bench_test.go1
-rw-r--r--core/block_processor.go460
-rw-r--r--core/block_validator.go243
-rw-r--r--core/block_validator_test.go (renamed from core/block_processor_test.go)6
-rw-r--r--core/blockchain.go146
-rw-r--r--core/blockchain_test.go147
-rw-r--r--core/chain_makers.go11
-rw-r--r--core/chain_makers_test.go9
-rw-r--r--core/database_util.go (renamed from core/chain_util.go)178
-rw-r--r--core/database_util_test.go (renamed from core/chain_util_test.go)162
-rw-r--r--core/gaspool.go46
-rw-r--r--core/genesis.go2
-rw-r--r--core/state_processor.go107
-rw-r--r--core/transaction_util.go171
-rw-r--r--core/types.go70
-rw-r--r--core/vm/runtime/doc.go (renamed from core/types/common.go)11
-rw-r--r--core/vm/runtime/env.go106
-rw-r--r--core/vm/runtime/runtime.go121
-rw-r--r--core/vm/runtime/runtime_example_test.go (renamed from core/manager.go)26
-rw-r--r--core/vm/runtime/runtime_test.go120
-rw-r--r--crypto/secp256k1/panic_cb.go33
-rw-r--r--crypto/secp256k1/secp256.go118
-rw-r--r--crypto/secp256k1/secp256_test.go15
-rw-r--r--eth/backend.go122
-rw-r--r--eth/backend_test.go4
-rw-r--r--eth/downloader/downloader.go488
-rw-r--r--eth/downloader/downloader_test.go135
-rw-r--r--eth/downloader/peer.go240
-rw-r--r--eth/downloader/queue.go301
-rw-r--r--eth/filters/filter_test.go8
-rw-r--r--eth/gasprice.go2
-rw-r--r--eth/handler.go42
-rw-r--r--eth/helper_test.go4
-rw-r--r--eth/peer.go47
-rw-r--r--eth/protocol.go3
-rw-r--r--eth/sync.go4
-rw-r--r--eth/sync_test.go4
-rw-r--r--event/filter/filter_test.go29
-rw-r--r--jsre/jsre_test.go2
-rw-r--r--miner/worker.go38
-rw-r--r--p2p/peer.go46
-rw-r--r--p2p/protocol.go15
-rw-r--r--p2p/server.go63
-rw-r--r--rpc/api/admin.go4
-rw-r--r--rpc/api/args_test.go12
-rw-r--r--rpc/api/debug.go28
-rw-r--r--rpc/api/eth.go46
-rw-r--r--rpc/api/eth_args.go7
-rw-r--r--rpc/api/eth_js.go22
-rw-r--r--rpc/api/utils.go2
-rw-r--r--tests/files/BlockchainTests/bcStateTest.json705
-rw-r--r--tests/files/StateTests/stPreCompiledContracts.json144
-rw-r--r--tests/files/StateTests/stSystemOperationsTest.json55
-rw-r--r--tests/init.go7
-rw-r--r--whisper/whisper_test.go13
-rw-r--r--xeth/xeth.go97
66 files changed, 3539 insertions, 1772 deletions
diff --git a/Makefile b/Makefile
index d2b57e13f..41cbc1ce6 100644
--- a/Makefile
+++ b/Makefile
@@ -2,9 +2,17 @@
# with Go source code. If you know what GOPATH is then you probably
# don't need to bother with make.
-.PHONY: geth geth-cross geth-linux geth-darwin geth-windows geth-android evm all test travis-test-with-coverage xgo clean
+.PHONY: geth geth-cross evm all test travis-test-with-coverage xgo clean
+.PHONY: geth-linux geth-linux-arm geth-linux-386 geth-linux-amd64
+.PHONY: geth-darwin geth-darwin-386 geth-darwin-amd64
+.PHONY: geth-windows geth-windows-386 geth-windows-amd64
+.PHONY: geth-android geth-android-16 geth-android-21
+
GOBIN = build/bin
+CROSSDEPS = https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2
+GO ?= latest
+
geth:
build/env.sh go install -v $(shell build/flags.sh) ./cmd/geth
@echo "Done building."
@@ -14,26 +22,67 @@ geth-cross: geth-linux geth-darwin geth-windows geth-android
@echo "Full cross compilation done:"
@ls -l $(GOBIN)/geth-*
-geth-linux: xgo
- build/env.sh $(GOBIN)/xgo --dest=$(GOBIN) --deps=https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2 --targets=linux/* -v $(shell build/flags.sh) ./cmd/geth
+geth-linux: xgo geth-linux-arm geth-linux-386 geth-linux-amd64
@echo "Linux cross compilation done:"
@ls -l $(GOBIN)/geth-linux-*
-geth-darwin: xgo
- build/env.sh $(GOBIN)/xgo --dest=$(GOBIN) --deps=https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2 --targets=darwin/* -v $(shell build/flags.sh) ./cmd/geth
+geth-linux-arm: xgo
+ build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=linux/arm -v $(shell build/flags.sh) ./cmd/geth
+ @echo "Linux ARM cross compilation done:"
+ @ls -l $(GOBIN)/geth-linux-* | grep arm
+
+geth-linux-386: xgo
+ build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=linux/386 -v $(shell build/flags.sh) ./cmd/geth
+ @echo "Linux 386 cross compilation done:"
+ @ls -l $(GOBIN)/geth-linux-* | grep 386
+
+geth-linux-amd64: xgo
+ build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=linux/amd64 -v $(shell build/flags.sh) ./cmd/geth
+ @echo "Linux amd64 cross compilation done:"
+ @ls -l $(GOBIN)/geth-linux-* | grep amd64
+
+geth-darwin: xgo geth-darwin-386 geth-darwin-amd64
@echo "Darwin cross compilation done:"
@ls -l $(GOBIN)/geth-darwin-*
-geth-windows: xgo
- build/env.sh $(GOBIN)/xgo --dest=$(GOBIN) --deps=https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2 --targets=windows/* -v $(shell build/flags.sh) ./cmd/geth
+geth-darwin-386: xgo
+ build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=darwin/386 -v $(shell build/flags.sh) ./cmd/geth
+ @echo "Darwin 386 cross compilation done:"
+ @ls -l $(GOBIN)/geth-darwin-* | grep 386
+
+geth-darwin-amd64: xgo
+ build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=darwin/amd64 -v $(shell build/flags.sh) ./cmd/geth
+ @echo "Darwin amd64 cross compilation done:"
+ @ls -l $(GOBIN)/geth-darwin-* | grep amd64
+
+geth-windows: xgo geth-windows-386 geth-windows-amd64
@echo "Windows cross compilation done:"
@ls -l $(GOBIN)/geth-windows-*
-geth-android: xgo
- build/env.sh $(GOBIN)/xgo --dest=$(GOBIN) --deps=https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2 --targets=android-16/*,android-21/* -v $(shell build/flags.sh) ./cmd/geth
+geth-windows-386: xgo
+ build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=windows/386 -v $(shell build/flags.sh) ./cmd/geth
+ @echo "Windows 386 cross compilation done:"
+ @ls -l $(GOBIN)/geth-windows-* | grep 386
+
+geth-windows-amd64: xgo
+ build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=windows/amd64 -v $(shell build/flags.sh) ./cmd/geth
+ @echo "Windows amd64 cross compilation done:"
+ @ls -l $(GOBIN)/geth-windows-* | grep amd64
+
+geth-android: xgo geth-android-16 geth-android-21
@echo "Android cross compilation done:"
@ls -l $(GOBIN)/geth-android-*
+geth-android-16: xgo
+ build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=android-16/* -v $(shell build/flags.sh) ./cmd/geth
+ @echo "Android 16 cross compilation done:"
+ @ls -l $(GOBIN)/geth-android-16-*
+
+geth-android-21: xgo
+ build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=android-21/* -v $(shell build/flags.sh) ./cmd/geth
+ @echo "Android 21 cross compilation done:"
+ @ls -l $(GOBIN)/geth-android-21-*
+
evm:
build/env.sh $(GOROOT)/bin/go install -v $(shell build/flags.sh) ./cmd/evm
@echo "Done building."
diff --git a/VERSION b/VERSION
index 3a3cd8cc8..1892b9267 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.3.1
+1.3.2
diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go
index de3128902..3f05bfe2d 100644
--- a/accounts/abi/abi.go
+++ b/accounts/abi/abi.go
@@ -36,7 +36,7 @@ import (
type Method struct {
Name string
Const bool
- Input []Argument
+ Inputs []Argument
Return Type // not yet implemented
}
@@ -49,9 +49,9 @@ type Method struct {
// Please note that "int" is substitute for its canonical representation "int256"
func (m Method) String() (out string) {
out += m.Name
- types := make([]string, len(m.Input))
+ types := make([]string, len(m.Inputs))
i := 0
- for _, input := range m.Input {
+ for _, input := range m.Inputs {
types[i] = input.Type.String()
i++
}
@@ -104,7 +104,7 @@ func (abi ABI) pack(name string, args ...interface{}) ([]byte, error) {
var ret []byte
for i, a := range args {
- input := method.Input[i]
+ input := method.Inputs[i]
packed, err := input.Type.pack(a)
if err != nil {
@@ -129,8 +129,8 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
}
// start with argument count match
- if len(args) != len(method.Input) {
- return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Input))
+ if len(args) != len(method.Inputs) {
+ return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Inputs))
}
arguments, err := abi.pack(name, args...)
diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go
index 7706de05d..96dd3ee62 100644
--- a/accounts/abi/abi_test.go
+++ b/accounts/abi/abi_test.go
@@ -18,35 +18,38 @@ package abi
import (
"bytes"
+ "fmt"
+ "log"
"math/big"
"reflect"
"strings"
"testing"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
const jsondata = `
[
{ "name" : "balance", "const" : true },
- { "name" : "send", "const" : false, "input" : [ { "name" : "amount", "type" : "uint256" } ] }
+ { "name" : "send", "const" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] }
]`
const jsondata2 = `
[
{ "name" : "balance", "const" : true },
- { "name" : "send", "const" : false, "input" : [ { "name" : "amount", "type" : "uint256" } ] },
- { "name" : "test", "const" : false, "input" : [ { "name" : "number", "type" : "uint32" } ] },
- { "name" : "string", "const" : false, "input" : [ { "name" : "input", "type" : "string" } ] },
- { "name" : "bool", "const" : false, "input" : [ { "name" : "input", "type" : "bool" } ] },
- { "name" : "address", "const" : false, "input" : [ { "name" : "input", "type" : "address" } ] },
- { "name" : "string32", "const" : false, "input" : [ { "name" : "input", "type" : "string32" } ] },
- { "name" : "uint64[2]", "const" : false, "input" : [ { "name" : "input", "type" : "uint64[2]" } ] },
- { "name" : "uint64[]", "const" : false, "input" : [ { "name" : "input", "type" : "uint64[]" } ] },
- { "name" : "foo", "const" : false, "input" : [ { "name" : "input", "type" : "uint32" } ] },
- { "name" : "bar", "const" : false, "input" : [ { "name" : "input", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] },
- { "name" : "slice", "const" : false, "input" : [ { "name" : "input", "type" : "uint32[2]" } ] },
- { "name" : "slice256", "const" : false, "input" : [ { "name" : "input", "type" : "uint256[2]" } ] }
+ { "name" : "send", "const" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] },
+ { "name" : "test", "const" : false, "inputs" : [ { "name" : "number", "type" : "uint32" } ] },
+ { "name" : "string", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "string" } ] },
+ { "name" : "bool", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "bool" } ] },
+ { "name" : "address", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "address" } ] },
+ { "name" : "string32", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "string32" } ] },
+ { "name" : "uint64[2]", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[2]" } ] },
+ { "name" : "uint64[]", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[]" } ] },
+ { "name" : "foo", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] },
+ { "name" : "bar", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] },
+ { "name" : "slice", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] },
+ { "name" : "slice256", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] }
]`
func TestType(t *testing.T) {
@@ -344,3 +347,49 @@ func TestPackSliceBig(t *testing.T) {
t.Errorf("expected %x got %x", sig, packed)
}
}
+
+func ExampleJSON() {
+ const definition = `[{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"isBar","outputs":[{"name":"","type":"bool"}],"type":"function"}]`
+
+ abi, err := JSON(strings.NewReader(definition))
+ if err != nil {
+ log.Fatalln(err)
+ }
+ out, err := abi.Pack("isBar", common.HexToAddress("01"))
+ if err != nil {
+ log.Fatalln(err)
+ }
+
+ fmt.Printf("%x\n", out)
+ // Output:
+ // 1f2c40920000000000000000000000000000000000000000000000000000000000000001
+}
+
+func TestBytes(t *testing.T) {
+ const definition = `[
+ { "name" : "balance", "const" : true, "inputs" : [ { "name" : "address", "type" : "bytes20" } ] },
+ { "name" : "send", "const" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] }
+]`
+
+ abi, err := JSON(strings.NewReader(definition))
+ if err != nil {
+ t.Fatal(err)
+ }
+ ok := make([]byte, 20)
+ _, err = abi.Pack("balance", ok)
+ if err != nil {
+ t.Error(err)
+ }
+
+ toosmall := make([]byte, 19)
+ _, err = abi.Pack("balance", toosmall)
+ if err != nil {
+ t.Error(err)
+ }
+
+ toobig := make([]byte, 21)
+ _, err = abi.Pack("balance", toobig)
+ if err == nil {
+ t.Error("expected error")
+ }
+}
diff --git a/accounts/abi/type.go b/accounts/abi/type.go
index b16822d3b..16d7491e7 100644
--- a/accounts/abi/type.go
+++ b/accounts/abi/type.go
@@ -43,7 +43,7 @@ type Type struct {
stringKind string // holds the unparsed string for deriving signatures
}
-// New type returns a fully parsed Type given by the input string or an error if it can't be parsed.
+// NewType returns a fully parsed Type given by the input string or an error if it can't be parsed.
//
// Strings can be in the format of:
//
@@ -130,6 +130,10 @@ func NewType(t string) (typ Type, err error) {
if vsize > 0 {
typ.Size = 32
}
+ case "bytes":
+ typ.Kind = reflect.Slice
+ typ.Type = byte_ts
+ typ.Size = vsize
default:
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
}
@@ -200,7 +204,13 @@ func (t Type) pack(v interface{}) ([]byte, error) {
} else {
return common.LeftPadBytes(common.Big0.Bytes(), 32), nil
}
+ case reflect.Array:
+ if v, ok := value.Interface().(common.Address); ok {
+ return t.pack(v[:])
+ } else if v, ok := value.Interface().(common.Hash); ok {
+ return t.pack(v[:])
+ }
}
- panic("unreached")
+ return nil, fmt.Errorf("ABI: bad input given %T", value.Kind())
}
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index f70a0bb67..9ac067209 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -48,10 +48,10 @@ import (
const (
ClientIdentifier = "Geth"
- Version = "1.3.1"
+ Version = "1.3.2"
VersionMajor = 1
VersionMinor = 3
- VersionPatch = 1
+ VersionPatch = 2
)
var (
@@ -305,6 +305,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Conso
utils.OlympicFlag,
utils.FastSyncFlag,
utils.CacheFlag,
+ utils.LightKDFFlag,
utils.JSpathFlag,
utils.ListenPortFlag,
utils.MaxPeersFlag,
@@ -404,8 +405,6 @@ func makeDefaultExtra() []byte {
}
func run(ctx *cli.Context) {
- utils.CheckLegalese(utils.MustDataDir(ctx))
-
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
cfg.ExtraData = makeExtra(ctx)
@@ -420,8 +419,6 @@ func run(ctx *cli.Context) {
}
func attach(ctx *cli.Context) {
- utils.CheckLegalese(utils.MustDataDir(ctx))
-
var client comms.EthereumClient
var err error
if ctx.Args().Present() {
@@ -453,8 +450,6 @@ func attach(ctx *cli.Context) {
}
func console(ctx *cli.Context) {
- utils.CheckLegalese(utils.MustDataDir(ctx))
-
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
cfg.ExtraData = makeExtra(ctx)
@@ -487,8 +482,6 @@ func console(ctx *cli.Context) {
}
func execJSFiles(ctx *cli.Context) {
- utils.CheckLegalese(utils.MustDataDir(ctx))
-
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
ethereum, err := eth.New(cfg)
if err != nil {
@@ -514,8 +507,6 @@ func execJSFiles(ctx *cli.Context) {
}
func unlockAccount(ctx *cli.Context, am *accounts.Manager, addr string, i int, inputpassphrases []string) (addrHex, auth string, passphrases []string) {
- utils.CheckLegalese(ctx.GlobalString(utils.DataDirFlag.Name))
-
var err error
passphrases = inputpassphrases
addrHex, err = utils.ParamToAddress(addr, am)
@@ -540,16 +531,12 @@ func unlockAccount(ctx *cli.Context, am *accounts.Manager, addr string, i int, i
}
func blockRecovery(ctx *cli.Context) {
- utils.CheckLegalese(utils.MustDataDir(ctx))
-
- arg := ctx.Args().First()
- if len(ctx.Args()) < 1 && len(arg) > 0 {
+ if len(ctx.Args()) < 1 {
glog.Fatal("recover requires block number or hash")
}
+ arg := ctx.Args().First()
cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
- utils.CheckLegalese(cfg.DataDir)
-
blockDb, err := ethdb.NewLDBDatabase(filepath.Join(cfg.DataDir, "blockchain"), cfg.DatabaseCache)
if err != nil {
glog.Fatalln("could not open db:", err)
@@ -610,8 +597,6 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) {
}
func accountList(ctx *cli.Context) {
- utils.CheckLegalese(utils.MustDataDir(ctx))
-
am := utils.MakeAccountManager(ctx)
accts, err := am.Accounts()
if err != nil {
@@ -663,8 +648,6 @@ func getPassPhrase(ctx *cli.Context, desc string, confirmation bool, i int, inpu
}
func accountCreate(ctx *cli.Context) {
- utils.CheckLegalese(utils.MustDataDir(ctx))
-
am := utils.MakeAccountManager(ctx)
passphrase, _ := getPassPhrase(ctx, "Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, nil)
acct, err := am.NewAccount(passphrase)
@@ -675,8 +658,6 @@ func accountCreate(ctx *cli.Context) {
}
func accountUpdate(ctx *cli.Context) {
- utils.CheckLegalese(utils.MustDataDir(ctx))
-
am := utils.MakeAccountManager(ctx)
arg := ctx.Args().First()
if len(arg) == 0 {
@@ -692,8 +673,6 @@ func accountUpdate(ctx *cli.Context) {
}
func importWallet(ctx *cli.Context) {
- utils.CheckLegalese(utils.MustDataDir(ctx))
-
keyfile := ctx.Args().First()
if len(keyfile) == 0 {
utils.Fatalf("keyfile must be given as argument")
@@ -714,8 +693,6 @@ func importWallet(ctx *cli.Context) {
}
func accountImport(ctx *cli.Context) {
- utils.CheckLegalese(utils.MustDataDir(ctx))
-
keyfile := ctx.Args().First()
if len(keyfile) == 0 {
utils.Fatalf("keyfile must be given as argument")
@@ -730,8 +707,6 @@ func accountImport(ctx *cli.Context) {
}
func makedag(ctx *cli.Context) {
- utils.CheckLegalese(utils.MustDataDir(ctx))
-
args := ctx.Args()
wrongArgs := func() {
utils.Fatalf(`Usage: geth makedag <block number> <outputdir>`)
diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go
index 9223b7cd6..5c09e29ce 100644
--- a/cmd/geth/usage.go
+++ b/cmd/geth/usage.go
@@ -69,6 +69,7 @@ var AppHelpFlagGroups = []flagGroup{
utils.GenesisFileFlag,
utils.IdentityFlag,
utils.FastSyncFlag,
+ utils.LightKDFFlag,
utils.CacheFlag,
utils.BlockchainVersionFlag,
},
diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go
index 9b75ccab4..5cbb58124 100644
--- a/cmd/utils/cmd.go
+++ b/cmd/utils/cmd.go
@@ -95,16 +95,6 @@ func PromptPassword(prompt string, warnTerm bool) (string, error) {
return input, err
}
-func CheckLegalese(datadir string) {
- // check "first run"
- if !common.FileExist(datadir) {
- r, _ := PromptConfirm(legalese)
- if !r {
- Fatalf("Must accept to continue. Shutting down...\n")
- }
- }
-}
-
// Fatalf formats a message to standard error and exits the program.
// The message is also printed to standard output if standard error
// is redirected to a different file.
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index d741d0544..3792dc1e0 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -150,11 +150,11 @@ var (
}
FastSyncFlag = cli.BoolFlag{
Name: "fast",
- Usage: "Enables fast syncing through state downloads",
+ Usage: "Enable fast syncing through state downloads",
}
LightKDFFlag = cli.BoolFlag{
Name: "lightkdf",
- Usage: "Reduce KDF memory & CPU usage at some expense of KDF strength",
+ Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
}
// Miner settings
// TODO: refactor CPU vs GPU mining flags
@@ -557,8 +557,6 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database
Fatalf("Could not start chainmanager: %v", err)
}
- proc := core.NewBlockProcessor(chainDb, pow, chain, eventMux)
- chain.SetProcessor(proc)
return chain, chainDb
}
diff --git a/cmd/utils/legalese.go b/cmd/utils/legalese.go
deleted file mode 100644
index 09e687c17..000000000
--- a/cmd/utils/legalese.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright 2015 The go-ethereum Authors
-// This file is part of go-ethereum.
-//
-// go-ethereum is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// go-ethereum 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 General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
-
-package utils
-
-const (
- legalese = `
-=======================================
-Disclaimer of Liabilites and Warranties
-=======================================
-
-THE USER EXPRESSLY KNOWS AND AGREES THAT THE USER IS USING THE ETHEREUM PLATFORM AT THE USER’S SOLE
-RISK. THE USER REPRESENTS THAT THE USER HAS AN ADEQUATE UNDERSTANDING OF THE RISKS, USAGE AND
-INTRICACIES OF CRYPTOGRAPHIC TOKENS AND BLOCKCHAIN-BASED OPEN SOURCE SOFTWARE, ETH PLATFORM AND ETH.
-THE USER ACKNOWLEDGES AND AGREES THAT, TO THE FULLEST EXTENT PERMITTED BY ANY APPLICABLE LAW, THE
-DISCLAIMERS OF LIABILITY CONTAINED HEREIN APPLY TO ANY AND ALL DAMAGES OR INJURY WHATSOEVER CAUSED
-BY OR RELATED TO RISKS OF, USE OF, OR INABILITY TO USE, ETH OR THE ETHEREUM PLATFORM UNDER ANY CAUSE
-OR ACTION WHATSOEVER OF ANY KIND IN ANY JURISDICTION, INCLUDING, WITHOUT LIMITATION, ACTIONS FOR
-BREACH OF WARRANTY, BREACH OF CONTRACT OR TORT (INCLUDING NEGLIGENCE) AND THAT NEITHER STIFTUNG
-ETHEREUM NOR ETHEREUM TEAM SHALL BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR
-CONSEQUENTIAL DAMAGES, INCLUDING FOR LOSS OF PROFITS, GOODWILL OR DATA. SOME JURISDICTIONS DO NOT
-ALLOW THE EXCLUSION OF CERTAIN WARRANTIES OR THE LIMITATION OR EXCLUSION OF LIABILITY FOR CERTAIN
-TYPES OF DAMAGES. THEREFORE, SOME OF THE ABOVE LIMITATIONS IN THIS SECTION MAY NOT APPLY TO A USER.
-IN PARTICULAR, NOTHING IN THESE TERMS SHALL AFFECT THE STATUTORY RIGHTS OF ANY USER OR EXCLUDE
-INJURY ARISING FROM ANY WILLFUL MISCONDUCT OR FRAUD OF STIFTUNG ETHEREUM.
-
-Do you accept this agreement?`
-)
diff --git a/core/bench_test.go b/core/bench_test.go
index b5eb51803..6fa7659b9 100644
--- a/core/bench_test.go
+++ b/core/bench_test.go
@@ -169,7 +169,6 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// State and blocks are stored in the same DB.
evmux := new(event.TypeMux)
chainman, _ := NewBlockChain(db, FakePow{}, evmux)
- chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux))
defer chainman.Stop()
b.ReportAllocs()
b.ResetTimer()
diff --git a/core/block_processor.go b/core/block_processor.go
deleted file mode 100644
index e7b2f63e5..000000000
--- a/core/block_processor.go
+++ /dev/null
@@ -1,460 +0,0 @@
-// Copyright 2014 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 core
-
-import (
- "fmt"
- "math/big"
- "sync"
- "time"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/state"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/event"
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/logger/glog"
- "github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/pow"
- "gopkg.in/fatih/set.v0"
-)
-
-const (
- // must be bumped when consensus algorithm is changed, this forces the upgradedb
- // command to be run (forces the blocks to be imported again using the new algorithm)
- BlockChainVersion = 3
-)
-
-type BlockProcessor struct {
- chainDb ethdb.Database
- // Mutex for locking the block processor. Blocks can only be handled one at a time
- mutex sync.Mutex
- // Canonical block chain
- bc *BlockChain
- // non-persistent key/value memory storage
- mem map[string]*big.Int
- // Proof of work used for validating
- Pow pow.PoW
-
- events event.Subscription
-
- eventMux *event.TypeMux
-}
-
-// GasPool tracks the amount of gas available during
-// execution of the transactions in a block.
-// The zero value is a pool with zero gas available.
-type GasPool big.Int
-
-// AddGas makes gas available for execution.
-func (gp *GasPool) AddGas(amount *big.Int) *GasPool {
- i := (*big.Int)(gp)
- i.Add(i, amount)
- return gp
-}
-
-// SubGas deducts the given amount from the pool if enough gas is
-// available and returns an error otherwise.
-func (gp *GasPool) SubGas(amount *big.Int) error {
- i := (*big.Int)(gp)
- if i.Cmp(amount) < 0 {
- return &GasLimitErr{Have: new(big.Int).Set(i), Want: amount}
- }
- i.Sub(i, amount)
- return nil
-}
-
-func (gp *GasPool) String() string {
- return (*big.Int)(gp).String()
-}
-
-func NewBlockProcessor(db ethdb.Database, pow pow.PoW, blockchain *BlockChain, eventMux *event.TypeMux) *BlockProcessor {
- sm := &BlockProcessor{
- chainDb: db,
- mem: make(map[string]*big.Int),
- Pow: pow,
- bc: blockchain,
- eventMux: eventMux,
- }
- return sm
-}
-
-func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) {
- gp := new(GasPool).AddGas(block.GasLimit())
- if glog.V(logger.Core) {
- glog.Infof("%x: gas (+ %v)", block.Coinbase(), gp)
- }
-
- // Process the transactions on to parent state
- receipts, err = sm.ApplyTransactions(gp, statedb, block, block.Transactions(), transientProcess)
- if err != nil {
- return nil, err
- }
-
- return receipts, nil
-}
-
-func (self *BlockProcessor) ApplyTransaction(gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) {
- _, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, header), tx, gp)
- if err != nil {
- return nil, nil, err
- }
-
- // Update the state with pending changes
- usedGas.Add(usedGas, gas)
- receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas)
- receipt.TxHash = tx.Hash()
- receipt.GasUsed = new(big.Int).Set(gas)
- if MessageCreatesContract(tx) {
- from, _ := tx.From()
- receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce())
- }
-
- logs := statedb.GetLogs(tx.Hash())
- receipt.Logs = logs
- receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
-
- glog.V(logger.Debug).Infoln(receipt)
-
- // Notify all subscribers
- if !transientProcess {
- go self.eventMux.Post(TxPostEvent{tx})
- go self.eventMux.Post(logs)
- }
-
- return receipt, gas, err
-}
-func (self *BlockProcessor) BlockChain() *BlockChain {
- return self.bc
-}
-
-func (self *BlockProcessor) ApplyTransactions(gp *GasPool, statedb *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, error) {
- var (
- receipts types.Receipts
- totalUsedGas = big.NewInt(0)
- err error
- cumulativeSum = new(big.Int)
- header = block.Header()
- )
-
- for i, tx := range txs {
- statedb.StartRecord(tx.Hash(), block.Hash(), i)
-
- receipt, txGas, err := self.ApplyTransaction(gp, statedb, header, tx, totalUsedGas, transientProcess)
- if err != nil {
- return nil, err
- }
-
- if err != nil {
- glog.V(logger.Core).Infoln("TX err:", err)
- }
- receipts = append(receipts, receipt)
-
- cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice()))
- }
-
- if block.GasUsed().Cmp(totalUsedGas) != 0 {
- return nil, ValidationError(fmt.Sprintf("gas used error (%v / %v)", block.GasUsed(), totalUsedGas))
- }
-
- if transientProcess {
- go self.eventMux.Post(PendingBlockEvent{block, statedb.Logs()})
- }
-
- return receipts, err
-}
-
-func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs vm.Logs, err error) {
- // Processing a blocks may never happen simultaneously
- sm.mutex.Lock()
- defer sm.mutex.Unlock()
-
- if !sm.bc.HasBlock(block.ParentHash()) {
- return nil, ParentError(block.ParentHash())
- }
- parent := sm.bc.GetBlock(block.ParentHash())
-
- // FIXME Change to full header validation. See #1225
- errch := make(chan bool)
- go func() { errch <- sm.Pow.Verify(block) }()
-
- logs, _, err = sm.processWithParent(block, parent)
- if !<-errch {
- return nil, ValidationError("Block's nonce is invalid (= %x)", block.Nonce)
- }
-
- return logs, err
-}
-
-// Process block will attempt to process the given block's transactions and applies them
-// on top of the block's parent state (given it exists) and will return wether it was
-// successful or not.
-func (sm *BlockProcessor) Process(block *types.Block) (logs vm.Logs, receipts types.Receipts, err error) {
- // Processing a blocks may never happen simultaneously
- sm.mutex.Lock()
- defer sm.mutex.Unlock()
-
- if sm.bc.HasBlock(block.Hash()) {
- if _, err := state.New(block.Root(), sm.chainDb); err == nil {
- return nil, nil, &KnownBlockError{block.Number(), block.Hash()}
- }
- }
- if parent := sm.bc.GetBlock(block.ParentHash()); parent != nil {
- if _, err := state.New(parent.Root(), sm.chainDb); err == nil {
- return sm.processWithParent(block, parent)
- }
- }
- return nil, nil, ParentError(block.ParentHash())
-}
-
-func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs vm.Logs, receipts types.Receipts, err error) {
- // Create a new state based on the parent's root (e.g., create copy)
- state, err := state.New(parent.Root(), sm.chainDb)
- if err != nil {
- return nil, nil, err
- }
- header := block.Header()
- uncles := block.Uncles()
- txs := block.Transactions()
-
- // Block validation
- if err = ValidateHeader(sm.Pow, header, parent.Header(), false, false); err != nil {
- return
- }
-
- // There can be at most two uncles
- if len(uncles) > 2 {
- return nil, nil, ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(uncles))
- }
-
- receipts, err = sm.TransitionState(state, parent, block, false)
- if err != nil {
- return
- }
-
- // Validate the received block's bloom with the one derived from the generated receipts.
- // For valid blocks this should always validate to true.
- rbloom := types.CreateBloom(receipts)
- if rbloom != header.Bloom {
- err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
- return
- }
-
- // The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]]))
- // can be used by light clients to make sure they've received the correct Txs
- txSha := types.DeriveSha(txs)
- if txSha != header.TxHash {
- err = fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha)
- return
- }
-
- // Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
- receiptSha := types.DeriveSha(receipts)
- if receiptSha != header.ReceiptHash {
- err = fmt.Errorf("invalid receipt root hash. received=%x calculated=%x", header.ReceiptHash, receiptSha)
- return
- }
-
- // Verify UncleHash before running other uncle validations
- unclesSha := types.CalcUncleHash(uncles)
- if unclesSha != header.UncleHash {
- err = fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha)
- return
- }
-
- // Verify uncles
- if err = sm.VerifyUncles(state, block, parent); err != nil {
- return
- }
- // Accumulate static rewards; block reward, uncle's and uncle inclusion.
- AccumulateRewards(state, header, uncles)
-
- // Commit state objects/accounts to a database batch and calculate
- // the state root. The database is not modified if the root
- // doesn't match.
- root, batch := state.CommitBatch()
- if header.Root != root {
- return nil, nil, fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root)
- }
-
- // Execute the database writes.
- batch.Write()
-
- return state.Logs(), receipts, nil
-}
-
-var (
- big8 = big.NewInt(8)
- big32 = big.NewInt(32)
-)
-
-// AccumulateRewards credits the coinbase of the given block with the
-// mining reward. The total reward consists of the static block reward
-// and rewards for included uncles. The coinbase of each uncle block is
-// also rewarded.
-func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*types.Header) {
- reward := new(big.Int).Set(BlockReward)
- r := new(big.Int)
- for _, uncle := range uncles {
- r.Add(uncle.Number, big8)
- r.Sub(r, header.Number)
- r.Mul(r, BlockReward)
- r.Div(r, big8)
- statedb.AddBalance(uncle.Coinbase, r)
-
- r.Div(BlockReward, big32)
- reward.Add(reward, r)
- }
- statedb.AddBalance(header.Coinbase, reward)
-}
-
-func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *types.Block) error {
- uncles := set.New()
- ancestors := make(map[common.Hash]*types.Block)
- for _, ancestor := range sm.bc.GetBlocksFromHash(block.ParentHash(), 7) {
- ancestors[ancestor.Hash()] = ancestor
- // Include ancestors uncles in the uncle set. Uncles must be unique.
- for _, uncle := range ancestor.Uncles() {
- uncles.Add(uncle.Hash())
- }
- }
- ancestors[block.Hash()] = block
- uncles.Add(block.Hash())
-
- for i, uncle := range block.Uncles() {
- hash := uncle.Hash()
- if uncles.Has(hash) {
- // Error not unique
- return UncleError("uncle[%d](%x) not unique", i, hash[:4])
- }
- uncles.Add(hash)
-
- if ancestors[hash] != nil {
- branch := fmt.Sprintf(" O - %x\n |\n", block.Hash())
- for h := range ancestors {
- branch += fmt.Sprintf(" O - %x\n |\n", h)
- }
- glog.Infoln(branch)
- return UncleError("uncle[%d](%x) is ancestor", i, hash[:4])
- }
-
- if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == parent.Hash() {
- return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4])
- }
-
- if err := ValidateHeader(sm.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil {
- return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err))
- }
- }
-
- return nil
-}
-
-// GetBlockReceipts returns the receipts beloniging to the block hash
-func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) types.Receipts {
- if block := sm.BlockChain().GetBlock(bhash); block != nil {
- return GetBlockReceipts(sm.chainDb, block.Hash())
- }
-
- return nil
-}
-
-// GetLogs returns the logs of the given block. This method is using a two step approach
-// where it tries to get it from the (updated) method which gets them from the receipts or
-// the depricated way by re-processing the block.
-func (sm *BlockProcessor) GetLogs(block *types.Block) (logs vm.Logs, err error) {
- receipts := GetBlockReceipts(sm.chainDb, block.Hash())
- // coalesce logs
- for _, receipt := range receipts {
- logs = append(logs, receipt.Logs...)
- }
- return logs, nil
-}
-
-// ValidateHeader verifies the validity of a header, relying on the database and
-// POW behind the block processor.
-func (sm *BlockProcessor) ValidateHeader(header *types.Header, checkPow, uncle bool) error {
- // Short circuit if the header's already known or its parent missing
- if sm.bc.HasHeader(header.Hash()) {
- return nil
- }
- if parent := sm.bc.GetHeader(header.ParentHash); parent == nil {
- return ParentError(header.ParentHash)
- } else {
- return ValidateHeader(sm.Pow, header, parent, checkPow, uncle)
- }
-}
-
-// ValidateHeaderWithParent verifies the validity of a header, relying on the database and
-// POW behind the block processor.
-func (sm *BlockProcessor) ValidateHeaderWithParent(header, parent *types.Header, checkPow, uncle bool) error {
- if sm.bc.HasHeader(header.Hash()) {
- return nil
- }
- return ValidateHeader(sm.Pow, header, parent, checkPow, uncle)
-}
-
-// See YP section 4.3.4. "Block Header Validity"
-// Validates a header. Returns an error if the header is invalid.
-func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {
- if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
- return fmt.Errorf("Header extra data too long (%d)", len(header.Extra))
- }
- if uncle {
- if header.Time.Cmp(common.MaxBig) == 1 {
- return BlockTSTooBigErr
- }
- } else {
- if header.Time.Cmp(big.NewInt(time.Now().Unix())) == 1 {
- return BlockFutureErr
- }
- }
- if header.Time.Cmp(parent.Time) != 1 {
- return BlockEqualTSErr
- }
-
- expd := CalcDifficulty(header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty)
- if expd.Cmp(header.Difficulty) != 0 {
- return fmt.Errorf("Difficulty check failed for header %v, %v", header.Difficulty, expd)
- }
-
- a := new(big.Int).Set(parent.GasLimit)
- a = a.Sub(a, header.GasLimit)
- a.Abs(a)
- b := new(big.Int).Set(parent.GasLimit)
- b = b.Div(b, params.GasLimitBoundDivisor)
- if !(a.Cmp(b) < 0) || (header.GasLimit.Cmp(params.MinGasLimit) == -1) {
- return fmt.Errorf("GasLimit check failed for header %v (%v > %v)", header.GasLimit, a, b)
- }
-
- num := new(big.Int).Set(parent.Number)
- num.Sub(header.Number, num)
- if num.Cmp(big.NewInt(1)) != 0 {
- return BlockNumberErr
- }
-
- if checkPow {
- // Verify the nonce of the header. Return an error if it's not valid
- if !pow.Verify(types.NewBlockWithHeader(header)) {
- return &BlockNonceErr{Hash: header.Hash(), Number: header.Number, Nonce: header.Nonce.Uint64()}
- }
- }
- return nil
-}
diff --git a/core/block_validator.go b/core/block_validator.go
new file mode 100644
index 000000000..62d096d02
--- /dev/null
+++ b/core/block_validator.go
@@ -0,0 +1,243 @@
+// Copyright 2014 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 core
+
+import (
+ "fmt"
+ "math/big"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/logger/glog"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/pow"
+ "gopkg.in/fatih/set.v0"
+)
+
+// BlockValidator is responsible for validating block headers, uncles and
+// processed state.
+//
+// BlockValidator implements Validator.
+type BlockValidator struct {
+ bc *BlockChain // Canonical block chain
+ Pow pow.PoW // Proof of work used for validating
+}
+
+// NewBlockValidator returns a new block validator which is safe for re-use
+func NewBlockValidator(blockchain *BlockChain, pow pow.PoW) *BlockValidator {
+ validator := &BlockValidator{
+ Pow: pow,
+ bc: blockchain,
+ }
+ return validator
+}
+
+// ValidateBlock validates the given block's header and uncles and verifies the
+// the block header's transaction and uncle roots.
+//
+// ValidateBlock does not validate the header's pow. The pow work validated
+// seperately so we can process them in paralel.
+//
+// ValidateBlock also validates and makes sure that any previous state (or present)
+// state that might or might not be present is checked to make sure that fast
+// sync has done it's job proper. This prevents the block validator form accepting
+// false positives where a header is present but the state is not.
+func (v *BlockValidator) ValidateBlock(block *types.Block) error {
+ if v.bc.HasBlock(block.Hash()) {
+ if _, err := state.New(block.Root(), v.bc.chainDb); err == nil {
+ return &KnownBlockError{block.Number(), block.Hash()}
+ }
+ }
+ parent := v.bc.GetBlock(block.ParentHash())
+ if parent == nil {
+ return ParentError(block.ParentHash())
+ }
+ if _, err := state.New(parent.Root(), v.bc.chainDb); err != nil {
+ return ParentError(block.ParentHash())
+ }
+
+ header := block.Header()
+ // validate the block header
+ if err := ValidateHeader(v.Pow, header, parent.Header(), false, false); err != nil {
+ return err
+ }
+ // verify the uncles are correctly rewarded
+ if err := v.VerifyUncles(block, parent); err != nil {
+ return err
+ }
+
+ // Verify UncleHash before running other uncle validations
+ unclesSha := types.CalcUncleHash(block.Uncles())
+ if unclesSha != header.UncleHash {
+ return fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha)
+ }
+
+ // The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]]))
+ // can be used by light clients to make sure they've received the correct Txs
+ txSha := types.DeriveSha(block.Transactions())
+ if txSha != header.TxHash {
+ return fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha)
+ }
+
+ return nil
+}
+
+// ValidateState validates the various changes that happen after a state
+// transition, such as amount of used gas, the receipt roots and the state root
+// itself. ValidateState returns a database batch if the validation was a succes
+// otherwise nil and an error is returned.
+func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas *big.Int) (err error) {
+ header := block.Header()
+ if block.GasUsed().Cmp(usedGas) != 0 {
+ return ValidationError(fmt.Sprintf("gas used error (%v / %v)", block.GasUsed(), usedGas))
+ }
+ // Validate the received block's bloom with the one derived from the generated receipts.
+ // For valid blocks this should always validate to true.
+ rbloom := types.CreateBloom(receipts)
+ if rbloom != header.Bloom {
+ return fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
+ }
+ // Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
+ receiptSha := types.DeriveSha(receipts)
+ if receiptSha != header.ReceiptHash {
+ return fmt.Errorf("invalid receipt root hash. received=%x calculated=%x", header.ReceiptHash, receiptSha)
+ }
+ // Validate the state root against the received state root and throw
+ // an error if they don't match.
+ if root := statedb.IntermediateRoot(); header.Root != root {
+ return fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root)
+ }
+ return nil
+}
+
+// VerifyUncles verifies the given block's uncles and applies the Ethereum
+// consensus rules to the various block headers included; it will return an
+// error if any of the included uncle headers were invalid. It returns an error
+// if the validation failed.
+func (v *BlockValidator) VerifyUncles(block, parent *types.Block) error {
+ // validate that there at most 2 uncles included in this block
+ if len(block.Uncles()) > 2 {
+ return ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(block.Uncles()))
+ }
+
+ uncles := set.New()
+ ancestors := make(map[common.Hash]*types.Block)
+ for _, ancestor := range v.bc.GetBlocksFromHash(block.ParentHash(), 7) {
+ ancestors[ancestor.Hash()] = ancestor
+ // Include ancestors uncles in the uncle set. Uncles must be unique.
+ for _, uncle := range ancestor.Uncles() {
+ uncles.Add(uncle.Hash())
+ }
+ }
+ ancestors[block.Hash()] = block
+ uncles.Add(block.Hash())
+
+ for i, uncle := range block.Uncles() {
+ hash := uncle.Hash()
+ if uncles.Has(hash) {
+ // Error not unique
+ return UncleError("uncle[%d](%x) not unique", i, hash[:4])
+ }
+ uncles.Add(hash)
+
+ if ancestors[hash] != nil {
+ branch := fmt.Sprintf(" O - %x\n |\n", block.Hash())
+ for h := range ancestors {
+ branch += fmt.Sprintf(" O - %x\n |\n", h)
+ }
+ glog.Infoln(branch)
+ return UncleError("uncle[%d](%x) is ancestor", i, hash[:4])
+ }
+
+ if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == parent.Hash() {
+ return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4])
+ }
+
+ if err := ValidateHeader(v.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil {
+ return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err))
+ }
+ }
+
+ return nil
+}
+
+// ValidateHeader validates the given header and, depending on the pow arg,
+// checks the proof of work of the given header. Returns an error if the
+// validation failed.
+func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow bool) error {
+ // Short circuit if the parent is missing.
+ if parent == nil {
+ return ParentError(header.ParentHash)
+ }
+ // Short circuit if the header's already known or its parent missing
+ if v.bc.HasHeader(header.Hash()) {
+ return nil
+ }
+ return ValidateHeader(v.Pow, header, parent, checkPow, false)
+}
+
+// Validates a header. Returns an error if the header is invalid.
+//
+// See YP section 4.3.4. "Block Header Validity"
+func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {
+ if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
+ return fmt.Errorf("Header extra data too long (%d)", len(header.Extra))
+ }
+
+ if uncle {
+ if header.Time.Cmp(common.MaxBig) == 1 {
+ return BlockTSTooBigErr
+ }
+ } else {
+ if header.Time.Cmp(big.NewInt(time.Now().Unix())) == 1 {
+ return BlockFutureErr
+ }
+ }
+ if header.Time.Cmp(parent.Time) != 1 {
+ return BlockEqualTSErr
+ }
+
+ expd := CalcDifficulty(header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty)
+ if expd.Cmp(header.Difficulty) != 0 {
+ return fmt.Errorf("Difficulty check failed for header %v, %v", header.Difficulty, expd)
+ }
+
+ a := new(big.Int).Set(parent.GasLimit)
+ a = a.Sub(a, header.GasLimit)
+ a.Abs(a)
+ b := new(big.Int).Set(parent.GasLimit)
+ b = b.Div(b, params.GasLimitBoundDivisor)
+ if !(a.Cmp(b) < 0) || (header.GasLimit.Cmp(params.MinGasLimit) == -1) {
+ return fmt.Errorf("GasLimit check failed for header %v (%v > %v)", header.GasLimit, a, b)
+ }
+
+ num := new(big.Int).Set(parent.Number)
+ num.Sub(header.Number, num)
+ if num.Cmp(big.NewInt(1)) != 0 {
+ return BlockNumberErr
+ }
+
+ if checkPow {
+ // Verify the nonce of the header. Return an error if it's not valid
+ if !pow.Verify(types.NewBlockWithHeader(header)) {
+ return &BlockNonceErr{header.Number, header.Hash(), header.Nonce.Uint64()}
+ }
+ }
+ return nil
+}
diff --git a/core/block_processor_test.go b/core/block_validator_test.go
index 3050456b4..70953d76d 100644
--- a/core/block_processor_test.go
+++ b/core/block_validator_test.go
@@ -30,7 +30,7 @@ import (
"github.com/ethereum/go-ethereum/pow/ezp"
)
-func proc() (*BlockProcessor, *BlockChain) {
+func proc() (Validator, *BlockChain) {
db, _ := ethdb.NewMemDatabase()
var mux event.TypeMux
@@ -39,7 +39,7 @@ func proc() (*BlockProcessor, *BlockChain) {
if err != nil {
fmt.Println(err)
}
- return NewBlockProcessor(db, ezp.New(), blockchain, &mux), blockchain
+ return blockchain.validator, blockchain
}
func TestNumber(t *testing.T) {
@@ -81,7 +81,7 @@ func TestPutReceipt(t *testing.T) {
Index: 0,
}}
- PutReceipts(db, types.Receipts{receipt})
+ WriteReceipts(db, types.Receipts{receipt})
receipt = GetReceipt(db, common.Hash{})
if receipt == nil {
t.Error("expected to get 1 receipt, got none.")
diff --git a/core/blockchain.go b/core/blockchain.go
index cea346e38..5e1fc9424 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
@@ -61,17 +62,34 @@ const (
blockCacheLimit = 256
maxFutureBlocks = 256
maxTimeFutureBlocks = 30
+ // must be bumped when consensus algorithm is changed, this forces the upgradedb
+ // command to be run (forces the blocks to be imported again using the new algorithm)
+ BlockChainVersion = 3
)
+// BlockChain represents the canonical chain given a database with a genesis
+// block. The Blockchain manages chain imports, reverts, chain reorganisations.
+//
+// Importing blocks in to the block chain happens according to the set of rules
+// defined by the two stage Validator. Processing of blocks is done using the
+// Processor which processes the included transaction. The validation of the state
+// is done in the second part of the Validator. Failing results in aborting of
+// the import.
+//
+// The BlockChain also helps in returning blocks from **any** chain included
+// in the database as well as blocks that represents the canonical chain. It's
+// important to note that GetBlock can return any block and does not need to be
+// included in the canonical one where as GetBlockByNumber always represents the
+// canonical chain.
type BlockChain struct {
chainDb ethdb.Database
- processor types.BlockProcessor
eventMux *event.TypeMux
genesisBlock *types.Block
// Last known total difficulty
mu sync.RWMutex
chainmu sync.RWMutex
tsmu sync.RWMutex
+ procmu sync.RWMutex
checkpoint int // checkpoint counts towards the new checkpoint
currentHeader *types.Header // Current head of the header chain (may be above the block chain!)
@@ -91,10 +109,15 @@ type BlockChain struct {
procInterrupt int32 // interrupt signaler for block processing
wg sync.WaitGroup
- pow pow.PoW
- rand *mrand.Rand
+ pow pow.PoW
+ rand *mrand.Rand
+ processor Processor
+ validator Validator
}
+// NewBlockChain returns a fully initialised block chain using information
+// available in the database. It initialiser the default Ethereum Validator and
+// Processor.
func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) {
headerCache, _ := lru.New(headerCacheLimit)
bodyCache, _ := lru.New(bodyCacheLimit)
@@ -121,6 +144,8 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
return nil, err
}
bc.rand = mrand.New(mrand.NewSource(seed.Int64()))
+ bc.SetValidator(NewBlockValidator(bc, pow))
+ bc.SetProcessor(NewStateProcessor(bc))
bc.genesisBlock = bc.GetBlockByNumber(0)
if bc.genesisBlock == nil {
@@ -292,6 +317,7 @@ func (self *BlockChain) FastSyncCommitHead(hash common.Hash) error {
return nil
}
+// GasLimit returns the gas limit of the current HEAD block.
func (self *BlockChain) GasLimit() *big.Int {
self.mu.RLock()
defer self.mu.RUnlock()
@@ -299,6 +325,7 @@ func (self *BlockChain) GasLimit() *big.Int {
return self.currentBlock.GasLimit()
}
+// LastBlockHash return the hash of the HEAD block.
func (self *BlockChain) LastBlockHash() common.Hash {
self.mu.RLock()
defer self.mu.RUnlock()
@@ -333,6 +360,8 @@ func (self *BlockChain) CurrentFastBlock() *types.Block {
return self.currentFastBlock
}
+// Status returns status information about the current chain such as the HEAD Td,
+// the HEAD hash and the hash of the genesis block.
func (self *BlockChain) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) {
self.mu.RLock()
defer self.mu.RUnlock()
@@ -340,10 +369,38 @@ func (self *BlockChain) Status() (td *big.Int, currentBlock common.Hash, genesis
return self.GetTd(self.currentBlock.Hash()), self.currentBlock.Hash(), self.genesisBlock.Hash()
}
-func (self *BlockChain) SetProcessor(proc types.BlockProcessor) {
- self.processor = proc
+// SetProcessor sets the processor required for making state modifications.
+func (self *BlockChain) SetProcessor(processor Processor) {
+ self.procmu.Lock()
+ defer self.procmu.Unlock()
+ self.processor = processor
+}
+
+// SetValidator sets the validator which is used to validate incoming blocks.
+func (self *BlockChain) SetValidator(validator Validator) {
+ self.procmu.Lock()
+ defer self.procmu.Unlock()
+ self.validator = validator
}
+// Validator returns the current validator.
+func (self *BlockChain) Validator() Validator {
+ self.procmu.RLock()
+ defer self.procmu.RUnlock()
+ return self.validator
+}
+
+// Processor returns the current processor.
+func (self *BlockChain) Processor() Processor {
+ self.procmu.RLock()
+ defer self.procmu.RUnlock()
+ return self.processor
+}
+
+// AuxValidator returns the auxiliary validator (Proof of work atm)
+func (self *BlockChain) AuxValidator() pow.PoW { return self.pow }
+
+// State returns a new mutable state based on the current HEAD block.
func (self *BlockChain) State() (*state.StateDB, error) {
return state.New(self.CurrentBlock().Root(), self.chainDb)
}
@@ -606,6 +663,8 @@ func (self *BlockChain) GetUnclesInChain(block *types.Block, length int) []*type
return uncles
}
+// Stop stops the blockchain service. If any imports are currently in progress
+// it will abort them using the procInterrupt.
func (bc *BlockChain) Stop() {
if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) {
return
@@ -758,9 +817,9 @@ func (self *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int)
var err error
if index == 0 {
- err = self.processor.ValidateHeader(header, checkPow, false)
+ err = self.Validator().ValidateHeader(header, self.GetHeader(header.ParentHash), checkPow)
} else {
- err = self.processor.ValidateHeaderWithParent(header, chain[index-1], checkPow, false)
+ err = self.Validator().ValidateHeader(header, chain[index-1], checkPow)
}
if err != nil {
errs[index] = err
@@ -913,7 +972,7 @@ func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain
glog.Fatal(errs[index])
return
}
- if err := PutBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil {
+ if err := WriteBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil {
errs[index] = fmt.Errorf("failed to write block receipts: %v", err)
atomic.AddInt32(&failed, 1)
glog.Fatal(errs[index])
@@ -1025,9 +1084,10 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
// faster than direct delivery and requires much less mutex
// acquiring.
var (
- stats struct{ queued, processed, ignored int }
- events = make([]interface{}, 0, len(chain))
- tstart = time.Now()
+ stats struct{ queued, processed, ignored int }
+ events = make([]interface{}, 0, len(chain))
+ coalescedLogs vm.Logs
+ tstart = time.Now()
nonceChecked = make([]bool, len(chain))
)
@@ -1057,12 +1117,12 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
if BadHashes[block.Hash()] {
err := BadHashError(block.Hash())
- blockErr(block, err)
+ reportBlock(block, err)
return i, err
}
- // Call in to the block processor and check for errors. It's likely that if one block fails
- // all others will fail too (unless a known block is returned).
- logs, receipts, err := self.processor.Process(block)
+ // Stage 1 validation of the block using the chain's validator
+ // interface.
+ err := self.Validator().ValidateBlock(block)
if err != nil {
if IsKnownBlockErr(err) {
stats.ignored++
@@ -1089,14 +1149,41 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
continue
}
- blockErr(block, err)
+ reportBlock(block, err)
- go ReportBlock(block, err)
+ return i, err
+ }
+ // Create a new statedb using the parent block and report an
+ // error if it fails.
+ statedb, err := state.New(self.GetBlock(block.ParentHash()).Root(), self.chainDb)
+ if err != nil {
+ reportBlock(block, err)
return i, err
}
- if err := PutBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil {
- glog.V(logger.Warn).Infoln("error writing block receipts:", err)
+ // Process block using the parent state as reference point.
+ receipts, logs, usedGas, err := self.processor.Process(block, statedb)
+ if err != nil {
+ reportBlock(block, err)
+ return i, err
+ }
+ // Validate the state using the default validator
+ err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash()), statedb, receipts, usedGas)
+ if err != nil {
+ reportBlock(block, err)
+ return i, err
+ }
+ // Write state changes to database
+ _, err = statedb.Commit()
+ if err != nil {
+ return i, err
+ }
+
+ // coalesce logs for later processing
+ coalescedLogs = append(coalescedLogs, logs...)
+
+ if err := WriteBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil {
+ return i, err
}
txcount += len(block.Transactions())
@@ -1105,6 +1192,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
if err != nil {
return i, err
}
+
switch status {
case CanonStatTy:
if glog.V(logger.Debug) {
@@ -1113,11 +1201,11 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
events = append(events, ChainEvent{block, block.Hash(), logs})
// This puts transactions in a extra db for rpc
- if err := PutTransactions(self.chainDb, block, block.Transactions()); err != nil {
+ if err := WriteTransactions(self.chainDb, block); err != nil {
return i, err
}
// store the receipts
- if err := PutReceipts(self.chainDb, receipts); err != nil {
+ if err := WriteReceipts(self.chainDb, receipts); err != nil {
return i, err
}
// Write map map bloom filters
@@ -1141,7 +1229,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
start, end := chain[0], chain[len(chain)-1]
glog.Infof("imported %d block(s) (%d queued %d ignored) including %d txs in %v. #%v [%x / %x]\n", stats.processed, stats.queued, stats.ignored, txcount, tend, end.Number(), start.Hash().Bytes()[:4], end.Hash().Bytes()[:4])
}
- go self.postChainEvents(events)
+ go self.postChainEvents(events, coalescedLogs)
return 0, nil
}
@@ -1206,12 +1294,12 @@ func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
// insert the block in the canonical way, re-writing history
self.insert(block)
// write canonical receipts and transactions
- if err := PutTransactions(self.chainDb, block, block.Transactions()); err != nil {
+ if err := WriteTransactions(self.chainDb, block); err != nil {
return err
}
receipts := GetBlockReceipts(self.chainDb, block.Hash())
// write receipts
- if err := PutReceipts(self.chainDb, receipts); err != nil {
+ if err := WriteReceipts(self.chainDb, receipts); err != nil {
return err
}
// Write map map bloom filters
@@ -1239,7 +1327,9 @@ func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
// postChainEvents iterates over the events generated by a chain insertion and
// posts them into the event mux.
-func (self *BlockChain) postChainEvents(events []interface{}) {
+func (self *BlockChain) postChainEvents(events []interface{}, logs vm.Logs) {
+ // post event logs for further processing
+ self.eventMux.Post(logs)
for _, event := range events {
if event, ok := event.(ChainEvent); ok {
// We need some control over the mining operation. Acquiring locks and waiting for the miner to create new block takes too long
@@ -1265,9 +1355,13 @@ func (self *BlockChain) update() {
}
}
-func blockErr(block *types.Block, err error) {
+// reportBlock reports the given block and error using the canonical block
+// reporting tool. Reporting the block to the service is handled in a separate
+// goroutine.
+func reportBlock(block *types.Block, err error) {
if glog.V(logger.Error) {
glog.Errorf("Bad block #%v (%s)\n", block.Number(), block.Hash().Hex())
glog.Errorf(" %v", err)
}
+ go ReportBlock(block, err)
}
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index 8ddc5032b..f18b5d084 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -28,6 +28,7 @@ import (
"github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
@@ -53,31 +54,29 @@ func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain {
WriteTestNetGenesisBlock(db, 0)
blockchain, err := NewBlockChain(db, thePow(), &eventMux)
if err != nil {
- t.Error("failed creating chainmanager:", err)
+ t.Error("failed creating blockchain:", err)
t.FailNow()
return nil
}
- blockMan := NewBlockProcessor(db, nil, blockchain, &eventMux)
- blockchain.SetProcessor(blockMan)
return blockchain
}
// Test fork of length N starting from block i
-func testFork(t *testing.T, processor *BlockProcessor, i, n int, full bool, comparator func(td1, td2 *big.Int)) {
+func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, comparator func(td1, td2 *big.Int)) {
// Copy old chain up to #i into a new db
- db, processor2, err := newCanonical(i, full)
+ db, blockchain2, err := newCanonical(i, full)
if err != nil {
t.Fatal("could not make new canonical in testFork", err)
}
// Assert the chains have the same header/block at #i
var hash1, hash2 common.Hash
if full {
- hash1 = processor.bc.GetBlockByNumber(uint64(i)).Hash()
- hash2 = processor2.bc.GetBlockByNumber(uint64(i)).Hash()
+ hash1 = blockchain.GetBlockByNumber(uint64(i)).Hash()
+ hash2 = blockchain2.GetBlockByNumber(uint64(i)).Hash()
} else {
- hash1 = processor.bc.GetHeaderByNumber(uint64(i)).Hash()
- hash2 = processor2.bc.GetHeaderByNumber(uint64(i)).Hash()
+ hash1 = blockchain.GetHeaderByNumber(uint64(i)).Hash()
+ hash2 = blockchain2.GetHeaderByNumber(uint64(i)).Hash()
}
if hash1 != hash2 {
t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1)
@@ -88,13 +87,13 @@ func testFork(t *testing.T, processor *BlockProcessor, i, n int, full bool, comp
headerChainB []*types.Header
)
if full {
- blockChainB = makeBlockChain(processor2.bc.CurrentBlock(), n, db, forkSeed)
- if _, err := processor2.bc.InsertChain(blockChainB); err != nil {
+ blockChainB = makeBlockChain(blockchain2.CurrentBlock(), n, db, forkSeed)
+ if _, err := blockchain2.InsertChain(blockChainB); err != nil {
t.Fatalf("failed to insert forking chain: %v", err)
}
} else {
- headerChainB = makeHeaderChain(processor2.bc.CurrentHeader(), n, db, forkSeed)
- if _, err := processor2.bc.InsertHeaderChain(headerChainB, 1); err != nil {
+ headerChainB = makeHeaderChain(blockchain2.CurrentHeader(), n, db, forkSeed)
+ if _, err := blockchain2.InsertHeaderChain(headerChainB, 1); err != nil {
t.Fatalf("failed to insert forking chain: %v", err)
}
}
@@ -102,17 +101,17 @@ func testFork(t *testing.T, processor *BlockProcessor, i, n int, full bool, comp
var tdPre, tdPost *big.Int
if full {
- tdPre = processor.bc.GetTd(processor.bc.CurrentBlock().Hash())
- if err := testBlockChainImport(blockChainB, processor); err != nil {
+ tdPre = blockchain.GetTd(blockchain.CurrentBlock().Hash())
+ if err := testBlockChainImport(blockChainB, blockchain); err != nil {
t.Fatalf("failed to import forked block chain: %v", err)
}
- tdPost = processor.bc.GetTd(blockChainB[len(blockChainB)-1].Hash())
+ tdPost = blockchain.GetTd(blockChainB[len(blockChainB)-1].Hash())
} else {
- tdPre = processor.bc.GetTd(processor.bc.CurrentHeader().Hash())
- if err := testHeaderChainImport(headerChainB, processor); err != nil {
+ tdPre = blockchain.GetTd(blockchain.CurrentHeader().Hash())
+ if err := testHeaderChainImport(headerChainB, blockchain); err != nil {
t.Fatalf("failed to import forked header chain: %v", err)
}
- tdPost = processor.bc.GetTd(headerChainB[len(headerChainB)-1].Hash())
+ tdPost = blockchain.GetTd(headerChainB[len(headerChainB)-1].Hash())
}
// Compare the total difficulties of the chains
comparator(tdPre, tdPost)
@@ -127,37 +126,52 @@ func printChain(bc *BlockChain) {
// testBlockChainImport tries to process a chain of blocks, writing them into
// the database if successful.
-func testBlockChainImport(chain []*types.Block, processor *BlockProcessor) error {
+func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
for _, block := range chain {
// Try and process the block
- if _, _, err := processor.Process(block); err != nil {
+ err := blockchain.Validator().ValidateBlock(block)
+ if err != nil {
if IsKnownBlockErr(err) {
continue
}
return err
}
- // Manually insert the block into the database, but don't reorganize (allows subsequent testing)
- processor.bc.mu.Lock()
- WriteTd(processor.chainDb, block.Hash(), new(big.Int).Add(block.Difficulty(), processor.bc.GetTd(block.ParentHash())))
- WriteBlock(processor.chainDb, block)
- processor.bc.mu.Unlock()
+ statedb, err := state.New(blockchain.GetBlock(block.ParentHash()).Root(), blockchain.chainDb)
+ if err != nil {
+ return err
+ }
+ receipts, _, usedGas, err := blockchain.Processor().Process(block, statedb)
+ if err != nil {
+ reportBlock(block, err)
+ return err
+ }
+ err = blockchain.Validator().ValidateState(block, blockchain.GetBlock(block.ParentHash()), statedb, receipts, usedGas)
+ if err != nil {
+ reportBlock(block, err)
+ return err
+ }
+ blockchain.mu.Lock()
+ WriteTd(blockchain.chainDb, block.Hash(), new(big.Int).Add(block.Difficulty(), blockchain.GetTd(block.ParentHash())))
+ WriteBlock(blockchain.chainDb, block)
+ statedb.Commit()
+ blockchain.mu.Unlock()
}
return nil
}
// testHeaderChainImport tries to process a chain of header, writing them into
// the database if successful.
-func testHeaderChainImport(chain []*types.Header, processor *BlockProcessor) error {
+func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error {
for _, header := range chain {
// Try and validate the header
- if err := processor.ValidateHeader(header, false, false); err != nil {
+ if err := blockchain.Validator().ValidateHeader(header, blockchain.GetHeader(header.ParentHash), false); err != nil {
return err
}
// Manually insert the header into the database, but don't reorganize (allows subsequent testing)
- processor.bc.mu.Lock()
- WriteTd(processor.chainDb, header.Hash(), new(big.Int).Add(header.Difficulty, processor.bc.GetTd(header.ParentHash)))
- WriteHeader(processor.chainDb, header)
- processor.bc.mu.Unlock()
+ blockchain.mu.Lock()
+ WriteTd(blockchain.chainDb, header.Hash(), new(big.Int).Add(header.Difficulty, blockchain.GetTd(header.ParentHash)))
+ WriteHeader(blockchain.chainDb, header)
+ blockchain.mu.Unlock()
}
return nil
}
@@ -313,19 +327,19 @@ func TestBrokenBlockChain(t *testing.T) { testBrokenChain(t, true) }
func testBrokenChain(t *testing.T, full bool) {
// Make chain starting from genesis
- db, processor, err := newCanonical(10, full)
+ db, blockchain, err := newCanonical(10, full)
if err != nil {
t.Fatalf("failed to make new canonical chain: %v", err)
}
// Create a forked chain, and try to insert with a missing link
if full {
- chain := makeBlockChain(processor.bc.CurrentBlock(), 5, db, forkSeed)[1:]
- if err := testBlockChainImport(chain, processor); err == nil {
+ chain := makeBlockChain(blockchain.CurrentBlock(), 5, db, forkSeed)[1:]
+ if err := testBlockChainImport(chain, blockchain); err == nil {
t.Errorf("broken block chain not reported")
}
} else {
- chain := makeHeaderChain(processor.bc.CurrentHeader(), 5, db, forkSeed)[1:]
- if err := testHeaderChainImport(chain, processor); err == nil {
+ chain := makeHeaderChain(blockchain.CurrentHeader(), 5, db, forkSeed)[1:]
+ if err := testHeaderChainImport(chain, blockchain); err == nil {
t.Errorf("broken header chain not reported")
}
}
@@ -415,9 +429,14 @@ func TestChainMultipleInsertions(t *testing.T) {
type bproc struct{}
-func (bproc) Process(*types.Block) (vm.Logs, types.Receipts, error) { return nil, nil, nil }
-func (bproc) ValidateHeader(*types.Header, bool, bool) error { return nil }
-func (bproc) ValidateHeaderWithParent(*types.Header, *types.Header, bool, bool) error { return nil }
+func (bproc) ValidateBlock(*types.Block) error { return nil }
+func (bproc) ValidateHeader(*types.Header, *types.Header, bool) error { return nil }
+func (bproc) ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error {
+ return nil
+}
+func (bproc) Process(block *types.Block, statedb *state.StateDB) (types.Receipts, vm.Logs, *big.Int, error) {
+ return nil, nil, nil, nil
+}
func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Header {
blocks := makeBlockChainWithDiff(genesis, d, seed)
@@ -459,7 +478,8 @@ func chm(genesis *types.Block, db ethdb.Database) *BlockChain {
bc.tdCache, _ = lru.New(100)
bc.blockCache, _ = lru.New(100)
bc.futureBlocks, _ = lru.New(100)
- bc.processor = bproc{}
+ bc.SetValidator(bproc{})
+ bc.SetProcessor(bproc{})
bc.ResetWithGenesisBlock(genesis)
return bc
@@ -612,12 +632,10 @@ func TestBlocksInsertNonceError(t *testing.T) { testInsertNonceError(t, true) }
func testInsertNonceError(t *testing.T, full bool) {
for i := 1; i < 25 && !t.Failed(); i++ {
// Create a pristine chain and database
- db, processor, err := newCanonical(0, full)
+ db, blockchain, err := newCanonical(0, full)
if err != nil {
t.Fatalf("failed to create pristine chain: %v", err)
}
- bc := processor.bc
-
// Create and insert a chain with a failing nonce
var (
failAt int
@@ -626,34 +644,33 @@ func testInsertNonceError(t *testing.T, full bool) {
failHash common.Hash
)
if full {
- blocks := makeBlockChain(processor.bc.CurrentBlock(), i, db, 0)
+ blocks := makeBlockChain(blockchain.CurrentBlock(), i, db, 0)
failAt = rand.Int() % len(blocks)
failNum = blocks[failAt].NumberU64()
failHash = blocks[failAt].Hash()
- processor.bc.pow = failPow{failNum}
- processor.Pow = failPow{failNum}
+ blockchain.pow = failPow{failNum}
- failRes, err = processor.bc.InsertChain(blocks)
+ failRes, err = blockchain.InsertChain(blocks)
} else {
- headers := makeHeaderChain(processor.bc.CurrentHeader(), i, db, 0)
+ headers := makeHeaderChain(blockchain.CurrentHeader(), i, db, 0)
failAt = rand.Int() % len(headers)
failNum = headers[failAt].Number.Uint64()
failHash = headers[failAt].Hash()
- processor.bc.pow = failPow{failNum}
- processor.Pow = failPow{failNum}
+ blockchain.pow = failPow{failNum}
+ blockchain.validator = NewBlockValidator(blockchain, failPow{failNum})
- failRes, err = processor.bc.InsertHeaderChain(headers, 1)
+ failRes, err = blockchain.InsertHeaderChain(headers, 1)
}
// Check that the returned error indicates the nonce failure.
if failRes != failAt {
t.Errorf("test %d: failure index mismatch: have %d, want %d", i, failRes, failAt)
}
if !IsBlockNonceErr(err) {
- t.Fatalf("test %d: error mismatch: have %v, want nonce error", i, err)
+ t.Fatalf("test %d: error mismatch: have %v, want nonce error %T", i, err, err)
}
nerr := err.(*BlockNonceErr)
if nerr.Number.Uint64() != failNum {
@@ -665,11 +682,11 @@ func testInsertNonceError(t *testing.T, full bool) {
// Check that all no blocks after the failing block have been inserted.
for j := 0; j < i-failAt; j++ {
if full {
- if block := bc.GetBlockByNumber(failNum + uint64(j)); block != nil {
+ if block := blockchain.GetBlockByNumber(failNum + uint64(j)); block != nil {
t.Errorf("test %d: invalid block in chain: %v", i, block)
}
} else {
- if header := bc.GetHeaderByNumber(failNum + uint64(j)); header != nil {
+ if header := blockchain.GetHeaderByNumber(failNum + uint64(j)); header != nil {
t.Errorf("test %d: invalid header in chain: %v", i, header)
}
}
@@ -711,7 +728,6 @@ func TestFastVsFullChains(t *testing.T) {
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux))
- archive.SetProcessor(NewBlockProcessor(archiveDb, FakePow{}, archive, new(event.TypeMux)))
if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err)
@@ -720,7 +736,6 @@ func TestFastVsFullChains(t *testing.T) {
fastDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux))
- fast.SetProcessor(NewBlockProcessor(fastDb, FakePow{}, fast, new(event.TypeMux)))
headers := make([]*types.Header, len(blocks))
for i, block := range blocks {
@@ -797,7 +812,6 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
archive, _ := NewBlockChain(archiveDb, FakePow{}, new(event.TypeMux))
- archive.SetProcessor(NewBlockProcessor(archiveDb, FakePow{}, archive, new(event.TypeMux)))
if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err)
@@ -810,7 +824,6 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
fastDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
fast, _ := NewBlockChain(fastDb, FakePow{}, new(event.TypeMux))
- fast.SetProcessor(NewBlockProcessor(fastDb, FakePow{}, fast, new(event.TypeMux)))
headers := make([]*types.Header, len(blocks))
for i, block := range blocks {
@@ -830,7 +843,6 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
lightDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds})
light, _ := NewBlockChain(lightDb, FakePow{}, new(event.TypeMux))
- light.SetProcessor(NewBlockProcessor(lightDb, FakePow{}, light, new(event.TypeMux)))
if n, err := light.InsertHeaderChain(headers, 1); err != nil {
t.Fatalf("failed to insert header %d: %v", n, err)
@@ -895,9 +907,8 @@ func TestChainTxReorgs(t *testing.T) {
})
// Import the chain. This runs all block validation rules.
evmux := &event.TypeMux{}
- chainman, _ := NewBlockChain(db, FakePow{}, evmux)
- chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux))
- if i, err := chainman.InsertChain(chain); err != nil {
+ blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
+ if i, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
}
@@ -920,14 +931,14 @@ func TestChainTxReorgs(t *testing.T) {
gen.AddTx(futureAdd) // This transaction will be added after a full reorg
}
})
- if _, err := chainman.InsertChain(chain); err != nil {
+ if _, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert forked chain: %v", err)
}
// removed tx
for i, tx := range (types.Transactions{pastDrop, freshDrop}) {
- if GetTransaction(db, tx.Hash()) != nil {
- t.Errorf("drop %d: tx found while shouldn't have been", i)
+ if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil {
+ t.Errorf("drop %d: tx %v found while shouldn't have been", i, txn)
}
if GetReceipt(db, tx.Hash()) != nil {
t.Errorf("drop %d: receipt found while shouldn't have been", i)
@@ -935,7 +946,7 @@ func TestChainTxReorgs(t *testing.T) {
}
// added tx
for i, tx := range (types.Transactions{pastAdd, freshAdd, futureAdd}) {
- if GetTransaction(db, tx.Hash()) == nil {
+ if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn == nil {
t.Errorf("add %d: expected tx to be found", i)
}
if GetReceipt(db, tx.Hash()) == nil {
@@ -944,7 +955,7 @@ func TestChainTxReorgs(t *testing.T) {
}
// shared tx
for i, tx := range (types.Transactions{postponed, swapped}) {
- if GetTransaction(db, tx.Hash()) == nil {
+ if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn == nil {
t.Errorf("share %d: expected tx to be found", i)
}
if GetReceipt(db, tx.Hash()) == nil {
diff --git a/core/chain_makers.go b/core/chain_makers.go
index 56e37a0fc..f1ada487f 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -214,7 +214,7 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
// newCanonical creates a chain database, and injects a deterministic canonical
// chain. Depending on the full flag, if creates either a full block chain or a
// header only chain.
-func newCanonical(n int, full bool) (ethdb.Database, *BlockProcessor, error) {
+func newCanonical(n int, full bool) (ethdb.Database, *BlockChain, error) {
// Create te new chain database
db, _ := ethdb.NewMemDatabase()
evmux := &event.TypeMux{}
@@ -223,23 +223,20 @@ func newCanonical(n int, full bool) (ethdb.Database, *BlockProcessor, error) {
genesis, _ := WriteTestNetGenesisBlock(db, 0)
blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
- processor := NewBlockProcessor(db, FakePow{}, blockchain, evmux)
- processor.bc.SetProcessor(processor)
-
// Create and inject the requested chain
if n == 0 {
- return db, processor, nil
+ return db, blockchain, nil
}
if full {
// Full block-chain requested
blocks := makeBlockChain(genesis, n, db, canonicalSeed)
_, err := blockchain.InsertChain(blocks)
- return db, processor, err
+ return db, blockchain, err
}
// Header-only chain requested
headers := makeHeaderChain(genesis.Header(), n, db, canonicalSeed)
_, err := blockchain.InsertHeaderChain(headers, 1)
- return db, processor, err
+ return db, blockchain, err
}
// makeHeaderChain creates a deterministic chain of headers rooted at parent.
diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go
index 7f47cf288..b9c1d89b7 100644
--- a/core/chain_makers_test.go
+++ b/core/chain_makers_test.go
@@ -77,15 +77,14 @@ func ExampleGenerateChain() {
// Import the chain. This runs all block validation rules.
evmux := &event.TypeMux{}
- chainman, _ := NewBlockChain(db, FakePow{}, evmux)
- chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux))
- if i, err := chainman.InsertChain(chain); err != nil {
+ blockchain, _ := NewBlockChain(db, FakePow{}, evmux)
+ if i, err := blockchain.InsertChain(chain); err != nil {
fmt.Printf("insert error (block %d): %v\n", i, err)
return
}
- state, _ := chainman.State()
- fmt.Printf("last block: #%d\n", chainman.CurrentBlock().Number())
+ state, _ := blockchain.State()
+ fmt.Printf("last block: #%d\n", blockchain.CurrentBlock().Number())
fmt.Println("balance of addr1:", state.GetBalance(addr1))
fmt.Println("balance of addr2:", state.GetBalance(addr2))
fmt.Println("balance of addr3:", state.GetBalance(addr3))
diff --git a/core/chain_util.go b/core/database_util.go
index ddff381a1..fbcce3e8c 100644
--- a/core/chain_util.go
+++ b/core/database_util.go
@@ -43,11 +43,15 @@ var (
bodySuffix = []byte("-body")
tdSuffix = []byte("-td")
- ExpDiffPeriod = big.NewInt(100000)
- blockHashPre = []byte("block-hash-") // [deprecated by eth/63]
+ txMetaSuffix = []byte{0x01}
+ receiptsPrefix = []byte("receipts-")
+ blockReceiptsPrefix = []byte("receipts-block-")
mipmapPre = []byte("mipmap-log-bloom-")
MIPMapLevels = []uint64{1000000, 500000, 100000, 50000, 1000}
+
+ ExpDiffPeriod = big.NewInt(100000)
+ blockHashPrefix = []byte("block-hash-") // [deprecated by the header/block split, remove eventually]
)
// CalcDifficulty is the difficulty adjustment algorithm. It returns
@@ -234,6 +238,67 @@ func GetBlock(db ethdb.Database, hash common.Hash) *types.Block {
return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles)
}
+// GetBlockReceipts retrieves the receipts generated by the transactions included
+// in a block given by its hash.
+func GetBlockReceipts(db ethdb.Database, hash common.Hash) types.Receipts {
+ data, _ := db.Get(append(blockReceiptsPrefix, hash[:]...))
+ if len(data) == 0 {
+ return nil
+ }
+ storageReceipts := []*types.ReceiptForStorage{}
+ if err := rlp.DecodeBytes(data, &storageReceipts); err != nil {
+ glog.V(logger.Error).Infof("invalid receipt array RLP for hash %x: %v", hash, err)
+ return nil
+ }
+ receipts := make(types.Receipts, len(storageReceipts))
+ for i, receipt := range storageReceipts {
+ receipts[i] = (*types.Receipt)(receipt)
+ }
+ return receipts
+}
+
+// GetTransaction retrieves a specific transaction from the database, along with
+// its added positional metadata.
+func GetTransaction(db ethdb.Database, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) {
+ // Retrieve the transaction itself from the database
+ data, _ := db.Get(hash.Bytes())
+ if len(data) == 0 {
+ return nil, common.Hash{}, 0, 0
+ }
+ var tx types.Transaction
+ if err := rlp.DecodeBytes(data, &tx); err != nil {
+ return nil, common.Hash{}, 0, 0
+ }
+ // Retrieve the blockchain positional metadata
+ data, _ = db.Get(append(hash.Bytes(), txMetaSuffix...))
+ if len(data) == 0 {
+ return nil, common.Hash{}, 0, 0
+ }
+ var meta struct {
+ BlockHash common.Hash
+ BlockIndex uint64
+ Index uint64
+ }
+ if err := rlp.DecodeBytes(data, &meta); err != nil {
+ return nil, common.Hash{}, 0, 0
+ }
+ return &tx, meta.BlockHash, meta.BlockIndex, meta.Index
+}
+
+// GetReceipt returns a receipt by hash
+func GetReceipt(db ethdb.Database, txHash common.Hash) *types.Receipt {
+ data, _ := db.Get(append(receiptsPrefix, txHash[:]...))
+ if len(data) == 0 {
+ return nil
+ }
+ var receipt types.ReceiptForStorage
+ err := rlp.DecodeBytes(data, &receipt)
+ if err != nil {
+ glog.V(logger.Core).Infoln("GetReceipt err:", err)
+ }
+ return (*types.Receipt)(&receipt)
+}
+
// WriteCanonicalHash stores the canonical hash for the given block number.
func WriteCanonicalHash(db ethdb.Database, hash common.Hash, number uint64) error {
key := append(blockNumPrefix, big.NewInt(int64(number)).Bytes()...)
@@ -329,6 +394,94 @@ func WriteBlock(db ethdb.Database, block *types.Block) error {
return nil
}
+// WriteBlockReceipts stores all the transaction receipts belonging to a block
+// as a single receipt slice. This is used during chain reorganisations for
+// rescheduling dropped transactions.
+func WriteBlockReceipts(db ethdb.Database, hash common.Hash, receipts types.Receipts) error {
+ // Convert the receipts into their storage form and serialize them
+ storageReceipts := make([]*types.ReceiptForStorage, len(receipts))
+ for i, receipt := range receipts {
+ storageReceipts[i] = (*types.ReceiptForStorage)(receipt)
+ }
+ bytes, err := rlp.EncodeToBytes(storageReceipts)
+ if err != nil {
+ return err
+ }
+ // Store the flattened receipt slice
+ if err := db.Put(append(blockReceiptsPrefix, hash.Bytes()...), bytes); err != nil {
+ glog.Fatalf("failed to store block receipts into database: %v", err)
+ return err
+ }
+ glog.V(logger.Debug).Infof("stored block receipts [%x…]", hash.Bytes()[:4])
+ return nil
+}
+
+// WriteTransactions stores the transactions associated with a specific block
+// into the given database. Beside writing the transaction, the function also
+// stores a metadata entry along with the transaction, detailing the position
+// of this within the blockchain.
+func WriteTransactions(db ethdb.Database, block *types.Block) error {
+ batch := db.NewBatch()
+
+ // Iterate over each transaction and encode it with its metadata
+ for i, tx := range block.Transactions() {
+ // Encode and queue up the transaction for storage
+ data, err := rlp.EncodeToBytes(tx)
+ if err != nil {
+ return err
+ }
+ if err := batch.Put(tx.Hash().Bytes(), data); err != nil {
+ return err
+ }
+ // Encode and queue up the transaction metadata for storage
+ meta := struct {
+ BlockHash common.Hash
+ BlockIndex uint64
+ Index uint64
+ }{
+ BlockHash: block.Hash(),
+ BlockIndex: block.NumberU64(),
+ Index: uint64(i),
+ }
+ data, err = rlp.EncodeToBytes(meta)
+ if err != nil {
+ return err
+ }
+ if err := batch.Put(append(tx.Hash().Bytes(), txMetaSuffix...), data); err != nil {
+ return err
+ }
+ }
+ // Write the scheduled data into the database
+ if err := batch.Write(); err != nil {
+ glog.Fatalf("failed to store transactions into database: %v", err)
+ return err
+ }
+ return nil
+}
+
+// WriteReceipts stores a batch of transaction receipts into the database.
+func WriteReceipts(db ethdb.Database, receipts types.Receipts) error {
+ batch := db.NewBatch()
+
+ // Iterate over all the receipts and queue them for database injection
+ for _, receipt := range receipts {
+ storageReceipt := (*types.ReceiptForStorage)(receipt)
+ data, err := rlp.EncodeToBytes(storageReceipt)
+ if err != nil {
+ return err
+ }
+ if err := batch.Put(append(receiptsPrefix, receipt.TxHash.Bytes()...), data); err != nil {
+ return err
+ }
+ }
+ // Write the scheduled data into the database
+ if err := batch.Write(); err != nil {
+ glog.Fatalf("failed to store receipts into database: %v", err)
+ return err
+ }
+ return nil
+}
+
// DeleteCanonicalHash removes the number to hash canonical mapping.
func DeleteCanonicalHash(db ethdb.Database, number uint64) {
db.Delete(append(blockNumPrefix, big.NewInt(int64(number)).Bytes()...))
@@ -351,18 +504,35 @@ func DeleteTd(db ethdb.Database, hash common.Hash) {
// DeleteBlock removes all block data associated with a hash.
func DeleteBlock(db ethdb.Database, hash common.Hash) {
+ DeleteBlockReceipts(db, hash)
DeleteHeader(db, hash)
DeleteBody(db, hash)
DeleteTd(db, hash)
}
-// [deprecated by eth/63]
+// DeleteBlockReceipts removes all receipt data associated with a block hash.
+func DeleteBlockReceipts(db ethdb.Database, hash common.Hash) {
+ db.Delete(append(blockReceiptsPrefix, hash.Bytes()...))
+}
+
+// DeleteTransaction removes all transaction data associated with a hash.
+func DeleteTransaction(db ethdb.Database, hash common.Hash) {
+ db.Delete(hash.Bytes())
+ db.Delete(append(hash.Bytes(), txMetaSuffix...))
+}
+
+// DeleteReceipt removes all receipt data associated with a transaction hash.
+func DeleteReceipt(db ethdb.Database, hash common.Hash) {
+ db.Delete(append(receiptsPrefix, hash.Bytes()...))
+}
+
+// [deprecated by the header/block split, remove eventually]
// GetBlockByHashOld returns the old combined block corresponding to the hash
// or nil if not found. This method is only used by the upgrade mechanism to
// access the old combined block representation. It will be dropped after the
// network transitions to eth/63.
func GetBlockByHashOld(db ethdb.Database, hash common.Hash) *types.Block {
- data, _ := db.Get(append(blockHashPre, hash[:]...))
+ data, _ := db.Get(append(blockHashPrefix, hash[:]...))
if len(data) == 0 {
return nil
}
diff --git a/core/chain_util_test.go b/core/database_util_test.go
index 0bbcbbe53..059f1ae9f 100644
--- a/core/chain_util_test.go
+++ b/core/database_util_test.go
@@ -17,6 +17,7 @@
package core
import (
+ "bytes"
"encoding/json"
"io/ioutil"
"math/big"
@@ -341,6 +342,163 @@ func TestHeadStorage(t *testing.T) {
}
}
+// Tests that transactions and associated metadata can be stored and retrieved.
+func TestTransactionStorage(t *testing.T) {
+ db, _ := ethdb.NewMemDatabase()
+
+ tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), big.NewInt(1111), big.NewInt(11111), []byte{0x11, 0x11, 0x11})
+ tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), big.NewInt(2222), big.NewInt(22222), []byte{0x22, 0x22, 0x22})
+ tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), big.NewInt(3333), big.NewInt(33333), []byte{0x33, 0x33, 0x33})
+ txs := []*types.Transaction{tx1, tx2, tx3}
+
+ block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil)
+
+ // Check that no transactions entries are in a pristine database
+ for i, tx := range txs {
+ if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil {
+ t.Fatalf("tx #%d [%x]: non existent transaction returned: %v", i, tx.Hash(), txn)
+ }
+ }
+ // Insert all the transactions into the database, and verify contents
+ if err := WriteTransactions(db, block); err != nil {
+ t.Fatalf("failed to write transactions: %v", err)
+ }
+ for i, tx := range txs {
+ if txn, hash, number, index := GetTransaction(db, tx.Hash()); txn == nil {
+ t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash())
+ } else {
+ if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) {
+ t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i)
+ }
+ if tx.String() != txn.String() {
+ t.Fatalf("tx #%d [%x]: transaction mismatch: have %v, want %v", i, tx.Hash(), txn, tx)
+ }
+ }
+ }
+ // Delete the transactions and check purge
+ for i, tx := range txs {
+ DeleteTransaction(db, tx.Hash())
+ if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil {
+ t.Fatalf("tx #%d [%x]: deleted transaction returned: %v", i, tx.Hash(), txn)
+ }
+ }
+}
+
+// Tests that receipts can be stored and retrieved.
+func TestReceiptStorage(t *testing.T) {
+ db, _ := ethdb.NewMemDatabase()
+
+ receipt1 := &types.Receipt{
+ PostState: []byte{0x01},
+ CumulativeGasUsed: big.NewInt(1),
+ Logs: vm.Logs{
+ &vm.Log{Address: common.BytesToAddress([]byte{0x11})},
+ &vm.Log{Address: common.BytesToAddress([]byte{0x01, 0x11})},
+ },
+ TxHash: common.BytesToHash([]byte{0x11, 0x11}),
+ ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
+ GasUsed: big.NewInt(111111),
+ }
+ receipt2 := &types.Receipt{
+ PostState: []byte{0x02},
+ CumulativeGasUsed: big.NewInt(2),
+ Logs: vm.Logs{
+ &vm.Log{Address: common.BytesToAddress([]byte{0x22})},
+ &vm.Log{Address: common.BytesToAddress([]byte{0x02, 0x22})},
+ },
+ TxHash: common.BytesToHash([]byte{0x22, 0x22}),
+ ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
+ GasUsed: big.NewInt(222222),
+ }
+ receipts := []*types.Receipt{receipt1, receipt2}
+
+ // Check that no receipt entries are in a pristine database
+ for i, receipt := range receipts {
+ if r := GetReceipt(db, receipt.TxHash); r != nil {
+ t.Fatalf("receipt #%d [%x]: non existent receipt returned: %v", i, receipt.TxHash, r)
+ }
+ }
+ // Insert all the receipts into the database, and verify contents
+ if err := WriteReceipts(db, receipts); err != nil {
+ t.Fatalf("failed to write receipts: %v", err)
+ }
+ for i, receipt := range receipts {
+ if r := GetReceipt(db, receipt.TxHash); r == nil {
+ t.Fatalf("receipt #%d [%x]: receipt not found", i, receipt.TxHash)
+ } else {
+ rlpHave, _ := rlp.EncodeToBytes(r)
+ rlpWant, _ := rlp.EncodeToBytes(receipt)
+
+ if bytes.Compare(rlpHave, rlpWant) != 0 {
+ t.Fatalf("receipt #%d [%x]: receipt mismatch: have %v, want %v", i, receipt.TxHash, r, receipt)
+ }
+ }
+ }
+ // Delete the receipts and check purge
+ for i, receipt := range receipts {
+ DeleteReceipt(db, receipt.TxHash)
+ if r := GetReceipt(db, receipt.TxHash); r != nil {
+ t.Fatalf("receipt #%d [%x]: deleted receipt returned: %v", i, receipt.TxHash, r)
+ }
+ }
+}
+
+// Tests that receipts associated with a single block can be stored and retrieved.
+func TestBlockReceiptStorage(t *testing.T) {
+ db, _ := ethdb.NewMemDatabase()
+
+ receipt1 := &types.Receipt{
+ PostState: []byte{0x01},
+ CumulativeGasUsed: big.NewInt(1),
+ Logs: vm.Logs{
+ &vm.Log{Address: common.BytesToAddress([]byte{0x11})},
+ &vm.Log{Address: common.BytesToAddress([]byte{0x01, 0x11})},
+ },
+ TxHash: common.BytesToHash([]byte{0x11, 0x11}),
+ ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
+ GasUsed: big.NewInt(111111),
+ }
+ receipt2 := &types.Receipt{
+ PostState: []byte{0x02},
+ CumulativeGasUsed: big.NewInt(2),
+ Logs: vm.Logs{
+ &vm.Log{Address: common.BytesToAddress([]byte{0x22})},
+ &vm.Log{Address: common.BytesToAddress([]byte{0x02, 0x22})},
+ },
+ TxHash: common.BytesToHash([]byte{0x22, 0x22}),
+ ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
+ GasUsed: big.NewInt(222222),
+ }
+ receipts := []*types.Receipt{receipt1, receipt2}
+
+ // Check that no receipt entries are in a pristine database
+ hash := common.BytesToHash([]byte{0x03, 0x14})
+ if rs := GetBlockReceipts(db, hash); len(rs) != 0 {
+ t.Fatalf("non existent receipts returned: %v", rs)
+ }
+ // Insert the receipt slice into the database and check presence
+ if err := WriteBlockReceipts(db, hash, receipts); err != nil {
+ t.Fatalf("failed to write block receipts: %v", err)
+ }
+ if rs := GetBlockReceipts(db, hash); len(rs) == 0 {
+ t.Fatalf("no receipts returned")
+ } else {
+ for i := 0; i < len(receipts); i++ {
+ rlpHave, _ := rlp.EncodeToBytes(rs[i])
+ rlpWant, _ := rlp.EncodeToBytes(receipts[i])
+
+ if bytes.Compare(rlpHave, rlpWant) != 0 {
+ t.Fatalf("receipt #%d: receipt mismatch: have %v, want %v", i, rs[i], receipts[i])
+ }
+ }
+ }
+ // Delete the receipt slice and check purge
+ DeleteBlockReceipts(db, hash)
+ if rs := GetBlockReceipts(db, hash); len(rs) != 0 {
+ t.Fatalf("deleted receipts returned: %v", rs)
+ }
+}
+
func TestMipmapBloom(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
@@ -425,7 +583,7 @@ func TestMipmapChain(t *testing.T) {
}
// store the receipts
- err := PutReceipts(db, receipts)
+ err := WriteReceipts(db, receipts)
if err != nil {
t.Fatal(err)
}
@@ -439,7 +597,7 @@ func TestMipmapChain(t *testing.T) {
if err := WriteHeadBlockHash(db, block.Hash()); err != nil {
t.Fatalf("failed to insert block number: %v", err)
}
- if err := PutBlockReceipts(db, block.Hash(), receipts[i]); err != nil {
+ if err := WriteBlockReceipts(db, block.Hash(), receipts[i]); err != nil {
t.Fatal("error writing block receipts:", err)
}
}
diff --git a/core/gaspool.go b/core/gaspool.go
new file mode 100644
index 000000000..2ef07c754
--- /dev/null
+++ b/core/gaspool.go
@@ -0,0 +1,46 @@
+// Copyright 2014 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 core
+
+import "math/big"
+
+// GasPool tracks the amount of gas available during
+// execution of the transactions in a block.
+// The zero value is a pool with zero gas available.
+type GasPool big.Int
+
+// AddGas makes gas available for execution.
+func (gp *GasPool) AddGas(amount *big.Int) *GasPool {
+ i := (*big.Int)(gp)
+ i.Add(i, amount)
+ return gp
+}
+
+// SubGas deducts the given amount from the pool if enough gas is
+// available and returns an error otherwise.
+func (gp *GasPool) SubGas(amount *big.Int) error {
+ i := (*big.Int)(gp)
+ if i.Cmp(amount) < 0 {
+ return &GasLimitErr{Have: new(big.Int).Set(i), Want: amount}
+ }
+ i.Sub(i, amount)
+ return nil
+}
+
+func (gp *GasPool) String() string {
+ return (*big.Int)(gp).String()
+}
diff --git a/core/genesis.go b/core/genesis.go
index dac5de92f..3fd8f42b0 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -103,7 +103,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
if err := WriteBlock(chainDb, block); err != nil {
return nil, err
}
- if err := PutBlockReceipts(chainDb, block.Hash(), nil); err != nil {
+ if err := WriteBlockReceipts(chainDb, block.Hash(), nil); err != nil {
return nil, err
}
if err := WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()); err != nil {
diff --git a/core/state_processor.go b/core/state_processor.go
new file mode 100644
index 000000000..d9c24935d
--- /dev/null
+++ b/core/state_processor.go
@@ -0,0 +1,107 @@
+package core
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/logger"
+ "github.com/ethereum/go-ethereum/logger/glog"
+)
+
+var (
+ big8 = big.NewInt(8)
+ big32 = big.NewInt(32)
+)
+
+type StateProcessor struct {
+ bc *BlockChain
+}
+
+func NewStateProcessor(bc *BlockChain) *StateProcessor {
+ return &StateProcessor{bc}
+}
+
+// Process processes the state changes according to the Ethereum rules by running
+// the transaction messages using the statedb and applying any rewards to both
+// the processor (coinbase) and any included uncles.
+//
+// Process returns the receipts and logs accumulated during the process and
+// returns the amount of gas that was used in the process. If any of the
+// transactions failed to execute due to insufficient gas it will return an error.
+func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB) (types.Receipts, vm.Logs, *big.Int, error) {
+ var (
+ receipts types.Receipts
+ totalUsedGas = big.NewInt(0)
+ err error
+ header = block.Header()
+ allLogs vm.Logs
+ gp = new(GasPool).AddGas(block.GasLimit())
+ )
+
+ for i, tx := range block.Transactions() {
+ statedb.StartRecord(tx.Hash(), block.Hash(), i)
+
+ receipt, logs, _, err := ApplyTransaction(p.bc, gp, statedb, header, tx, totalUsedGas)
+ if err != nil {
+ return nil, nil, totalUsedGas, err
+ }
+ receipts = append(receipts, receipt)
+ allLogs = append(allLogs, logs...)
+ }
+ AccumulateRewards(statedb, header, block.Uncles())
+
+ return receipts, allLogs, totalUsedGas, err
+}
+
+// ApplyTransaction attemps to apply a transaction to the given state database
+// and uses the input parameters for its environment.
+//
+// ApplyTransactions returns the generated receipts and vm logs during the
+// execution of the state transition phase.
+func ApplyTransaction(bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int) (*types.Receipt, vm.Logs, *big.Int, error) {
+ _, gas, err := ApplyMessage(NewEnv(statedb, bc, tx, header), tx, gp)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ // Update the state with pending changes
+ usedGas.Add(usedGas, gas)
+ receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas)
+ receipt.TxHash = tx.Hash()
+ receipt.GasUsed = new(big.Int).Set(gas)
+ if MessageCreatesContract(tx) {
+ from, _ := tx.From()
+ receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce())
+ }
+
+ logs := statedb.GetLogs(tx.Hash())
+ receipt.Logs = logs
+ receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
+
+ glog.V(logger.Debug).Infoln(receipt)
+
+ return receipt, logs, gas, err
+}
+
+// AccumulateRewards credits the coinbase of the given block with the
+// mining reward. The total reward consists of the static block reward
+// and rewards for included uncles. The coinbase of each uncle block is
+// also rewarded.
+func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*types.Header) {
+ reward := new(big.Int).Set(BlockReward)
+ r := new(big.Int)
+ for _, uncle := range uncles {
+ r.Add(uncle.Number, big8)
+ r.Sub(r, header.Number)
+ r.Mul(r, BlockReward)
+ r.Div(r, big8)
+ statedb.AddBalance(uncle.Coinbase, r)
+
+ r.Div(BlockReward, big32)
+ reward.Add(reward, r)
+ }
+ statedb.AddBalance(header.Coinbase, reward)
+}
diff --git a/core/transaction_util.go b/core/transaction_util.go
deleted file mode 100644
index e2e5b9aee..000000000
--- a/core/transaction_util.go
+++ /dev/null
@@ -1,171 +0,0 @@
-// Copyright 2015 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 core
-
-import (
- "fmt"
-
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/logger/glog"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/syndtr/goleveldb/leveldb"
-)
-
-var (
- receiptsPre = []byte("receipts-")
- blockReceiptsPre = []byte("receipts-block-")
-)
-
-// PutTransactions stores the transactions in the given database
-func PutTransactions(db ethdb.Database, block *types.Block, txs types.Transactions) error {
- batch := db.NewBatch()
-
- for i, tx := range block.Transactions() {
- rlpEnc, err := rlp.EncodeToBytes(tx)
- if err != nil {
- return fmt.Errorf("failed encoding tx: %v", err)
- }
-
- batch.Put(tx.Hash().Bytes(), rlpEnc)
-
- var txExtra struct {
- BlockHash common.Hash
- BlockIndex uint64
- Index uint64
- }
- txExtra.BlockHash = block.Hash()
- txExtra.BlockIndex = block.NumberU64()
- txExtra.Index = uint64(i)
- rlpMeta, err := rlp.EncodeToBytes(txExtra)
- if err != nil {
- return fmt.Errorf("failed encoding tx meta data: %v", err)
- }
-
- batch.Put(append(tx.Hash().Bytes(), 0x0001), rlpMeta)
- }
-
- if err := batch.Write(); err != nil {
- return fmt.Errorf("failed writing tx to db: %v", err)
- }
- return nil
-}
-
-func DeleteTransaction(db ethdb.Database, txHash common.Hash) {
- db.Delete(txHash[:])
-}
-
-func GetTransaction(db ethdb.Database, txhash common.Hash) *types.Transaction {
- data, _ := db.Get(txhash[:])
- if len(data) != 0 {
- var tx types.Transaction
- if err := rlp.DecodeBytes(data, &tx); err != nil {
- return nil
- }
- return &tx
- }
- return nil
-}
-
-// PutReceipts stores the receipts in the current database
-func PutReceipts(db ethdb.Database, receipts types.Receipts) error {
- batch := new(leveldb.Batch)
- _, batchWrite := db.(*ethdb.LDBDatabase)
-
- for _, receipt := range receipts {
- storageReceipt := (*types.ReceiptForStorage)(receipt)
- bytes, err := rlp.EncodeToBytes(storageReceipt)
- if err != nil {
- return err
- }
-
- if batchWrite {
- batch.Put(append(receiptsPre, receipt.TxHash[:]...), bytes)
- } else {
- err = db.Put(append(receiptsPre, receipt.TxHash[:]...), bytes)
- if err != nil {
- return err
- }
- }
- }
- if db, ok := db.(*ethdb.LDBDatabase); ok {
- if err := db.LDB().Write(batch, nil); err != nil {
- return err
- }
- }
-
- return nil
-}
-
-// Delete a receipts from the database
-func DeleteReceipt(db ethdb.Database, txHash common.Hash) {
- db.Delete(append(receiptsPre, txHash[:]...))
-}
-
-// GetReceipt returns a receipt by hash
-func GetReceipt(db ethdb.Database, txHash common.Hash) *types.Receipt {
- data, _ := db.Get(append(receiptsPre, txHash[:]...))
- if len(data) == 0 {
- return nil
- }
- var receipt types.ReceiptForStorage
- err := rlp.DecodeBytes(data, &receipt)
- if err != nil {
- glog.V(logger.Core).Infoln("GetReceipt err:", err)
- }
- return (*types.Receipt)(&receipt)
-}
-
-// GetBlockReceipts returns the receipts generated by the transactions
-// included in block's given hash.
-func GetBlockReceipts(db ethdb.Database, hash common.Hash) types.Receipts {
- data, _ := db.Get(append(blockReceiptsPre, hash[:]...))
- if len(data) == 0 {
- return nil
- }
- rs := []*types.ReceiptForStorage{}
- if err := rlp.DecodeBytes(data, &rs); err != nil {
- glog.V(logger.Error).Infof("invalid receipt array RLP for hash %x: %v", hash, err)
- return nil
- }
- receipts := make(types.Receipts, len(rs))
- for i, receipt := range rs {
- receipts[i] = (*types.Receipt)(receipt)
- }
- return receipts
-}
-
-// PutBlockReceipts stores the block's transactions associated receipts
-// and stores them by block hash in a single slice. This is required for
-// forks and chain reorgs
-func PutBlockReceipts(db ethdb.Database, hash common.Hash, receipts types.Receipts) error {
- rs := make([]*types.ReceiptForStorage, len(receipts))
- for i, receipt := range receipts {
- rs[i] = (*types.ReceiptForStorage)(receipt)
- }
- bytes, err := rlp.EncodeToBytes(rs)
- if err != nil {
- return err
- }
- err = db.Put(append(blockReceiptsPre, hash[:]...), bytes)
- if err != nil {
- return err
- }
- return nil
-}
diff --git a/core/types.go b/core/types.go
new file mode 100644
index 000000000..027f628b1
--- /dev/null
+++ b/core/types.go
@@ -0,0 +1,70 @@
+// Copyright 2014 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 core
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/event"
+)
+
+// Validator is an interface which defines the standard for block validation.
+//
+// The validator is responsible for validating incoming block or, if desired,
+// validates headers for fast validation.
+//
+// ValidateBlock validates the given block and should return an error if it
+// failed to do so and should be used for "full" validation.
+//
+// ValidateHeader validates the given header and parent and returns an error
+// if it failed to do so.
+//
+// ValidateStack validates the given statedb and optionally the receipts and
+// gas used. The implementor should decide what to do with the given input.
+type Validator interface {
+ ValidateBlock(block *types.Block) error
+ ValidateHeader(header, parent *types.Header, checkPow bool) error
+ ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) error
+}
+
+// Processor is an interface for processing blocks using a given initial state.
+//
+// Process takes the block to be processed and the statedb upon which the
+// initial state is based. It should return the receipts generated, amount
+// of gas used in the process and return an error if any of the internal rules
+// failed.
+type Processor interface {
+ Process(block *types.Block, statedb *state.StateDB) (types.Receipts, vm.Logs, *big.Int, error)
+}
+
+// Backend is an interface defining the basic functionality for an operable node
+// with all the functionality to be a functional, valid Ethereum operator.
+//
+// TODO Remove this
+type Backend interface {
+ AccountManager() *accounts.Manager
+ BlockChain() *BlockChain
+ TxPool() *TxPool
+ ChainDb() ethdb.Database
+ DappDb() ethdb.Database
+ EventMux() *event.TypeMux
+}
diff --git a/core/types/common.go b/core/vm/runtime/doc.go
index fe682f98a..a3b464a7d 100644
--- a/core/types/common.go
+++ b/core/vm/runtime/doc.go
@@ -14,12 +14,5 @@
// 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 "github.com/ethereum/go-ethereum/core/vm"
-
-type BlockProcessor interface {
- Process(*Block) (vm.Logs, Receipts, error)
- ValidateHeader(*Header, bool, bool) error
- ValidateHeaderWithParent(*Header, *Header, bool, bool) error
-}
+// Package runtime provides a basic execution model for executing EVM code.
+package runtime
diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go
new file mode 100644
index 000000000..22f9ea14d
--- /dev/null
+++ b/core/vm/runtime/env.go
@@ -0,0 +1,106 @@
+// Copyright 2014 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 runtime
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/vm"
+)
+
+// Env is a basic runtime environment required for running the EVM.
+type Env struct {
+ depth int
+ state *state.StateDB
+
+ origin common.Address
+ coinbase common.Address
+
+ number *big.Int
+ time *big.Int
+ difficulty *big.Int
+ gasLimit *big.Int
+
+ logs []vm.StructLog
+
+ getHashFn func(uint64) common.Hash
+}
+
+// NewEnv returns a new vm.Environment
+func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
+ return &Env{
+ state: state,
+ origin: cfg.Origin,
+ coinbase: cfg.Coinbase,
+ number: cfg.BlockNumber,
+ time: cfg.Time,
+ difficulty: cfg.Difficulty,
+ gasLimit: cfg.GasLimit,
+ }
+}
+
+func (self *Env) StructLogs() []vm.StructLog {
+ return self.logs
+}
+
+func (self *Env) AddStructLog(log vm.StructLog) {
+ self.logs = append(self.logs, log)
+}
+
+func (self *Env) Origin() common.Address { return self.origin }
+func (self *Env) BlockNumber() *big.Int { return self.number }
+func (self *Env) Coinbase() common.Address { return self.coinbase }
+func (self *Env) Time() *big.Int { return self.time }
+func (self *Env) Difficulty() *big.Int { return self.difficulty }
+func (self *Env) Db() vm.Database { return self.state }
+func (self *Env) GasLimit() *big.Int { return self.gasLimit }
+func (self *Env) VmType() vm.Type { return vm.StdVmTy }
+func (self *Env) GetHash(n uint64) common.Hash {
+ return self.getHashFn(n)
+}
+func (self *Env) AddLog(log *vm.Log) {
+ self.state.AddLog(log)
+}
+func (self *Env) Depth() int { return self.depth }
+func (self *Env) SetDepth(i int) { self.depth = i }
+func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
+ return self.state.GetBalance(from).Cmp(balance) >= 0
+}
+func (self *Env) MakeSnapshot() vm.Database {
+ return self.state.Copy()
+}
+func (self *Env) SetSnapshot(copy vm.Database) {
+ self.state.Set(copy.(*state.StateDB))
+}
+
+func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {
+ core.Transfer(from, to, amount)
+}
+
+func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
+ return core.Call(self, caller, addr, data, gas, price, value)
+}
+func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
+ return core.CallCode(self, caller, addr, data, gas, price, value)
+}
+
+func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
+ return core.Create(self, caller, data, gas, price, value)
+}
diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go
new file mode 100644
index 000000000..dd3aa1b0b
--- /dev/null
+++ b/core/vm/runtime/runtime.go
@@ -0,0 +1,121 @@
+// Copyright 2014 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 runtime
+
+import (
+ "math/big"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethdb"
+)
+
+// Config is a basic type specifing certain configuration flags for running
+// the EVM.
+type Config struct {
+ Difficulty *big.Int
+ Origin common.Address
+ Coinbase common.Address
+ BlockNumber *big.Int
+ Time *big.Int
+ GasLimit *big.Int
+ GasPrice *big.Int
+ Value *big.Int
+ DisableJit bool // "disable" so it's enabled by default
+ Debug bool
+
+ GetHashFn func(n uint64) common.Hash
+}
+
+// sets defaults on the config
+func setDefaults(cfg *Config) {
+ if cfg.Difficulty == nil {
+ cfg.Difficulty = new(big.Int)
+ }
+ if cfg.Time == nil {
+ cfg.Time = big.NewInt(time.Now().Unix())
+ }
+ if cfg.GasLimit == nil {
+ cfg.GasLimit = new(big.Int).Set(common.MaxBig)
+ }
+ if cfg.GasPrice == nil {
+ cfg.GasPrice = new(big.Int)
+ }
+ if cfg.Value == nil {
+ cfg.Value = new(big.Int)
+ }
+ if cfg.BlockNumber == nil {
+ cfg.BlockNumber = new(big.Int)
+ }
+ if cfg.GetHashFn == nil {
+ cfg.GetHashFn = func(n uint64) common.Hash {
+ return common.BytesToHash(crypto.Sha3([]byte(new(big.Int).SetUint64(n).String())))
+ }
+ }
+}
+
+// Execute executes the code using the input as call data during the execution.
+// It returns the EVM's return value, the new state and an error if it failed.
+//
+// Executes sets up a in memory, temporarily, environment for the execution of
+// the given code. It enabled the JIT by default and make sure that it's restored
+// to it's original state afterwards.
+func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
+ if cfg == nil {
+ cfg = new(Config)
+ }
+ setDefaults(cfg)
+
+ // defer the call to setting back the original values
+ defer func(debug, forceJit, enableJit bool) {
+ vm.Debug = debug
+ vm.ForceJit = forceJit
+ vm.EnableJit = enableJit
+ }(vm.Debug, vm.ForceJit, vm.EnableJit)
+
+ vm.ForceJit = !cfg.DisableJit
+ vm.EnableJit = !cfg.DisableJit
+ vm.Debug = cfg.Debug
+
+ var (
+ db, _ = ethdb.NewMemDatabase()
+ statedb, _ = state.New(common.Hash{}, db)
+ vmenv = NewEnv(cfg, statedb)
+ sender = statedb.CreateAccount(cfg.Origin)
+ receiver = statedb.CreateAccount(common.StringToAddress("contract"))
+ )
+ // set the receiver's (the executing contract) code for execution.
+ receiver.SetCode(code)
+
+ // Call the code with the given configuration.
+ ret, err := vmenv.Call(
+ sender,
+ receiver.Address(),
+ input,
+ cfg.GasLimit,
+ cfg.GasPrice,
+ cfg.Value,
+ )
+
+ if cfg.Debug {
+ vm.StdErrFormat(vmenv.StructLogs())
+ }
+ return ret, statedb, err
+}
diff --git a/core/manager.go b/core/vm/runtime/runtime_example_test.go
index 289c87c11..b7d0ddc38 100644
--- a/core/manager.go
+++ b/core/vm/runtime/runtime_example_test.go
@@ -14,21 +14,21 @@
// 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 core
+package runtime_test
import (
- "github.com/ethereum/go-ethereum/accounts"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/event"
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/vm/runtime"
)
-// TODO move this to types?
-type Backend interface {
- AccountManager() *accounts.Manager
- BlockProcessor() *BlockProcessor
- BlockChain() *BlockChain
- TxPool() *TxPool
- ChainDb() ethdb.Database
- DappDb() ethdb.Database
- EventMux() *event.TypeMux
+func ExampleExecute() {
+ ret, _, err := runtime.Execute(common.Hex2Bytes("6060604052600a8060106000396000f360606040526008565b00"), nil, nil)
+ if err != nil {
+ fmt.Println(err)
+ }
+ fmt.Println(ret)
+ // Output:
+ // [96 96 96 64 82 96 8 86 91 0]
}
diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go
new file mode 100644
index 000000000..773a0163e
--- /dev/null
+++ b/core/vm/runtime/runtime_test.go
@@ -0,0 +1,120 @@
+// Copyright 2015 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 runtime
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/vm"
+)
+
+func TestDefaults(t *testing.T) {
+ cfg := new(Config)
+ setDefaults(cfg)
+
+ if cfg.Difficulty == nil {
+ t.Error("expected difficulty to be non nil")
+ }
+
+ if cfg.Time == nil {
+ t.Error("expected time to be non nil")
+ }
+ if cfg.GasLimit == nil {
+ t.Error("expected time to be non nil")
+ }
+ if cfg.GasPrice == nil {
+ t.Error("expected time to be non nil")
+ }
+ if cfg.Value == nil {
+ t.Error("expected time to be non nil")
+ }
+ if cfg.GetHashFn == nil {
+ t.Error("expected time to be non nil")
+ }
+ if cfg.BlockNumber == nil {
+ t.Error("expected block number to be non nil")
+ }
+}
+
+func TestEnvironment(t *testing.T) {
+ defer func() {
+ if r := recover(); r != nil {
+ t.Fatalf("crashed with: %v", r)
+ }
+ }()
+
+ Execute([]byte{
+ byte(vm.DIFFICULTY),
+ byte(vm.TIMESTAMP),
+ byte(vm.GASLIMIT),
+ byte(vm.PUSH1),
+ byte(vm.ORIGIN),
+ byte(vm.BLOCKHASH),
+ byte(vm.COINBASE),
+ }, nil, nil)
+}
+
+func TestRestoreDefaults(t *testing.T) {
+ Execute(nil, nil, &Config{Debug: true})
+ if vm.ForceJit {
+ t.Error("expected force jit to be disabled")
+ }
+
+ if vm.Debug {
+ t.Error("expected debug to be disabled")
+ }
+
+ if vm.EnableJit {
+ t.Error("expected jit to be disabled")
+ }
+}
+
+func BenchmarkCall(b *testing.B) {
+ var definition = `[{"constant":true,"inputs":[],"name":"seller","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"abort","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"refund","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"buyer","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmReceived","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"state","outputs":[{"name":"","type":"uint8"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmPurchase","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"},{"anonymous":false,"inputs":[],"name":"Aborted","type":"event"},{"anonymous":false,"inputs":[],"name":"PurchaseConfirmed","type":"event"},{"anonymous":false,"inputs":[],"name":"ItemReceived","type":"event"},{"anonymous":false,"inputs":[],"name":"Refunded","type":"event"}]`
+
+ var code = common.Hex2Bytes("6060604052361561006c5760e060020a600035046308551a53811461007457806335a063b4146100865780633fa4f245146100a6578063590e1ae3146100af5780637150d8ae146100cf57806373fac6f0146100e1578063c19d93fb146100fe578063d696069714610112575b610131610002565b610133600154600160a060020a031681565b610131600154600160a060020a0390811633919091161461015057610002565b61014660005481565b610131600154600160a060020a039081163391909116146102d557610002565b610133600254600160a060020a031681565b610131600254600160a060020a0333811691161461023757610002565b61014660025460ff60a060020a9091041681565b61013160025460009060ff60a060020a9091041681146101cc57610002565b005b600160a060020a03166060908152602090f35b6060908152602090f35b60025460009060a060020a900460ff16811461016b57610002565b600154600160a060020a03908116908290301631606082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517f72c874aeff0b183a56e2b79c71b46e1aed4dee5e09862134b8821ba2fddbf8bf9250a150565b80546002023414806101dd57610002565b6002805460a060020a60ff021973ffffffffffffffffffffffffffffffffffffffff1990911633171660a060020a1790557fd5d55c8a68912e9a110618df8d5e2e83b8d83211c57a8ddd1203df92885dc881826060a15050565b60025460019060a060020a900460ff16811461025257610002565b60025460008054600160a060020a0390921691606082818181858883f150508354604051600160a060020a0391821694503090911631915082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517fe89152acd703c9d8c7d28829d443260b411454d45394e7995815140c8cbcbcf79250a150565b60025460019060a060020a900460ff1681146102f057610002565b6002805460008054600160a060020a0390921692909102606082818181858883f150508354604051600160a060020a0391821694503090911631915082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517f8616bbbbad963e4e65b1366f1d75dfb63f9e9704bbbf91fb01bec70849906cf79250a15056")
+
+ abi, err := abi.JSON(strings.NewReader(definition))
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ cpurchase, err := abi.Pack("confirmPurchase")
+ if err != nil {
+ b.Fatal(err)
+ }
+ creceived, err := abi.Pack("confirmReceived")
+ if err != nil {
+ b.Fatal(err)
+ }
+ refund, err := abi.Pack("refund")
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ for j := 0; j < 400; j++ {
+ Execute(code, cpurchase, nil)
+ Execute(code, creceived, nil)
+ Execute(code, refund, nil)
+ }
+ }
+}
diff --git a/crypto/secp256k1/panic_cb.go b/crypto/secp256k1/panic_cb.go
new file mode 100644
index 000000000..e0e9034ee
--- /dev/null
+++ b/crypto/secp256k1/panic_cb.go
@@ -0,0 +1,33 @@
+// Copyright 2015 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 secp256k1
+
+import "C"
+import "unsafe"
+
+// Callbacks for converting libsecp256k1 internal faults into
+// recoverable Go panics.
+
+//export secp256k1GoPanicIllegal
+func secp256k1GoPanicIllegal(msg *C.char, data unsafe.Pointer) {
+ panic("illegal argument: " + C.GoString(msg))
+}
+
+//export secp256k1GoPanicError
+func secp256k1GoPanicError(msg *C.char, data unsafe.Pointer) {
+ panic("internal error: " + C.GoString(msg))
+}
diff --git a/crypto/secp256k1/secp256.go b/crypto/secp256k1/secp256.go
index 88b43034f..41a5608a5 100644
--- a/crypto/secp256k1/secp256.go
+++ b/crypto/secp256k1/secp256.go
@@ -35,11 +35,14 @@ package secp256k1
#define NDEBUG
#include "./libsecp256k1/src/secp256k1.c"
#include "./libsecp256k1/src/modules/recovery/main_impl.h"
+
+typedef void (*callbackFunc) (const char* msg, void* data);
+extern void secp256k1GoPanicIllegal(const char* msg, void* data);
+extern void secp256k1GoPanicError(const char* msg, void* data);
*/
import "C"
import (
- "bytes"
"errors"
"unsafe"
@@ -62,8 +65,16 @@ var context *C.secp256k1_context
func init() {
// around 20 ms on a modern CPU.
context = C.secp256k1_context_create(3) // SECP256K1_START_SIGN | SECP256K1_START_VERIFY
+ C.secp256k1_context_set_illegal_callback(context, C.callbackFunc(C.secp256k1GoPanicIllegal), nil)
+ C.secp256k1_context_set_error_callback(context, C.callbackFunc(C.secp256k1GoPanicError), nil)
}
+var (
+ ErrInvalidMsgLen = errors.New("invalid message length for signature recovery")
+ ErrInvalidSignatureLen = errors.New("invalid signature length")
+ ErrInvalidRecoveryID = errors.New("invalid signature recovery id")
+)
+
func GenerateKeyPair() ([]byte, []byte) {
var seckey []byte = randentropy.GetEntropyCSPRNG(32)
var seckey_ptr *C.uchar = (*C.uchar)(unsafe.Pointer(&seckey[0]))
@@ -177,69 +188,20 @@ func VerifySeckeyValidity(seckey []byte) error {
return nil
}
-func VerifySignatureValidity(sig []byte) bool {
- //64+1
- if len(sig) != 65 {
- return false
- }
- //malleability check, highest bit must be 1
- if (sig[32] & 0x80) == 0x80 {
- return false
- }
- //recovery id check
- if sig[64] >= 4 {
- return false
- }
-
- return true
-}
-
-//for compressed signatures, does not need pubkey
-func VerifySignature(msg []byte, sig []byte, pubkey1 []byte) error {
- if msg == nil || sig == nil || pubkey1 == nil {
- return errors.New("inputs must be non-nil")
- }
- if len(sig) != 65 {
- return errors.New("invalid signature length")
- }
- if len(pubkey1) != 65 {
- return errors.New("Invalid public key length")
- }
-
- //to enforce malleability, highest bit of S must be 0
- //S starts at 32nd byte
- if (sig[32] & 0x80) == 0x80 { //highest bit must be 1
- return errors.New("Signature not malleable")
- }
-
- if sig[64] >= 4 {
- return errors.New("Recover byte invalid")
- }
-
- // if pubkey recovered, signature valid
- pubkey2, err := RecoverPubkey(msg, sig)
- if err != nil {
- return err
- }
- if len(pubkey2) != 65 {
- return errors.New("Invalid recovered public key length")
- }
- if !bytes.Equal(pubkey1, pubkey2) {
- return errors.New("Public key does not match recovered public key")
- }
-
- return nil
-}
-
-// recovers a public key from the signature
+// RecoverPubkey returns the the public key of the signer.
+// msg must be the 32-byte hash of the message to be signed.
+// sig must be a 65-byte compact ECDSA signature containing the
+// recovery id as the last element.
func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
- if len(sig) != 65 {
- return nil, errors.New("Invalid signature length")
+ if len(msg) != 32 {
+ return nil, ErrInvalidMsgLen
+ }
+ if err := checkSignature(sig); err != nil {
+ return nil, err
}
msg_ptr := (*C.uchar)(unsafe.Pointer(&msg[0]))
sig_ptr := (*C.uchar)(unsafe.Pointer(&sig[0]))
-
pubkey := make([]byte, 64)
/*
this slice is used for both the recoverable signature and the
@@ -248,17 +210,15 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
pubkey recovery is one bottleneck during load in Ethereum
*/
bytes65 := make([]byte, 65)
-
pubkey_ptr := (*C.secp256k1_pubkey)(unsafe.Pointer(&pubkey[0]))
recoverable_sig_ptr := (*C.secp256k1_ecdsa_recoverable_signature)(unsafe.Pointer(&bytes65[0]))
-
recid := C.int(sig[64])
+
ret := C.secp256k1_ecdsa_recoverable_signature_parse_compact(
context,
recoverable_sig_ptr,
sig_ptr,
recid)
-
if ret == C.int(0) {
return nil, errors.New("Failed to parse signature")
}
@@ -269,20 +229,28 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
recoverable_sig_ptr,
msg_ptr,
)
-
if ret == C.int(0) {
return nil, errors.New("Failed to recover public key")
- } else {
- serialized_pubkey_ptr := (*C.uchar)(unsafe.Pointer(&bytes65[0]))
-
- var output_len C.size_t
- C.secp256k1_ec_pubkey_serialize( // always returns 1
- context,
- serialized_pubkey_ptr,
- &output_len,
- pubkey_ptr,
- 0, // SECP256K1_EC_COMPRESSED
- )
- return bytes65, nil
}
+
+ serialized_pubkey_ptr := (*C.uchar)(unsafe.Pointer(&bytes65[0]))
+ var output_len C.size_t
+ C.secp256k1_ec_pubkey_serialize( // always returns 1
+ context,
+ serialized_pubkey_ptr,
+ &output_len,
+ pubkey_ptr,
+ 0, // SECP256K1_EC_COMPRESSED
+ )
+ return bytes65, nil
+}
+
+func checkSignature(sig []byte) error {
+ if len(sig) != 65 {
+ return ErrInvalidSignatureLen
+ }
+ if sig[64] >= 4 {
+ return ErrInvalidRecoveryID
+ }
+ return nil
}
diff --git a/crypto/secp256k1/secp256_test.go b/crypto/secp256k1/secp256_test.go
index 45c448f3c..cb71ea5e7 100644
--- a/crypto/secp256k1/secp256_test.go
+++ b/crypto/secp256k1/secp256_test.go
@@ -56,6 +56,17 @@ func TestSignatureValidity(t *testing.T) {
}
}
+func TestInvalidRecoveryID(t *testing.T) {
+ _, seckey := GenerateKeyPair()
+ msg := randentropy.GetEntropyCSPRNG(32)
+ sig, _ := Sign(msg, seckey)
+ sig[64] = 99
+ _, err := RecoverPubkey(msg, sig)
+ if err != ErrInvalidRecoveryID {
+ t.Fatalf("got %q, want %q", err, ErrInvalidRecoveryID)
+ }
+}
+
func TestSignAndRecover(t *testing.T) {
pubkey1, seckey := GenerateKeyPair()
msg := randentropy.GetEntropyCSPRNG(32)
@@ -70,10 +81,6 @@ func TestSignAndRecover(t *testing.T) {
if !bytes.Equal(pubkey1, pubkey2) {
t.Errorf("pubkey mismatch: want: %x have: %x", pubkey1, pubkey2)
}
- err = VerifySignature(msg, sig, pubkey1)
- if err != nil {
- t.Errorf("signature verification error: %s", err)
- }
}
func TestRandomMessagesWithSameKey(t *testing.T) {
diff --git a/eth/backend.go b/eth/backend.go
index 0683705df..5bd6ac55d 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -65,7 +65,7 @@ const (
var (
jsonlogger = logger.NewJsonLogger()
- datadirInUseErrNos = []uint{11, 32, 35}
+ datadirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true}
portInUseErrRE = regexp.MustCompile("address already in use")
defaultBootNodes = []*discover.Node{
@@ -231,9 +231,7 @@ type Ethereum struct {
chainDb ethdb.Database // Block chain database
dappDb ethdb.Database // Dapp database
- //*** SERVICES ***
- // State manager for processing new blocks and managing the over all states
- blockProcessor *core.BlockProcessor
+ // Handlers
txPool *core.TxPool
blockchain *core.BlockChain
accountManager *accounts.Manager
@@ -286,15 +284,7 @@ func New(config *Config) (*Ethereum, error) {
// Open the chain database and perform any upgrades needed
chainDb, err := newdb(filepath.Join(config.DataDir, "chaindata"))
if err != nil {
- var ok bool
- errno := uint(err.(syscall.Errno))
- for _, no := range datadirInUseErrNos {
- if errno == no {
- ok = true
- break
- }
- }
- if ok {
+ if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] {
err = fmt.Errorf("%v (check if another instance of geth is already running with the same data directory '%s')", err, config.DataDir)
}
return nil, fmt.Errorf("blockchain db err: %v", err)
@@ -311,14 +301,7 @@ func New(config *Config) (*Ethereum, error) {
dappDb, err := newdb(filepath.Join(config.DataDir, "dapp"))
if err != nil {
- var ok bool
- for _, no := range datadirInUseErrNos {
- if uint(err.(syscall.Errno)) == no {
- ok = true
- break
- }
- }
- if ok {
+ if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] {
err = fmt.Errorf("%v (check if another instance of geth is already running with the same data directory '%s')", err, config.DataDir)
}
return nil, fmt.Errorf("dapp db err: %v", err)
@@ -422,8 +405,6 @@ func New(config *Config) (*Ethereum, error) {
newPool := core.NewTxPool(eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit)
eth.txPool = newPool
- eth.blockProcessor = core.NewBlockProcessor(chainDb, eth.pow, eth.blockchain, eth.EventMux())
- eth.blockchain.SetProcessor(eth.blockProcessor)
if eth.protocolManager, err = NewProtocolManager(config.FastSync, config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil {
return nil, err
}
@@ -467,62 +448,10 @@ func New(config *Config) (*Ethereum, error) {
return eth, nil
}
-type NodeInfo struct {
- Name string
- NodeUrl string
- NodeID string
- IP string
- DiscPort int // UDP listening port for discovery protocol
- TCPPort int // TCP listening port for RLPx
- Td string
- ListenAddr string
-}
-
-func (s *Ethereum) NodeInfo() *NodeInfo {
- node := s.net.Self()
-
- return &NodeInfo{
- Name: s.Name(),
- NodeUrl: node.String(),
- NodeID: node.ID.String(),
- IP: node.IP.String(),
- DiscPort: int(node.UDP),
- TCPPort: int(node.TCP),
- ListenAddr: s.net.ListenAddr,
- Td: s.BlockChain().GetTd(s.BlockChain().CurrentBlock().Hash()).String(),
- }
-}
-
-type PeerInfo struct {
- ID string
- Name string
- Caps string
- RemoteAddress string
- LocalAddress string
-}
-
-func newPeerInfo(peer *p2p.Peer) *PeerInfo {
- var caps []string
- for _, cap := range peer.Caps() {
- caps = append(caps, cap.String())
- }
- return &PeerInfo{
- ID: peer.ID().String(),
- Name: peer.Name(),
- Caps: strings.Join(caps, ", "),
- RemoteAddress: peer.RemoteAddr().String(),
- LocalAddress: peer.LocalAddr().String(),
- }
-}
-
-// PeersInfo returns an array of PeerInfo objects describing connected peers
-func (s *Ethereum) PeersInfo() (peersinfo []*PeerInfo) {
- for _, peer := range s.net.Peers() {
- if peer != nil {
- peersinfo = append(peersinfo, newPeerInfo(peer))
- }
- }
- return
+// Network retrieves the underlying P2P network server. This should eventually
+// be moved out into a protocol independent package, but for now use an accessor.
+func (s *Ethereum) Network() *p2p.Server {
+ return s.net
}
func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
@@ -552,24 +481,23 @@ func (s *Ethereum) IsMining() bool { return s.miner.Mining() }
func (s *Ethereum) Miner() *miner.Miner { return s.miner }
// func (s *Ethereum) Logger() logger.LogSystem { return s.logger }
-func (s *Ethereum) Name() string { return s.net.Name }
-func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
-func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
-func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcessor }
-func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
-func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper }
-func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
-func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
-func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb }
-func (s *Ethereum) IsListening() bool { return true } // Always listening
-func (s *Ethereum) PeerCount() int { return s.net.PeerCount() }
-func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() }
-func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers }
-func (s *Ethereum) ClientVersion() string { return s.clientVersion }
-func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
-func (s *Ethereum) NetVersion() int { return s.netVersionId }
-func (s *Ethereum) ShhVersion() int { return s.shhVersionId }
-func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
+func (s *Ethereum) Name() string { return s.net.Name }
+func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
+func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
+func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
+func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper }
+func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
+func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
+func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb }
+func (s *Ethereum) IsListening() bool { return true } // Always listening
+func (s *Ethereum) PeerCount() int { return s.net.PeerCount() }
+func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() }
+func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers }
+func (s *Ethereum) ClientVersion() string { return s.clientVersion }
+func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
+func (s *Ethereum) NetVersion() int { return s.netVersionId }
+func (s *Ethereum) ShhVersion() int { return s.shhVersionId }
+func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
// Start the ethereum
func (s *Ethereum) Start() error {
diff --git a/eth/backend_test.go b/eth/backend_test.go
index 0379fc843..83219de62 100644
--- a/eth/backend_test.go
+++ b/eth/backend_test.go
@@ -32,7 +32,7 @@ func TestMipmapUpgrade(t *testing.T) {
}
// store the receipts
- err := core.PutReceipts(db, receipts)
+ err := core.WriteReceipts(db, receipts)
if err != nil {
t.Fatal(err)
}
@@ -45,7 +45,7 @@ func TestMipmapUpgrade(t *testing.T) {
if err := core.WriteHeadBlockHash(db, block.Hash()); err != nil {
t.Fatalf("failed to insert block number: %v", err)
}
- if err := core.PutBlockReceipts(db, block.Hash(), receipts[i]); err != nil {
+ if err := core.WriteBlockReceipts(db, block.Hash(), receipts[i]); err != nil {
t.Fatal("error writing block receipts:", err)
}
}
diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go
index 153427ee4..c272d05af 100644
--- a/eth/downloader/downloader.go
+++ b/eth/downloader/downloader.go
@@ -45,16 +45,17 @@ var (
MaxReceiptFetch = 256 // Amount of transaction receipts to allow fetching per request
MaxStateFetch = 384 // Amount of node state values to allow fetching per request
- hashTTL = 5 * time.Second // [eth/61] Time it takes for a hash request to time out
- blockSoftTTL = 3 * time.Second // [eth/61] Request completion threshold for increasing or decreasing a peer's bandwidth
- blockHardTTL = 3 * blockSoftTTL // [eth/61] Maximum time allowance before a block request is considered expired
- headerTTL = 5 * time.Second // [eth/62] Time it takes for a header request to time out
- bodySoftTTL = 3 * time.Second // [eth/62] Request completion threshold for increasing or decreasing a peer's bandwidth
- bodyHardTTL = 3 * bodySoftTTL // [eth/62] Maximum time allowance before a block body request is considered expired
- receiptSoftTTL = 3 * time.Second // [eth/63] Request completion threshold for increasing or decreasing a peer's bandwidth
- receiptHardTTL = 3 * receiptSoftTTL // [eth/63] Maximum time allowance before a receipt request is considered expired
- stateSoftTTL = 2 * time.Second // [eth/63] Request completion threshold for increasing or decreasing a peer's bandwidth
- stateHardTTL = 3 * stateSoftTTL // [eth/63] Maximum time allowance before a node data request is considered expired
+ hashTTL = 3 * time.Second // [eth/61] Time it takes for a hash request to time out
+ blockTargetRTT = 3 * time.Second / 2 // [eth/61] Target time for completing a block retrieval request
+ blockTTL = 3 * blockTargetRTT // [eth/61] Maximum time allowance before a block request is considered expired
+
+ headerTTL = 3 * time.Second // [eth/62] Time it takes for a header request to time out
+ bodyTargetRTT = 3 * time.Second / 2 // [eth/62] Target time for completing a block body retrieval request
+ bodyTTL = 3 * bodyTargetRTT // [eth/62] Maximum time allowance before a block body request is considered expired
+ receiptTargetRTT = 3 * time.Second / 2 // [eth/63] Target time for completing a receipt retrieval request
+ receiptTTL = 3 * receiptTargetRTT // [eth/63] Maximum time allowance before a receipt request is considered expired
+ stateTargetRTT = 2 * time.Second / 2 // [eth/63] Target time for completing a state trie retrieval request
+ stateTTL = 3 * stateTargetRTT // [eth/63] Maximum time allowance before a node data request is considered expired
maxQueuedHashes = 256 * 1024 // [eth/61] Maximum number of hashes to queue for import (DOS protection)
maxQueuedHeaders = 256 * 1024 // [eth/62] Maximum number of headers to queue for import (DOS protection)
@@ -74,7 +75,6 @@ var (
errBadPeer = errors.New("action from bad peer ignored")
errStallingPeer = errors.New("peer is stalling")
errNoPeers = errors.New("no peers to keep download active")
- errPendingQueue = errors.New("pending items in queue")
errTimeout = errors.New("timeout")
errEmptyHashSet = errors.New("empty hash set by peer")
errEmptyHeaderSet = errors.New("empty header set by peer")
@@ -90,6 +90,7 @@ var (
errCancelBodyFetch = errors.New("block body download canceled (requested)")
errCancelReceiptFetch = errors.New("receipt download canceled (requested)")
errCancelStateFetch = errors.New("state data download canceled (requested)")
+ errCancelProcessing = errors.New("processing canceled (requested)")
errNoSyncActive = errors.New("no sync active")
)
@@ -129,7 +130,6 @@ type Downloader struct {
// Status
synchroniseMock func(id string, hash common.Hash) error // Replacement for synchronise during testing
synchronising int32
- processing int32
notified int32
// Channels
@@ -215,7 +215,7 @@ func (d *Downloader) Progress() (uint64, uint64, uint64) {
// Synchronising returns whether the downloader is currently retrieving blocks.
func (d *Downloader) Synchronising() bool {
- return atomic.LoadInt32(&d.synchronising) > 0 || atomic.LoadInt32(&d.processing) > 0
+ return atomic.LoadInt32(&d.synchronising) > 0
}
// RegisterPeer injects a new download peer into the set of block source to be
@@ -263,9 +263,6 @@ func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode
glog.V(logger.Debug).Infof("Removing peer %v: %v", id, err)
d.dropPeer(id)
- case errPendingQueue:
- glog.V(logger.Debug).Infoln("Synchronisation aborted:", err)
-
default:
glog.V(logger.Warn).Infof("Synchronisation failed: %v", err)
}
@@ -290,10 +287,6 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode
if atomic.CompareAndSwapInt32(&d.notified, 0, 1) {
glog.V(logger.Info).Infoln("Block synchronisation started")
}
- // Abort if the queue still contains some leftover data
- if d.queue.GetHeadResult() != nil {
- return errPendingQueue
- }
// Reset the queue, peer set and wake channels to clean any internal leftover state
d.queue.Reset()
d.peers.Reset()
@@ -335,7 +328,6 @@ func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err e
defer func() {
// reset on error
if err != nil {
- d.cancel()
d.mux.Post(FailedEvent{err})
} else {
d.mux.Post(DoneEvent{})
@@ -365,23 +357,15 @@ func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err e
d.syncStatsChainHeight = latest
d.syncStatsLock.Unlock()
- // Initiate the sync using a concurrent hash and block retrieval algorithm
+ // Initiate the sync using a concurrent hash and block retrieval algorithm
+ d.queue.Prepare(origin+1, d.mode, 0)
if d.syncInitHook != nil {
d.syncInitHook(origin, latest)
}
- d.queue.Prepare(origin+1, d.mode, 0)
-
- errc := make(chan error, 2)
- go func() { errc <- d.fetchHashes61(p, td, origin+1) }()
- go func() { errc <- d.fetchBlocks61(origin + 1) }()
-
- // If any fetcher fails, cancel the other
- if err := <-errc; err != nil {
- d.cancel()
- <-errc
- return err
- }
- return <-errc
+ return d.spawnSync(
+ func() error { return d.fetchHashes61(p, td, origin+1) },
+ func() error { return d.fetchBlocks61(origin + 1) },
+ )
case p.version >= 62:
// Look up the sync boundaries: the common ancestor and the target block
@@ -405,7 +389,6 @@ func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err e
switch d.mode {
case LightSync:
pivot = latest
-
case FastSync:
// Calculate the new fast/slow sync pivot point
pivotOffset, err := rand.Int(rand.Reader, big.NewInt(int64(fsPivotInterval)))
@@ -426,34 +409,51 @@ func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err e
glog.V(logger.Debug).Infof("Fast syncing until pivot block #%d", pivot)
}
d.queue.Prepare(origin+1, d.mode, pivot)
-
if d.syncInitHook != nil {
d.syncInitHook(origin, latest)
}
- errc := make(chan error, 4)
- go func() { errc <- d.fetchHeaders(p, td, origin+1) }() // Headers are always retrieved
- go func() { errc <- d.fetchBodies(origin + 1) }() // Bodies are retrieved during normal and fast sync
- go func() { errc <- d.fetchReceipts(origin + 1) }() // Receipts are retrieved during fast sync
- go func() { errc <- d.fetchNodeData() }() // Node state data is retrieved during fast sync
-
- // If any fetcher fails, cancel the others
- var fail error
- for i := 0; i < cap(errc); i++ {
- if err := <-errc; err != nil {
- if fail == nil {
- fail = err
- d.cancel()
- }
- }
- }
- return fail
+ return d.spawnSync(
+ func() error { return d.fetchHeaders(p, td, origin+1) }, // Headers are always retrieved
+ func() error { return d.fetchBodies(origin + 1) }, // Bodies are retrieved during normal and fast sync
+ func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during fast sync
+ func() error { return d.fetchNodeData() }, // Node state data is retrieved during fast sync
+ )
default:
// Something very wrong, stop right here
glog.V(logger.Error).Infof("Unsupported eth protocol: %d", p.version)
return errBadPeer
}
- return nil
+}
+
+// spawnSync runs d.process and all given fetcher functions to completion in
+// separate goroutines, returning the first error that appears.
+func (d *Downloader) spawnSync(fetchers ...func() error) error {
+ var wg sync.WaitGroup
+ errc := make(chan error, len(fetchers)+1)
+ wg.Add(len(fetchers) + 1)
+ go func() { defer wg.Done(); errc <- d.process() }()
+ for _, fn := range fetchers {
+ fn := fn
+ go func() { defer wg.Done(); errc <- fn() }()
+ }
+ // Wait for the first error, then terminate the others.
+ var err error
+ for i := 0; i < len(fetchers)+1; i++ {
+ if i == len(fetchers) {
+ // Close the queue when all fetchers have exited.
+ // This will cause the block processor to end when
+ // it has processed the queue.
+ d.queue.Close()
+ }
+ if err = <-errc; err != nil {
+ break
+ }
+ }
+ d.queue.Close()
+ d.cancel()
+ wg.Wait()
+ return err
}
// cancel cancels all of the operations and resets the queue. It returns true
@@ -470,12 +470,10 @@ func (d *Downloader) cancel() {
}
}
d.cancelLock.Unlock()
-
- // Reset the queue
- d.queue.Reset()
}
// Terminate interrupts the downloader, canceling all pending operations.
+// The downloader cannot be reused after calling Terminate.
func (d *Downloader) Terminate() {
atomic.StoreInt32(&d.interrupt, 1)
d.cancel()
@@ -489,21 +487,12 @@ func (d *Downloader) fetchHeight61(p *peer) (uint64, error) {
// Request the advertised remote head block and wait for the response
go p.getBlocks([]common.Hash{p.head})
- timeout := time.After(blockSoftTTL)
+ timeout := time.After(hashTTL)
for {
select {
case <-d.cancelCh:
return 0, errCancelBlockFetch
- case <-d.headerCh:
- // Out of bounds eth/62 block headers received, ignore them
-
- case <-d.bodyCh:
- // Out of bounds eth/62 block bodies received, ignore them
-
- case <-d.hashCh:
- // Out of bounds hashes received, ignore them
-
case packet := <-d.blockCh:
// Discard anything not from the origin peer
if packet.PeerId() != p.id {
@@ -521,6 +510,16 @@ func (d *Downloader) fetchHeight61(p *peer) (uint64, error) {
case <-timeout:
glog.V(logger.Debug).Infof("%v: head block timeout", p)
return 0, errTimeout
+
+ case <-d.hashCh:
+ // Out of bounds hashes received, ignore them
+
+ case <-d.headerCh:
+ case <-d.bodyCh:
+ case <-d.stateCh:
+ case <-d.receiptCh:
+ // Ignore eth/{62,63} packets because this is eth/61.
+ // These can arrive as a late delivery from a previous sync.
}
}
}
@@ -571,18 +570,19 @@ func (d *Downloader) findAncestor61(p *peer) (uint64, error) {
}
}
+ case <-timeout:
+ glog.V(logger.Debug).Infof("%v: head hash timeout", p)
+ return 0, errTimeout
+
case <-d.blockCh:
// Out of bounds blocks received, ignore them
case <-d.headerCh:
- // Out of bounds eth/62 block headers received, ignore them
-
case <-d.bodyCh:
- // Out of bounds eth/62 block bodies received, ignore them
-
- case <-timeout:
- glog.V(logger.Debug).Infof("%v: head hash timeout", p)
- return 0, errTimeout
+ case <-d.stateCh:
+ case <-d.receiptCh:
+ // Ignore eth/{62,63} packets because this is eth/61.
+ // These can arrive as a late delivery from a previous sync.
}
}
// If the head fetch already found an ancestor, return
@@ -631,18 +631,19 @@ func (d *Downloader) findAncestor61(p *peer) (uint64, error) {
}
start = check
+ case <-timeout:
+ glog.V(logger.Debug).Infof("%v: search hash timeout", p)
+ return 0, errTimeout
+
case <-d.blockCh:
// Out of bounds blocks received, ignore them
case <-d.headerCh:
- // Out of bounds eth/62 block headers received, ignore them
-
case <-d.bodyCh:
- // Out of bounds eth/62 block bodies received, ignore them
-
- case <-timeout:
- glog.V(logger.Debug).Infof("%v: search hash timeout", p)
- return 0, errTimeout
+ case <-d.stateCh:
+ case <-d.receiptCh:
+ // Ignore eth/{62,63} packets because this is eth/61.
+ // These can arrive as a late delivery from a previous sync.
}
}
}
@@ -676,12 +677,6 @@ func (d *Downloader) fetchHashes61(p *peer, td *big.Int, from uint64) error {
case <-d.cancelCh:
return errCancelHashFetch
- case <-d.headerCh:
- // Out of bounds eth/62 block headers received, ignore them
-
- case <-d.bodyCh:
- // Out of bounds eth/62 block bodies received, ignore them
-
case packet := <-d.hashCh:
// Make sure the active peer is giving us the hashes
if packet.PeerId() != p.id {
@@ -750,6 +745,13 @@ func (d *Downloader) fetchHashes61(p *peer, td *big.Int, from uint64) error {
glog.V(logger.Debug).Infof("%v: hash request timed out", p)
hashTimeoutMeter.Mark(1)
return errTimeout
+
+ case <-d.headerCh:
+ case <-d.bodyCh:
+ case <-d.stateCh:
+ case <-d.receiptCh:
+ // Ignore eth/{62,63} packets because this is eth/61.
+ // These can arrive as a late delivery from a previous sync.
}
}
}
@@ -774,59 +776,31 @@ func (d *Downloader) fetchBlocks61(from uint64) error {
case <-d.cancelCh:
return errCancelBlockFetch
- case <-d.headerCh:
- // Out of bounds eth/62 block headers received, ignore them
-
- case <-d.bodyCh:
- // Out of bounds eth/62 block bodies received, ignore them
-
case packet := <-d.blockCh:
// If the peer was previously banned and failed to deliver it's pack
// in a reasonable time frame, ignore it's message.
if peer := d.peers.Peer(packet.PeerId()); peer != nil {
- // Deliver the received chunk of blocks, and demote in case of errors
blocks := packet.(*blockPack).blocks
- err := d.queue.DeliverBlocks(peer.id, blocks)
- switch err {
- case nil:
- // If no blocks were delivered, demote the peer (need the delivery above)
- if len(blocks) == 0 {
- peer.Demote()
- peer.SetBlocksIdle()
- glog.V(logger.Detail).Infof("%s: no blocks delivered", peer)
- break
- }
- // All was successful, promote the peer and potentially start processing
- peer.Promote()
- peer.SetBlocksIdle()
- glog.V(logger.Detail).Infof("%s: delivered %d blocks", peer, len(blocks))
- go d.process()
- case errInvalidChain:
- // The hash chain is invalid (blocks are not ordered properly), abort
+ // Deliver the received chunk of blocks and check chain validity
+ accepted, err := d.queue.DeliverBlocks(peer.id, blocks)
+ if err == errInvalidChain {
return err
-
- case errNoFetchesPending:
- // Peer probably timed out with its delivery but came through
- // in the end, demote, but allow to to pull from this peer.
- peer.Demote()
- peer.SetBlocksIdle()
- glog.V(logger.Detail).Infof("%s: out of bound delivery", peer)
-
- case errStaleDelivery:
- // Delivered something completely else than requested, usually
- // caused by a timeout and delivery during a new sync cycle.
- // Don't set it to idle as the original request should still be
- // in flight.
- peer.Demote()
- glog.V(logger.Detail).Infof("%s: stale delivery", peer)
-
+ }
+ // Unless a peer delivered something completely else than requested (usually
+ // caused by a timed out request which came through in the end), set it to
+ // idle. If the delivery's stale, the peer should have already been idled.
+ if err != errStaleDelivery {
+ peer.SetBlocksIdle(accepted)
+ }
+ // Issue a log to the user to see what's going on
+ switch {
+ case err == nil && len(blocks) == 0:
+ glog.V(logger.Detail).Infof("%s: no blocks delivered", peer)
+ case err == nil:
+ glog.V(logger.Detail).Infof("%s: delivered %d blocks", peer, len(blocks))
default:
- // Peer did something semi-useful, demote but keep it around
- peer.Demote()
- peer.SetBlocksIdle()
- glog.V(logger.Detail).Infof("%s: delivery partially failed: %v", peer, err)
- go d.process()
+ glog.V(logger.Detail).Infof("%s: delivery failed: %v", peer, err)
}
}
// Blocks arrived, try to update the progress
@@ -859,10 +833,15 @@ func (d *Downloader) fetchBlocks61(from uint64) error {
return errNoPeers
}
// Check for block request timeouts and demote the responsible peers
- for _, pid := range d.queue.ExpireBlocks(blockHardTTL) {
+ for pid, fails := range d.queue.ExpireBlocks(blockTTL) {
if peer := d.peers.Peer(pid); peer != nil {
- peer.Demote()
- glog.V(logger.Detail).Infof("%s: block delivery timeout", peer)
+ if fails > 1 {
+ glog.V(logger.Detail).Infof("%s: block delivery timeout", peer)
+ peer.SetBlocksIdle(0)
+ } else {
+ glog.V(logger.Debug).Infof("%s: stalling block delivery, dropping", peer)
+ d.dropPeer(pid)
+ }
}
}
// If there's nothing more to fetch, wait or terminate
@@ -909,6 +888,13 @@ func (d *Downloader) fetchBlocks61(from uint64) error {
if !throttled && !d.queue.InFlightBlocks() && len(idles) == total {
return errPeersUnavailable
}
+
+ case <-d.headerCh:
+ case <-d.bodyCh:
+ case <-d.stateCh:
+ case <-d.receiptCh:
+ // Ignore eth/{62,63} packets because this is eth/61.
+ // These can arrive as a late delivery from a previous sync.
}
}
}
@@ -941,18 +927,19 @@ func (d *Downloader) fetchHeight(p *peer) (uint64, error) {
}
return headers[0].Number.Uint64(), nil
+ case <-timeout:
+ glog.V(logger.Debug).Infof("%v: head header timeout", p)
+ return 0, errTimeout
+
case <-d.bodyCh:
- // Out of bounds block bodies received, ignore them
+ case <-d.stateCh:
+ case <-d.receiptCh:
+ // Out of bounds delivery, ignore
case <-d.hashCh:
- // Out of bounds eth/61 hashes received, ignore them
-
case <-d.blockCh:
- // Out of bounds eth/61 blocks received, ignore them
-
- case <-timeout:
- glog.V(logger.Debug).Infof("%v: head header timeout", p)
- return 0, errTimeout
+ // Ignore eth/61 packets because this is eth/62+.
+ // These can arrive as a late delivery from a previous sync.
}
}
}
@@ -1008,18 +995,19 @@ func (d *Downloader) findAncestor(p *peer) (uint64, error) {
}
}
+ case <-timeout:
+ glog.V(logger.Debug).Infof("%v: head header timeout", p)
+ return 0, errTimeout
+
case <-d.bodyCh:
- // Out of bounds block bodies received, ignore them
+ case <-d.stateCh:
+ case <-d.receiptCh:
+ // Out of bounds delivery, ignore
case <-d.hashCh:
- // Out of bounds eth/61 hashes received, ignore them
-
case <-d.blockCh:
- // Out of bounds eth/61 blocks received, ignore them
-
- case <-timeout:
- glog.V(logger.Debug).Infof("%v: head header timeout", p)
- return 0, errTimeout
+ // Ignore eth/61 packets because this is eth/62+.
+ // These can arrive as a late delivery from a previous sync.
}
}
// If the head fetch already found an ancestor, return
@@ -1068,18 +1056,19 @@ func (d *Downloader) findAncestor(p *peer) (uint64, error) {
}
start = check
+ case <-timeout:
+ glog.V(logger.Debug).Infof("%v: search header timeout", p)
+ return 0, errTimeout
+
case <-d.bodyCh:
- // Out of bounds block bodies received, ignore them
+ case <-d.stateCh:
+ case <-d.receiptCh:
+ // Out of bounds delivery, ignore
case <-d.hashCh:
- // Out of bounds eth/61 hashes received, ignore them
-
case <-d.blockCh:
- // Out of bounds eth/61 blocks received, ignore them
-
- case <-timeout:
- glog.V(logger.Debug).Infof("%v: search header timeout", p)
- return 0, errTimeout
+ // Ignore eth/61 packets because this is eth/62+.
+ // These can arrive as a late delivery from a previous sync.
}
}
}
@@ -1141,12 +1130,6 @@ func (d *Downloader) fetchHeaders(p *peer, td *big.Int, from uint64) error {
case <-d.cancelCh:
return errCancelHeaderFetch
- case <-d.hashCh:
- // Out of bounds eth/61 hashes received, ignore them
-
- case <-d.blockCh:
- // Out of bounds eth/61 blocks received, ignore them
-
case packet := <-d.headerCh:
// Make sure the active peer is giving us the headers
if packet.PeerId() != p.id {
@@ -1268,6 +1251,11 @@ func (d *Downloader) fetchHeaders(p *peer, td *big.Int, from uint64) error {
}
}
return nil
+
+ case <-d.hashCh:
+ case <-d.blockCh:
+ // Ignore eth/61 packets because this is eth/62+.
+ // These can arrive as a late delivery from a previous sync.
}
}
}
@@ -1279,14 +1267,14 @@ func (d *Downloader) fetchBodies(from uint64) error {
glog.V(logger.Debug).Infof("Downloading block bodies from #%d", from)
var (
- deliver = func(packet dataPack) error {
+ deliver = func(packet dataPack) (int, error) {
pack := packet.(*bodyPack)
return d.queue.DeliverBodies(pack.peerId, pack.transactions, pack.uncles)
}
- expire = func() []string { return d.queue.ExpireBodies(bodyHardTTL) }
+ expire = func() map[string]int { return d.queue.ExpireBodies(bodyTTL) }
fetch = func(p *peer, req *fetchRequest) error { return p.FetchBodies(req) }
capacity = func(p *peer) int { return p.BlockCapacity() }
- setIdle = func(p *peer) { p.SetBodiesIdle() }
+ setIdle = func(p *peer, accepted int) { p.SetBodiesIdle(accepted) }
)
err := d.fetchParts(errCancelBodyFetch, d.bodyCh, deliver, d.bodyWakeCh, expire,
d.queue.PendingBlocks, d.queue.InFlightBlocks, d.queue.ShouldThrottleBlocks, d.queue.ReserveBodies,
@@ -1303,14 +1291,14 @@ func (d *Downloader) fetchReceipts(from uint64) error {
glog.V(logger.Debug).Infof("Downloading receipts from #%d", from)
var (
- deliver = func(packet dataPack) error {
+ deliver = func(packet dataPack) (int, error) {
pack := packet.(*receiptPack)
return d.queue.DeliverReceipts(pack.peerId, pack.receipts)
}
- expire = func() []string { return d.queue.ExpireReceipts(receiptHardTTL) }
+ expire = func() map[string]int { return d.queue.ExpireReceipts(receiptTTL) }
fetch = func(p *peer, req *fetchRequest) error { return p.FetchReceipts(req) }
capacity = func(p *peer) int { return p.ReceiptCapacity() }
- setIdle = func(p *peer) { p.SetReceiptsIdle() }
+ setIdle = func(p *peer, accepted int) { p.SetReceiptsIdle(accepted) }
)
err := d.fetchParts(errCancelReceiptFetch, d.receiptCh, deliver, d.receiptWakeCh, expire,
d.queue.PendingReceipts, d.queue.InFlightReceipts, d.queue.ShouldThrottleReceipts, d.queue.ReserveReceipts,
@@ -1327,7 +1315,7 @@ func (d *Downloader) fetchNodeData() error {
glog.V(logger.Debug).Infof("Downloading node state data")
var (
- deliver = func(packet dataPack) error {
+ deliver = func(packet dataPack) (int, error) {
start := time.Now()
return d.queue.DeliverNodeData(packet.PeerId(), packet.(*statePack).states, func(err error, delivered int) {
if err != nil {
@@ -1336,10 +1324,8 @@ func (d *Downloader) fetchNodeData() error {
d.cancel()
return
}
- // Processing succeeded, notify state fetcher and processor of continuation
- if d.queue.PendingNodeData() == 0 {
- go d.process()
- } else {
+ // Processing succeeded, notify state fetcher of continuation
+ if d.queue.PendingNodeData() > 0 {
select {
case d.stateWakeCh <- true:
default:
@@ -1348,19 +1334,18 @@ func (d *Downloader) fetchNodeData() error {
// Log a message to the user and return
d.syncStatsLock.Lock()
defer d.syncStatsLock.Unlock()
-
d.syncStatsStateDone += uint64(delivered)
glog.V(logger.Info).Infof("imported %d state entries in %v: processed %d in total", delivered, time.Since(start), d.syncStatsStateDone)
})
}
- expire = func() []string { return d.queue.ExpireNodeData(stateHardTTL) }
+ expire = func() map[string]int { return d.queue.ExpireNodeData(stateTTL) }
throttle = func() bool { return false }
reserve = func(p *peer, count int) (*fetchRequest, bool, error) {
return d.queue.ReserveNodeData(p, count), false, nil
}
fetch = func(p *peer, req *fetchRequest) error { return p.FetchNodeData(req) }
capacity = func(p *peer) int { return p.NodeDataCapacity() }
- setIdle = func(p *peer) { p.SetNodeDataIdle() }
+ setIdle = func(p *peer, accepted int) { p.SetNodeDataIdle(accepted) }
)
err := d.fetchParts(errCancelStateFetch, d.stateCh, deliver, d.stateWakeCh, expire,
d.queue.PendingNodeData, d.queue.InFlightNodeData, throttle, reserve, nil, fetch,
@@ -1373,10 +1358,10 @@ func (d *Downloader) fetchNodeData() error {
// fetchParts iteratively downloads scheduled block parts, taking any available
// peers, reserving a chunk of fetch requests for each, waiting for delivery and
// also periodically checking for timeouts.
-func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliver func(packet dataPack) error, wakeCh chan bool,
- expire func() []string, pending func() int, inFlight func() bool, throttle func() bool, reserve func(*peer, int) (*fetchRequest, bool, error),
+func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliver func(dataPack) (int, error), wakeCh chan bool,
+ expire func() map[string]int, pending func() int, inFlight func() bool, throttle func() bool, reserve func(*peer, int) (*fetchRequest, bool, error),
fetchHook func([]*types.Header), fetch func(*peer, *fetchRequest) error, cancel func(*fetchRequest), capacity func(*peer) int,
- idle func() ([]*peer, int), setIdle func(*peer), kind string) error {
+ idle func() ([]*peer, int), setIdle func(*peer, int), kind string) error {
// Create a ticker to detect expired retrieval tasks
ticker := time.NewTicker(100 * time.Millisecond)
@@ -1391,57 +1376,29 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
case <-d.cancelCh:
return errCancel
- case <-d.hashCh:
- // Out of bounds eth/61 hashes received, ignore them
-
- case <-d.blockCh:
- // Out of bounds eth/61 blocks received, ignore them
-
case packet := <-deliveryCh:
// If the peer was previously banned and failed to deliver it's pack
// in a reasonable time frame, ignore it's message.
if peer := d.peers.Peer(packet.PeerId()); peer != nil {
- // Deliver the received chunk of data, and demote in case of errors
- switch err := deliver(packet); err {
- case nil:
- // If no blocks were delivered, demote the peer (need the delivery above to clean internal queue!)
- if packet.Items() == 0 {
- peer.Demote()
- setIdle(peer)
- glog.V(logger.Detail).Infof("%s: no %s delivered", peer, strings.ToLower(kind))
- break
- }
- // All was successful, promote the peer and potentially start processing
- peer.Promote()
- setIdle(peer)
- glog.V(logger.Detail).Infof("%s: delivered %s %s(s)", peer, packet.Stats(), strings.ToLower(kind))
- go d.process()
-
- case errInvalidChain:
- // The hash chain is invalid (blocks are not ordered properly), abort
+ // Deliver the received chunk of data and check chain validity
+ accepted, err := deliver(packet)
+ if err == errInvalidChain {
return err
-
- case errNoFetchesPending:
- // Peer probably timed out with its delivery but came through
- // in the end, demote, but allow to to pull from this peer.
- peer.Demote()
- setIdle(peer)
- glog.V(logger.Detail).Infof("%s: out of bound %s delivery", peer, strings.ToLower(kind))
-
- case errStaleDelivery:
- // Delivered something completely else than requested, usually
- // caused by a timeout and delivery during a new sync cycle.
- // Don't set it to idle as the original request should still be
- // in flight.
- peer.Demote()
- glog.V(logger.Detail).Infof("%s: %s stale delivery", peer, strings.ToLower(kind))
-
+ }
+ // Unless a peer delivered something completely else than requested (usually
+ // caused by a timed out request which came through in the end), set it to
+ // idle. If the delivery's stale, the peer should have already been idled.
+ if err != errStaleDelivery {
+ setIdle(peer, accepted)
+ }
+ // Issue a log to the user to see what's going on
+ switch {
+ case err == nil && packet.Items() == 0:
+ glog.V(logger.Detail).Infof("%s: no %s delivered", peer, strings.ToLower(kind))
+ case err == nil:
+ glog.V(logger.Detail).Infof("%s: delivered %s %s(s)", peer, packet.Stats(), strings.ToLower(kind))
default:
- // Peer did something semi-useful, demote but keep it around
- peer.Demote()
- setIdle(peer)
- glog.V(logger.Detail).Infof("%s: %s delivery partially failed: %v", peer, strings.ToLower(kind), err)
- go d.process()
+ glog.V(logger.Detail).Infof("%s: %s delivery failed: %v", peer, strings.ToLower(kind), err)
}
}
// Blocks assembled, try to update the progress
@@ -1474,11 +1431,15 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
return errNoPeers
}
// Check for fetch request timeouts and demote the responsible peers
- for _, pid := range expire() {
+ for pid, fails := range expire() {
if peer := d.peers.Peer(pid); peer != nil {
- peer.Demote()
- setIdle(peer)
- glog.V(logger.Detail).Infof("%s: %s delivery timeout", peer, strings.ToLower(kind))
+ if fails > 1 {
+ glog.V(logger.Detail).Infof("%s: %s delivery timeout", peer, strings.ToLower(kind))
+ setIdle(peer, 0)
+ } else {
+ glog.V(logger.Debug).Infof("%s: stalling %s delivery, dropping", peer, strings.ToLower(kind))
+ d.dropPeer(pid)
+ }
}
}
// If there's nothing more to fetch, wait or terminate
@@ -1508,7 +1469,6 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
}
if progress {
progressed = true
- go d.process()
}
if request == nil {
continue
@@ -1540,51 +1500,23 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
if !progressed && !throttled && !running && len(idles) == total && pending() > 0 {
return errPeersUnavailable
}
+
+ case <-d.hashCh:
+ case <-d.blockCh:
+ // Ignore eth/61 packets because this is eth/62+.
+ // These can arrive as a late delivery from a previous sync.
}
}
}
// process takes fetch results from the queue and tries to import them into the
-// chain. The type of import operation will depend on the result contents:
-// -
-//
-// The algorithmic flow is as follows:
-// - The `processing` flag is swapped to 1 to ensure singleton access
-// - The current `cancel` channel is retrieved to detect sync abortions
-// - Blocks are iteratively taken from the cache and inserted into the chain
-// - When the cache becomes empty, insertion stops
-// - The `processing` flag is swapped back to 0
-// - A post-exit check is made whether new blocks became available
-// - This step is important: it handles a potential race condition between
-// checking for no more work, and releasing the processing "mutex". In
-// between these state changes, a block may have arrived, but a processing
-// attempt denied, so we need to re-enter to ensure the block isn't left
-// to idle in the cache.
-func (d *Downloader) process() {
- // Make sure only one goroutine is ever allowed to process blocks at once
- if !atomic.CompareAndSwapInt32(&d.processing, 0, 1) {
- return
- }
- // If the processor just exited, but there are freshly pending items, try to
- // reenter. This is needed because the goroutine spinned up for processing
- // the fresh results might have been rejected entry to to this present thread
- // not yet releasing the `processing` state.
- defer func() {
- if atomic.LoadInt32(&d.interrupt) == 0 && d.queue.GetHeadResult() != nil {
- d.process()
- }
- }()
- // Release the lock upon exit (note, before checking for reentry!)
- // the import statistics to zero.
- defer atomic.StoreInt32(&d.processing, 0)
-
- // Repeat the processing as long as there are results to process
+// chain. The type of import operation will depend on the result contents.
+func (d *Downloader) process() error {
+ pivot := d.queue.FastSyncPivot()
for {
- // Fetch the next batch of results
- pivot := d.queue.FastSyncPivot() // Fetch pivot before results to prevent reset race
- results := d.queue.TakeResults()
+ results := d.queue.WaitResults()
if len(results) == 0 {
- return
+ return nil // queue empty
}
if d.chainInsertHook != nil {
d.chainInsertHook(results)
@@ -1597,7 +1529,7 @@ func (d *Downloader) process() {
for len(results) != 0 {
// Check for any termination requests
if atomic.LoadInt32(&d.interrupt) == 1 {
- return
+ return errCancelProcessing
}
// Retrieve the a batch of results to import
var (
@@ -1633,8 +1565,7 @@ func (d *Downloader) process() {
}
if err != nil {
glog.V(logger.Debug).Infof("Result #%d [%x…] processing failed: %v", results[index].Header.Number, results[index].Header.Hash().Bytes()[:4], err)
- d.cancel()
- return
+ return err
}
// Shift the results to the next batch
results = results[items:]
@@ -1685,19 +1616,16 @@ func (d *Downloader) deliver(id string, destCh chan dataPack, packet dataPack, i
dropMeter.Mark(int64(packet.Items()))
}
}()
- // Make sure the downloader is active
- if atomic.LoadInt32(&d.synchronising) == 0 {
- return errNoSyncActive
- }
// Deliver or abort if the sync is canceled while queuing
d.cancelLock.RLock()
cancel := d.cancelCh
d.cancelLock.RUnlock()
-
+ if cancel == nil {
+ return errNoSyncActive
+ }
select {
case destCh <- packet:
return nil
-
case <-cancel:
return errNoSyncActive
}
diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go
index ef6f74a6b..cfcc8a2ef 100644
--- a/eth/downloader/downloader_test.go
+++ b/eth/downloader/downloader_test.go
@@ -169,17 +169,7 @@ func (dl *downloadTester) sync(id string, td *big.Int, mode SyncMode) error {
}
}
dl.lock.RUnlock()
-
- err := dl.downloader.synchronise(id, hash, td, mode)
- for {
- // If the queue is empty and processing stopped, break
- if dl.downloader.queue.Idle() && atomic.LoadInt32(&dl.downloader.processing) == 0 {
- break
- }
- // Otherwise sleep a bit and retry
- time.Sleep(time.Millisecond)
- }
- return err
+ return dl.downloader.synchronise(id, hash, td, mode)
}
// hasHeader checks if a header is present in the testers canonical chain.
@@ -701,6 +691,8 @@ func TestCanonicalSynchronisation64Fast(t *testing.T) { testCanonicalSynchronis
func TestCanonicalSynchronisation64Light(t *testing.T) { testCanonicalSynchronisation(t, 64, LightSync) }
func testCanonicalSynchronisation(t *testing.T, protocol int, mode SyncMode) {
+ t.Parallel()
+
// Create a small enough block chain to download
targetBlocks := blockCacheLimit - 15
hashes, headers, blocks, receipts := makeChain(targetBlocks, 0, genesis, nil)
@@ -725,6 +717,8 @@ func TestThrottling64Full(t *testing.T) { testThrottling(t, 64, FullSync) }
func TestThrottling64Fast(t *testing.T) { testThrottling(t, 64, FastSync) }
func testThrottling(t *testing.T, protocol int, mode SyncMode) {
+ t.Parallel()
+
// Create a long block chain to download and the tester
targetBlocks := 8 * blockCacheLimit
hashes, headers, blocks, receipts := makeChain(targetBlocks, 0, genesis, nil)
@@ -757,8 +751,8 @@ func testThrottling(t *testing.T, protocol int, mode SyncMode) {
for start := time.Now(); time.Since(start) < time.Second; {
time.Sleep(25 * time.Millisecond)
- tester.lock.RLock()
- tester.downloader.queue.lock.RLock()
+ tester.lock.Lock()
+ tester.downloader.queue.lock.Lock()
cached = len(tester.downloader.queue.blockDonePool)
if mode == FastSync {
if receipts := len(tester.downloader.queue.receiptDonePool); receipts < cached {
@@ -769,8 +763,8 @@ func testThrottling(t *testing.T, protocol int, mode SyncMode) {
}
frozen = int(atomic.LoadUint32(&blocked))
retrieved = len(tester.ownBlocks)
- tester.downloader.queue.lock.RUnlock()
- tester.lock.RUnlock()
+ tester.downloader.queue.lock.Unlock()
+ tester.lock.Unlock()
if cached == blockCacheLimit || retrieved+cached+frozen == targetBlocks+1 {
break
@@ -810,6 +804,8 @@ func TestForkedSynchronisation64Fast(t *testing.T) { testForkedSynchronisation(
func TestForkedSynchronisation64Light(t *testing.T) { testForkedSynchronisation(t, 64, LightSync) }
func testForkedSynchronisation(t *testing.T, protocol int, mode SyncMode) {
+ t.Parallel()
+
// Create a long enough forked chain
common, fork := MaxHashFetch, 2*MaxHashFetch
hashesA, hashesB, headersA, headersB, blocksA, blocksB, receiptsA, receiptsB := makeChainFork(common+fork, fork, genesis, nil)
@@ -833,6 +829,7 @@ func testForkedSynchronisation(t *testing.T, protocol int, mode SyncMode) {
// Tests that an inactive downloader will not accept incoming hashes and blocks.
func TestInactiveDownloader61(t *testing.T) {
+ t.Parallel()
tester := newTester()
// Check that neither hashes nor blocks are accepted
@@ -847,6 +844,7 @@ func TestInactiveDownloader61(t *testing.T) {
// Tests that an inactive downloader will not accept incoming block headers and
// bodies.
func TestInactiveDownloader62(t *testing.T) {
+ t.Parallel()
tester := newTester()
// Check that neither block headers nor bodies are accepted
@@ -861,6 +859,7 @@ func TestInactiveDownloader62(t *testing.T) {
// Tests that an inactive downloader will not accept incoming block headers,
// bodies and receipts.
func TestInactiveDownloader63(t *testing.T) {
+ t.Parallel()
tester := newTester()
// Check that neither block headers nor bodies are accepted
@@ -885,6 +884,8 @@ func TestCancel64Fast(t *testing.T) { testCancel(t, 64, FastSync) }
func TestCancel64Light(t *testing.T) { testCancel(t, 64, LightSync) }
func testCancel(t *testing.T, protocol int, mode SyncMode) {
+ t.Parallel()
+
// Create a small enough block chain to download and the tester
targetBlocks := blockCacheLimit - 15
if targetBlocks >= MaxHashFetch {
@@ -923,6 +924,8 @@ func TestMultiSynchronisation64Fast(t *testing.T) { testMultiSynchronisation(t,
func TestMultiSynchronisation64Light(t *testing.T) { testMultiSynchronisation(t, 64, LightSync) }
func testMultiSynchronisation(t *testing.T, protocol int, mode SyncMode) {
+ t.Parallel()
+
// Create various peers with various parts of the chain
targetPeers := 8
targetBlocks := targetPeers*blockCacheLimit - 15
@@ -950,6 +953,8 @@ func TestMultiProtoSynchronisation64Fast(t *testing.T) { testMultiProtoSync(t,
func TestMultiProtoSynchronisation64Light(t *testing.T) { testMultiProtoSync(t, 64, LightSync) }
func testMultiProtoSync(t *testing.T, protocol int, mode SyncMode) {
+ t.Parallel()
+
// Create a small enough block chain to download
targetBlocks := blockCacheLimit - 15
hashes, headers, blocks, receipts := makeChain(targetBlocks, 0, genesis, nil)
@@ -986,6 +991,8 @@ func TestEmptyShortCircuit64Fast(t *testing.T) { testEmptyShortCircuit(t, 64, F
func TestEmptyShortCircuit64Light(t *testing.T) { testEmptyShortCircuit(t, 64, LightSync) }
func testEmptyShortCircuit(t *testing.T, protocol int, mode SyncMode) {
+ t.Parallel()
+
// Create a block chain to download
targetBlocks := 2*blockCacheLimit - 15
hashes, headers, blocks, receipts := makeChain(targetBlocks, 0, genesis, nil)
@@ -1037,6 +1044,8 @@ func TestMissingHeaderAttack64Fast(t *testing.T) { testMissingHeaderAttack(t, 6
func TestMissingHeaderAttack64Light(t *testing.T) { testMissingHeaderAttack(t, 64, LightSync) }
func testMissingHeaderAttack(t *testing.T, protocol int, mode SyncMode) {
+ t.Parallel()
+
// Create a small enough block chain to download
targetBlocks := blockCacheLimit - 15
hashes, headers, blocks, receipts := makeChain(targetBlocks, 0, genesis, nil)
@@ -1188,6 +1197,8 @@ func TestHighTDStarvationAttack64Fast(t *testing.T) { testHighTDStarvationAttac
func TestHighTDStarvationAttack64Light(t *testing.T) { testHighTDStarvationAttack(t, 64, LightSync) }
func testHighTDStarvationAttack(t *testing.T, protocol int, mode SyncMode) {
+ t.Parallel()
+
tester := newTester()
hashes, headers, blocks, receipts := makeChain(0, 0, genesis, nil)
@@ -1209,25 +1220,26 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol int) {
result error
drop bool
}{
- {nil, false}, // Sync succeeded, all is well
- {errBusy, false}, // Sync is already in progress, no problem
- {errUnknownPeer, false}, // Peer is unknown, was already dropped, don't double drop
- {errBadPeer, true}, // Peer was deemed bad for some reason, drop it
- {errStallingPeer, true}, // Peer was detected to be stalling, drop it
- {errNoPeers, false}, // No peers to download from, soft race, no issue
- {errPendingQueue, false}, // There are blocks still cached, wait to exhaust, no issue
- {errTimeout, true}, // No hashes received in due time, drop the peer
- {errEmptyHashSet, true}, // No hashes were returned as a response, drop as it's a dead end
- {errEmptyHeaderSet, true}, // No headers were returned as a response, drop as it's a dead end
- {errPeersUnavailable, true}, // Nobody had the advertised blocks, drop the advertiser
- {errInvalidChain, true}, // Hash chain was detected as invalid, definitely drop
- {errInvalidBlock, false}, // A bad peer was detected, but not the sync origin
- {errInvalidBody, false}, // A bad peer was detected, but not the sync origin
- {errInvalidReceipt, false}, // A bad peer was detected, but not the sync origin
- {errCancelHashFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
- {errCancelBlockFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
- {errCancelHeaderFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
- {errCancelBodyFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
+ {nil, false}, // Sync succeeded, all is well
+ {errBusy, false}, // Sync is already in progress, no problem
+ {errUnknownPeer, false}, // Peer is unknown, was already dropped, don't double drop
+ {errBadPeer, true}, // Peer was deemed bad for some reason, drop it
+ {errStallingPeer, true}, // Peer was detected to be stalling, drop it
+ {errNoPeers, false}, // No peers to download from, soft race, no issue
+ {errTimeout, true}, // No hashes received in due time, drop the peer
+ {errEmptyHashSet, true}, // No hashes were returned as a response, drop as it's a dead end
+ {errEmptyHeaderSet, true}, // No headers were returned as a response, drop as it's a dead end
+ {errPeersUnavailable, true}, // Nobody had the advertised blocks, drop the advertiser
+ {errInvalidChain, true}, // Hash chain was detected as invalid, definitely drop
+ {errInvalidBlock, false}, // A bad peer was detected, but not the sync origin
+ {errInvalidBody, false}, // A bad peer was detected, but not the sync origin
+ {errInvalidReceipt, false}, // A bad peer was detected, but not the sync origin
+ {errCancelHashFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
+ {errCancelBlockFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
+ {errCancelHeaderFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
+ {errCancelBodyFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
+ {errCancelReceiptFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
+ {errCancelProcessing, false}, // Synchronisation was canceled, origin may be innocent, don't drop
}
// Run the tests and check disconnection status
tester := newTester()
@@ -1261,6 +1273,8 @@ func TestSyncProgress64Fast(t *testing.T) { testSyncProgress(t, 64, FastSync) }
func TestSyncProgress64Light(t *testing.T) { testSyncProgress(t, 64, LightSync) }
func testSyncProgress(t *testing.T, protocol int, mode SyncMode) {
+ t.Parallel()
+
// Create a small enough block chain to download
targetBlocks := blockCacheLimit - 15
hashes, headers, blocks, receipts := makeChain(targetBlocks, 0, genesis, nil)
@@ -1331,6 +1345,8 @@ func TestForkedSyncProgress64Fast(t *testing.T) { testForkedSyncProgress(t, 64,
func TestForkedSyncProgress64Light(t *testing.T) { testForkedSyncProgress(t, 64, LightSync) }
func testForkedSyncProgress(t *testing.T, protocol int, mode SyncMode) {
+ t.Parallel()
+
// Create a forked chain to simulate origin revertal
common, fork := MaxHashFetch, 2*MaxHashFetch
hashesA, hashesB, headersA, headersB, blocksA, blocksB, receiptsA, receiptsB := makeChainFork(common+fork, fork, genesis, nil)
@@ -1404,6 +1420,8 @@ func TestFailedSyncProgress64Fast(t *testing.T) { testFailedSyncProgress(t, 64,
func TestFailedSyncProgress64Light(t *testing.T) { testFailedSyncProgress(t, 64, LightSync) }
func testFailedSyncProgress(t *testing.T, protocol int, mode SyncMode) {
+ t.Parallel()
+
// Create a small enough block chain to download
targetBlocks := blockCacheLimit - 15
hashes, headers, blocks, receipts := makeChain(targetBlocks, 0, genesis, nil)
@@ -1478,6 +1496,8 @@ func TestFakedSyncProgress64Fast(t *testing.T) { testFakedSyncProgress(t, 64, F
func TestFakedSyncProgress64Light(t *testing.T) { testFakedSyncProgress(t, 64, LightSync) }
func testFakedSyncProgress(t *testing.T, protocol int, mode SyncMode) {
+ t.Parallel()
+
// Create a small block chain
targetBlocks := blockCacheLimit - 15
hashes, headers, blocks, receipts := makeChain(targetBlocks+3, 0, genesis, nil)
@@ -1541,3 +1561,50 @@ func testFakedSyncProgress(t *testing.T, protocol int, mode SyncMode) {
t.Fatalf("Final progress mismatch: have %v/%v/%v, want 0-%v/%v/%v", origin, current, latest, targetBlocks, targetBlocks, targetBlocks)
}
}
+
+// This test reproduces an issue where unexpected deliveries would
+// block indefinitely if they arrived at the right time.
+func TestDeliverHeadersHang62(t *testing.T) { testDeliverHeadersHang(t, 62, FullSync) }
+func TestDeliverHeadersHang63Full(t *testing.T) { testDeliverHeadersHang(t, 63, FullSync) }
+func TestDeliverHeadersHang63Fast(t *testing.T) { testDeliverHeadersHang(t, 63, FastSync) }
+func TestDeliverHeadersHang64Full(t *testing.T) { testDeliverHeadersHang(t, 64, FullSync) }
+func TestDeliverHeadersHang64Fast(t *testing.T) { testDeliverHeadersHang(t, 64, FastSync) }
+func TestDeliverHeadersHang64Light(t *testing.T) { testDeliverHeadersHang(t, 64, LightSync) }
+
+func testDeliverHeadersHang(t *testing.T, protocol int, mode SyncMode) {
+ t.Parallel()
+ hashes, headers, blocks, receipts := makeChain(5, 0, genesis, nil)
+ fakeHeads := []*types.Header{{}, {}, {}, {}}
+ for i := 0; i < 200; i++ {
+ tester := newTester()
+ tester.newPeer("peer", protocol, hashes, headers, blocks, receipts)
+ // Whenever the downloader requests headers, flood it with
+ // a lot of unrequested header deliveries.
+ tester.downloader.peers.peers["peer"].getAbsHeaders = func(from uint64, count, skip int, reverse bool) error {
+ deliveriesDone := make(chan struct{}, 500)
+ for i := 0; i < cap(deliveriesDone); i++ {
+ peer := fmt.Sprintf("fake-peer%d", i)
+ go func() {
+ tester.downloader.DeliverHeaders(peer, fakeHeads)
+ deliveriesDone <- struct{}{}
+ }()
+ }
+ // Deliver the actual requested headers.
+ impl := tester.peerGetAbsHeadersFn("peer", 0)
+ go impl(from, count, skip, reverse)
+ // None of the extra deliveries should block.
+ timeout := time.After(5 * time.Second)
+ for i := 0; i < cap(deliveriesDone); i++ {
+ select {
+ case <-deliveriesDone:
+ case <-timeout:
+ panic("blocked")
+ }
+ }
+ return nil
+ }
+ if err := tester.sync("peer", nil, mode); err != nil {
+ t.Errorf("sync failed: %v", err)
+ }
+ }
+}
diff --git a/eth/downloader/peer.go b/eth/downloader/peer.go
index 1f457cb15..80f08b68f 100644
--- a/eth/downloader/peer.go
+++ b/eth/downloader/peer.go
@@ -28,7 +28,11 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
- "gopkg.in/fatih/set.v0"
+)
+
+const (
+ maxLackingHashes = 4096 // Maximum number of entries allowed on the list or lacking items
+ throughputImpact = 0.1 // The impact a single measurement has on a peer's final throughput value.
)
// Hash and block fetchers belonging to eth/61 and below
@@ -57,17 +61,16 @@ type peer struct {
blockIdle int32 // Current block activity state of the peer (idle = 0, active = 1)
receiptIdle int32 // Current receipt activity state of the peer (idle = 0, active = 1)
stateIdle int32 // Current node data activity state of the peer (idle = 0, active = 1)
- rep int32 // Simple peer reputation
- blockCapacity int32 // Number of blocks (bodies) allowed to fetch per request
- receiptCapacity int32 // Number of receipts allowed to fetch per request
- stateCapacity int32 // Number of node data pieces allowed to fetch per request
+ blockThroughput float64 // Number of blocks (bodies) measured to be retrievable per second
+ receiptThroughput float64 // Number of receipts measured to be retrievable per second
+ stateThroughput float64 // Number of node data pieces measured to be retrievable per second
blockStarted time.Time // Time instance when the last block (body)fetch was started
receiptStarted time.Time // Time instance when the last receipt fetch was started
stateStarted time.Time // Time instance when the last node data fetch was started
- ignored *set.Set // Set of hashes not to request (didn't have previously)
+ lacking map[common.Hash]struct{} // Set of hashes not to request (didn't have previously)
getRelHashes relativeHashFetcherFn // [eth/61] Method to retrieve a batch of hashes from an origin hash
getAbsHashes absoluteHashFetcherFn // [eth/61] Method to retrieve a batch of hashes from an absolute position
@@ -81,6 +84,7 @@ type peer struct {
getNodeData stateFetcherFn // [eth/63] Method to retrieve a batch of state trie data
version int // Eth protocol version number to switch strategies
+ lock sync.RWMutex
}
// newPeer create a new downloader peer, with specific hash and block retrieval
@@ -90,12 +94,9 @@ func newPeer(id string, version int, head common.Hash,
getRelHeaders relativeHeaderFetcherFn, getAbsHeaders absoluteHeaderFetcherFn, getBlockBodies blockBodyFetcherFn,
getReceipts receiptFetcherFn, getNodeData stateFetcherFn) *peer {
return &peer{
- id: id,
- head: head,
- blockCapacity: 1,
- receiptCapacity: 1,
- stateCapacity: 1,
- ignored: set.New(),
+ id: id,
+ head: head,
+ lacking: make(map[common.Hash]struct{}),
getRelHashes: getRelHashes,
getAbsHashes: getAbsHashes,
@@ -114,12 +115,18 @@ func newPeer(id string, version int, head common.Hash,
// Reset clears the internal state of a peer entity.
func (p *peer) Reset() {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
atomic.StoreInt32(&p.blockIdle, 0)
atomic.StoreInt32(&p.receiptIdle, 0)
- atomic.StoreInt32(&p.blockCapacity, 1)
- atomic.StoreInt32(&p.receiptCapacity, 1)
- atomic.StoreInt32(&p.stateCapacity, 1)
- p.ignored.Clear()
+ atomic.StoreInt32(&p.stateIdle, 0)
+
+ p.blockThroughput = 0
+ p.receiptThroughput = 0
+ p.stateThroughput = 0
+
+ p.lacking = make(map[common.Hash]struct{})
}
// Fetch61 sends a block retrieval request to the remote peer.
@@ -210,108 +217,116 @@ func (p *peer) FetchNodeData(request *fetchRequest) error {
return nil
}
-// SetBlocksIdle sets the peer to idle, allowing it to execute new retrieval requests.
-// Its block retrieval allowance will also be updated either up- or downwards,
-// depending on whether the previous fetch completed in time.
-func (p *peer) SetBlocksIdle() {
- p.setIdle(p.blockStarted, blockSoftTTL, blockHardTTL, MaxBlockFetch, &p.blockCapacity, &p.blockIdle)
+// SetBlocksIdle sets the peer to idle, allowing it to execute new block retrieval
+// requests. Its estimated block retrieval throughput is updated with that measured
+// just now.
+func (p *peer) SetBlocksIdle(delivered int) {
+ p.setIdle(p.blockStarted, delivered, &p.blockThroughput, &p.blockIdle)
}
-// SetBodiesIdle sets the peer to idle, allowing it to execute new retrieval requests.
-// Its block body retrieval allowance will also be updated either up- or downwards,
-// depending on whether the previous fetch completed in time.
-func (p *peer) SetBodiesIdle() {
- p.setIdle(p.blockStarted, bodySoftTTL, bodyHardTTL, MaxBodyFetch, &p.blockCapacity, &p.blockIdle)
+// SetBodiesIdle sets the peer to idle, allowing it to execute block body retrieval
+// requests. Its estimated body retrieval throughput is updated with that measured
+// just now.
+func (p *peer) SetBodiesIdle(delivered int) {
+ p.setIdle(p.blockStarted, delivered, &p.blockThroughput, &p.blockIdle)
}
-// SetReceiptsIdle sets the peer to idle, allowing it to execute new retrieval requests.
-// Its receipt retrieval allowance will also be updated either up- or downwards,
-// depending on whether the previous fetch completed in time.
-func (p *peer) SetReceiptsIdle() {
- p.setIdle(p.receiptStarted, receiptSoftTTL, receiptHardTTL, MaxReceiptFetch, &p.receiptCapacity, &p.receiptIdle)
+// SetReceiptsIdle sets the peer to idle, allowing it to execute new receipt
+// retrieval requests. Its estimated receipt retrieval throughput is updated
+// with that measured just now.
+func (p *peer) SetReceiptsIdle(delivered int) {
+ p.setIdle(p.receiptStarted, delivered, &p.receiptThroughput, &p.receiptIdle)
}
-// SetNodeDataIdle sets the peer to idle, allowing it to execute new retrieval
-// requests. Its node data retrieval allowance will also be updated either up- or
-// downwards, depending on whether the previous fetch completed in time.
-func (p *peer) SetNodeDataIdle() {
- p.setIdle(p.stateStarted, stateSoftTTL, stateSoftTTL, MaxStateFetch, &p.stateCapacity, &p.stateIdle)
+// SetNodeDataIdle sets the peer to idle, allowing it to execute new state trie
+// data retrieval requests. Its estimated state retrieval throughput is updated
+// with that measured just now.
+func (p *peer) SetNodeDataIdle(delivered int) {
+ p.setIdle(p.stateStarted, delivered, &p.stateThroughput, &p.stateIdle)
}
// setIdle sets the peer to idle, allowing it to execute new retrieval requests.
-// Its data retrieval allowance will also be updated either up- or downwards,
-// depending on whether the previous fetch completed in time.
-func (p *peer) setIdle(started time.Time, softTTL, hardTTL time.Duration, maxFetch int, capacity, idle *int32) {
- // Update the peer's download allowance based on previous performance
- scale := 2.0
- if time.Since(started) > softTTL {
- scale = 0.5
- if time.Since(started) > hardTTL {
- scale = 1 / float64(maxFetch) // reduces capacity to 1
- }
+// Its estimated retrieval throughput is updated with that measured just now.
+func (p *peer) setIdle(started time.Time, delivered int, throughput *float64, idle *int32) {
+ // Irrelevant of the scaling, make sure the peer ends up idle
+ defer atomic.StoreInt32(idle, 0)
+
+ p.lock.RLock()
+ defer p.lock.RUnlock()
+
+ // If nothing was delivered (hard timeout / unavailable data), reduce throughput to minimum
+ if delivered == 0 {
+ *throughput = 0
+ return
}
- for {
- // Calculate the new download bandwidth allowance
- prev := atomic.LoadInt32(capacity)
- next := int32(math.Max(1, math.Min(float64(maxFetch), float64(prev)*scale)))
-
- // Try to update the old value
- if atomic.CompareAndSwapInt32(capacity, prev, next) {
- // If we're having problems at 1 capacity, try to find better peers
- if next == 1 {
- p.Demote()
- }
- break
- }
- }
- // Set the peer to idle to allow further fetch requests
- atomic.StoreInt32(idle, 0)
+ // Otherwise update the throughput with a new measurement
+ measured := float64(delivered) / (float64(time.Since(started)+1) / float64(time.Second)) // +1 (ns) to ensure non-zero divisor
+ *throughput = (1-throughputImpact)*(*throughput) + throughputImpact*measured
}
// BlockCapacity retrieves the peers block download allowance based on its
-// previously discovered bandwidth capacity.
+// previously discovered throughput.
func (p *peer) BlockCapacity() int {
- return int(atomic.LoadInt32(&p.blockCapacity))
+ p.lock.RLock()
+ defer p.lock.RUnlock()
+
+ return int(math.Max(1, math.Min(p.blockThroughput*float64(blockTargetRTT)/float64(time.Second), float64(MaxBlockFetch))))
}
-// ReceiptCapacity retrieves the peers block download allowance based on its
-// previously discovered bandwidth capacity.
+// ReceiptCapacity retrieves the peers receipt download allowance based on its
+// previously discovered throughput.
func (p *peer) ReceiptCapacity() int {
- return int(atomic.LoadInt32(&p.receiptCapacity))
+ p.lock.RLock()
+ defer p.lock.RUnlock()
+
+ return int(math.Max(1, math.Min(p.receiptThroughput*float64(receiptTargetRTT)/float64(time.Second), float64(MaxReceiptFetch))))
}
-// NodeDataCapacity retrieves the peers block download allowance based on its
-// previously discovered bandwidth capacity.
+// NodeDataCapacity retrieves the peers state download allowance based on its
+// previously discovered throughput.
func (p *peer) NodeDataCapacity() int {
- return int(atomic.LoadInt32(&p.stateCapacity))
-}
+ p.lock.RLock()
+ defer p.lock.RUnlock()
-// Promote increases the peer's reputation.
-func (p *peer) Promote() {
- atomic.AddInt32(&p.rep, 1)
+ return int(math.Max(1, math.Min(p.stateThroughput*float64(stateTargetRTT)/float64(time.Second), float64(MaxStateFetch))))
}
-// Demote decreases the peer's reputation or leaves it at 0.
-func (p *peer) Demote() {
- for {
- // Calculate the new reputation value
- prev := atomic.LoadInt32(&p.rep)
- next := prev / 2
+// MarkLacking appends a new entity to the set of items (blocks, receipts, states)
+// that a peer is known not to have (i.e. have been requested before). If the
+// set reaches its maximum allowed capacity, items are randomly dropped off.
+func (p *peer) MarkLacking(hash common.Hash) {
+ p.lock.Lock()
+ defer p.lock.Unlock()
- // Try to update the old value
- if atomic.CompareAndSwapInt32(&p.rep, prev, next) {
- return
+ for len(p.lacking) >= maxLackingHashes {
+ for drop, _ := range p.lacking {
+ delete(p.lacking, drop)
+ break
}
}
+ p.lacking[hash] = struct{}{}
+}
+
+// Lacks retrieves whether the hash of a blockchain item is on the peers lacking
+// list (i.e. whether we know that the peer does not have it).
+func (p *peer) Lacks(hash common.Hash) bool {
+ p.lock.RLock()
+ defer p.lock.RUnlock()
+
+ _, ok := p.lacking[hash]
+ return ok
}
// String implements fmt.Stringer.
func (p *peer) String() string {
+ p.lock.RLock()
+ defer p.lock.RUnlock()
+
return fmt.Sprintf("Peer %s [%s]", p.id,
- fmt.Sprintf("reputation %3d, ", atomic.LoadInt32(&p.rep))+
- fmt.Sprintf("block cap %3d, ", atomic.LoadInt32(&p.blockCapacity))+
- fmt.Sprintf("receipt cap %3d, ", atomic.LoadInt32(&p.receiptCapacity))+
- fmt.Sprintf("ignored %4d", p.ignored.Size()),
+ fmt.Sprintf("blocks %3.2f/s, ", p.blockThroughput)+
+ fmt.Sprintf("receipts %3.2f/s, ", p.receiptThroughput)+
+ fmt.Sprintf("states %3.2f/s, ", p.stateThroughput)+
+ fmt.Sprintf("lacking %4d", len(p.lacking)),
)
}
@@ -342,6 +357,10 @@ func (ps *peerSet) Reset() {
// Register injects a new peer into the working set, or returns an error if the
// peer is already known.
+//
+// The method also sets the starting throughput values of the new peer to the
+// average of all existing peers, to give it a realistic change of being used
+// for data retrievals.
func (ps *peerSet) Register(p *peer) error {
ps.lock.Lock()
defer ps.lock.Unlock()
@@ -349,6 +368,20 @@ func (ps *peerSet) Register(p *peer) error {
if _, ok := ps.peers[p.id]; ok {
return errAlreadyRegistered
}
+ if len(ps.peers) > 0 {
+ p.blockThroughput, p.receiptThroughput, p.stateThroughput = 0, 0, 0
+
+ for _, peer := range ps.peers {
+ peer.lock.RLock()
+ p.blockThroughput += peer.blockThroughput
+ p.receiptThroughput += peer.receiptThroughput
+ p.stateThroughput += peer.stateThroughput
+ peer.lock.RUnlock()
+ }
+ p.blockThroughput /= float64(len(ps.peers))
+ p.receiptThroughput /= float64(len(ps.peers))
+ p.stateThroughput /= float64(len(ps.peers))
+ }
ps.peers[p.id] = p
return nil
}
@@ -400,7 +433,12 @@ func (ps *peerSet) BlockIdlePeers() ([]*peer, int) {
idle := func(p *peer) bool {
return atomic.LoadInt32(&p.blockIdle) == 0
}
- return ps.idlePeers(61, 61, idle)
+ throughput := func(p *peer) float64 {
+ p.lock.RLock()
+ defer p.lock.RUnlock()
+ return p.blockThroughput
+ }
+ return ps.idlePeers(61, 61, idle, throughput)
}
// BodyIdlePeers retrieves a flat list of all the currently body-idle peers within
@@ -409,7 +447,12 @@ func (ps *peerSet) BodyIdlePeers() ([]*peer, int) {
idle := func(p *peer) bool {
return atomic.LoadInt32(&p.blockIdle) == 0
}
- return ps.idlePeers(62, 64, idle)
+ throughput := func(p *peer) float64 {
+ p.lock.RLock()
+ defer p.lock.RUnlock()
+ return p.blockThroughput
+ }
+ return ps.idlePeers(62, 64, idle, throughput)
}
// ReceiptIdlePeers retrieves a flat list of all the currently receipt-idle peers
@@ -418,7 +461,12 @@ func (ps *peerSet) ReceiptIdlePeers() ([]*peer, int) {
idle := func(p *peer) bool {
return atomic.LoadInt32(&p.receiptIdle) == 0
}
- return ps.idlePeers(63, 64, idle)
+ throughput := func(p *peer) float64 {
+ p.lock.RLock()
+ defer p.lock.RUnlock()
+ return p.receiptThroughput
+ }
+ return ps.idlePeers(63, 64, idle, throughput)
}
// NodeDataIdlePeers retrieves a flat list of all the currently node-data-idle
@@ -427,12 +475,18 @@ func (ps *peerSet) NodeDataIdlePeers() ([]*peer, int) {
idle := func(p *peer) bool {
return atomic.LoadInt32(&p.stateIdle) == 0
}
- return ps.idlePeers(63, 64, idle)
+ throughput := func(p *peer) float64 {
+ p.lock.RLock()
+ defer p.lock.RUnlock()
+ return p.stateThroughput
+ }
+ return ps.idlePeers(63, 64, idle, throughput)
}
// idlePeers retrieves a flat list of all currently idle peers satisfying the
// protocol version constraints, using the provided function to check idleness.
-func (ps *peerSet) idlePeers(minProtocol, maxProtocol int, idleCheck func(*peer) bool) ([]*peer, int) {
+// The resulting set of peers are sorted by their measure throughput.
+func (ps *peerSet) idlePeers(minProtocol, maxProtocol int, idleCheck func(*peer) bool, throughput func(*peer) float64) ([]*peer, int) {
ps.lock.RLock()
defer ps.lock.RUnlock()
@@ -447,7 +501,7 @@ func (ps *peerSet) idlePeers(minProtocol, maxProtocol int, idleCheck func(*peer)
}
for i := 0; i < len(idle); i++ {
for j := i + 1; j < len(idle); j++ {
- if atomic.LoadInt32(&idle[i].rep) < atomic.LoadInt32(&idle[j].rep) {
+ if throughput(idle[i]) < throughput(idle[j]) {
idle[i], idle[j] = idle[j], idle[i]
}
}
diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go
index 56b46e285..1e55560db 100644
--- a/eth/downloader/queue.go
+++ b/eth/downloader/queue.go
@@ -101,11 +101,14 @@ type queue struct {
resultCache []*fetchResult // Downloaded but not yet delivered fetch results
resultOffset uint64 // Offset of the first cached fetch result in the block chain
- lock sync.RWMutex
+ lock *sync.Mutex
+ active *sync.Cond
+ closed bool
}
// newQueue creates a new download queue for scheduling block retrieval.
func newQueue(stateDb ethdb.Database) *queue {
+ lock := new(sync.Mutex)
return &queue{
hashPool: make(map[common.Hash]int),
hashQueue: prque.New(),
@@ -122,6 +125,8 @@ func newQueue(stateDb ethdb.Database) *queue {
statePendPool: make(map[string]*fetchRequest),
stateDatabase: stateDb,
resultCache: make([]*fetchResult, blockCacheLimit),
+ active: sync.NewCond(lock),
+ lock: lock,
}
}
@@ -133,6 +138,7 @@ func (q *queue) Reset() {
q.stateSchedLock.Lock()
defer q.stateSchedLock.Unlock()
+ q.closed = false
q.mode = FullSync
q.fastSyncPivot = 0
@@ -162,18 +168,27 @@ func (q *queue) Reset() {
q.resultOffset = 0
}
+// Close marks the end of the sync, unblocking WaitResults.
+// It may be called even if the queue is already closed.
+func (q *queue) Close() {
+ q.lock.Lock()
+ q.closed = true
+ q.lock.Unlock()
+ q.active.Broadcast()
+}
+
// PendingBlocks retrieves the number of block (body) requests pending for retrieval.
func (q *queue) PendingBlocks() int {
- q.lock.RLock()
- defer q.lock.RUnlock()
+ q.lock.Lock()
+ defer q.lock.Unlock()
return q.hashQueue.Size() + q.blockTaskQueue.Size()
}
// PendingReceipts retrieves the number of block receipts pending for retrieval.
func (q *queue) PendingReceipts() int {
- q.lock.RLock()
- defer q.lock.RUnlock()
+ q.lock.Lock()
+ defer q.lock.Unlock()
return q.receiptTaskQueue.Size()
}
@@ -192,8 +207,8 @@ func (q *queue) PendingNodeData() int {
// InFlightBlocks retrieves whether there are block fetch requests currently in
// flight.
func (q *queue) InFlightBlocks() bool {
- q.lock.RLock()
- defer q.lock.RUnlock()
+ q.lock.Lock()
+ defer q.lock.Unlock()
return len(q.blockPendPool) > 0
}
@@ -201,8 +216,8 @@ func (q *queue) InFlightBlocks() bool {
// InFlightReceipts retrieves whether there are receipt fetch requests currently
// in flight.
func (q *queue) InFlightReceipts() bool {
- q.lock.RLock()
- defer q.lock.RUnlock()
+ q.lock.Lock()
+ defer q.lock.Unlock()
return len(q.receiptPendPool) > 0
}
@@ -210,8 +225,8 @@ func (q *queue) InFlightReceipts() bool {
// InFlightNodeData retrieves whether there are node data entry fetch requests
// currently in flight.
func (q *queue) InFlightNodeData() bool {
- q.lock.RLock()
- defer q.lock.RUnlock()
+ q.lock.Lock()
+ defer q.lock.Unlock()
return len(q.statePendPool)+int(atomic.LoadInt32(&q.stateProcessors)) > 0
}
@@ -219,8 +234,8 @@ func (q *queue) InFlightNodeData() bool {
// Idle returns if the queue is fully idle or has some data still inside. This
// method is used by the tester to detect termination events.
func (q *queue) Idle() bool {
- q.lock.RLock()
- defer q.lock.RUnlock()
+ q.lock.Lock()
+ defer q.lock.Unlock()
queued := q.hashQueue.Size() + q.blockTaskQueue.Size() + q.receiptTaskQueue.Size() + q.stateTaskQueue.Size()
pending := len(q.blockPendPool) + len(q.receiptPendPool) + len(q.statePendPool)
@@ -237,8 +252,8 @@ func (q *queue) Idle() bool {
// FastSyncPivot retrieves the currently used fast sync pivot point.
func (q *queue) FastSyncPivot() uint64 {
- q.lock.RLock()
- defer q.lock.RUnlock()
+ q.lock.Lock()
+ defer q.lock.Unlock()
return q.fastSyncPivot
}
@@ -246,8 +261,8 @@ func (q *queue) FastSyncPivot() uint64 {
// ShouldThrottleBlocks checks if the download should be throttled (active block (body)
// fetches exceed block cache).
func (q *queue) ShouldThrottleBlocks() bool {
- q.lock.RLock()
- defer q.lock.RUnlock()
+ q.lock.Lock()
+ defer q.lock.Unlock()
// Calculate the currently in-flight block (body) requests
pending := 0
@@ -261,8 +276,8 @@ func (q *queue) ShouldThrottleBlocks() bool {
// ShouldThrottleReceipts checks if the download should be throttled (active receipt
// fetches exceed block cache).
func (q *queue) ShouldThrottleReceipts() bool {
- q.lock.RLock()
- defer q.lock.RUnlock()
+ q.lock.Lock()
+ defer q.lock.Unlock()
// Calculate the currently in-flight receipt requests
pending := 0
@@ -351,91 +366,74 @@ func (q *queue) Schedule(headers []*types.Header, from uint64) []*types.Header {
return inserts
}
-// GetHeadResult retrieves the first fetch result from the cache, or nil if it hasn't
-// been downloaded yet (or simply non existent).
-func (q *queue) GetHeadResult() *fetchResult {
- q.lock.RLock()
- defer q.lock.RUnlock()
+// WaitResults retrieves and permanently removes a batch of fetch
+// results from the cache. the result slice will be empty if the queue
+// has been closed.
+func (q *queue) WaitResults() []*fetchResult {
+ q.lock.Lock()
+ defer q.lock.Unlock()
- // If there are no results pending, return nil
- if len(q.resultCache) == 0 || q.resultCache[0] == nil {
- return nil
- }
- // If the next result is still incomplete, return nil
- if q.resultCache[0].Pending > 0 {
- return nil
+ nproc := q.countProcessableItems()
+ for nproc == 0 && !q.closed {
+ q.active.Wait()
+ nproc = q.countProcessableItems()
}
- // If the next result is the fast sync pivot...
- if q.mode == FastSync && q.resultCache[0].Header.Number.Uint64() == q.fastSyncPivot {
- // If the pivot state trie is still being pulled, return nil
- if len(q.stateTaskPool) > 0 {
- return nil
+ results := make([]*fetchResult, nproc)
+ copy(results, q.resultCache[:nproc])
+ if len(results) > 0 {
+ // Mark results as done before dropping them from the cache.
+ for _, result := range results {
+ hash := result.Header.Hash()
+ delete(q.blockDonePool, hash)
+ delete(q.receiptDonePool, hash)
}
- if q.PendingNodeData() > 0 {
- return nil
- }
- // If the state is done, but not enough post-pivot headers were verified, stall...
- for i := 0; i < fsHeaderForceVerify; i++ {
- if i+1 >= len(q.resultCache) || q.resultCache[i+1] == nil {
- return nil
- }
+ // Delete the results from the cache and clear the tail.
+ copy(q.resultCache, q.resultCache[nproc:])
+ for i := len(q.resultCache) - nproc; i < len(q.resultCache); i++ {
+ q.resultCache[i] = nil
}
+ // Advance the expected block number of the first cache entry.
+ q.resultOffset += uint64(nproc)
}
- return q.resultCache[0]
+ return results
}
-// TakeResults retrieves and permanently removes a batch of fetch results from
-// the cache.
-func (q *queue) TakeResults() []*fetchResult {
- q.lock.Lock()
- defer q.lock.Unlock()
-
- // Accumulate all available results
- results := []*fetchResult{}
+// countProcessableItems counts the processable items.
+func (q *queue) countProcessableItems() int {
for i, result := range q.resultCache {
- // Stop if no more results are ready
+ // Don't process incomplete or unavailable items.
if result == nil || result.Pending > 0 {
- break
+ return i
}
- // The fast sync pivot block may only be processed after state fetch completes
- if q.mode == FastSync && result.Header.Number.Uint64() == q.fastSyncPivot {
- if len(q.stateTaskPool) > 0 {
- break
- }
- if q.PendingNodeData() > 0 {
- break
- }
- // Even is state fetch is done, ensure post-pivot headers passed verifications
- safe := true
- for j := 0; j < fsHeaderForceVerify; j++ {
- if i+j+1 >= len(q.resultCache) || q.resultCache[i+j+1] == nil {
- safe = false
+ // Special handling for the fast-sync pivot block:
+ if q.mode == FastSync {
+ bnum := result.Header.Number.Uint64()
+ if bnum == q.fastSyncPivot {
+ // If the state of the pivot block is not
+ // available yet, we cannot proceed and return 0.
+ //
+ // Stop before processing the pivot block to ensure that
+ // resultCache has space for fsHeaderForceVerify items. Not
+ // doing this could leave us unable to download the required
+ // amount of headers.
+ if i > 0 || len(q.stateTaskPool) > 0 || q.PendingNodeData() > 0 {
+ return i
+ }
+ for j := 0; j < fsHeaderForceVerify; j++ {
+ if i+j+1 >= len(q.resultCache) || q.resultCache[i+j+1] == nil {
+ return i
+ }
}
}
- if !safe {
- break
+ // If we're just the fast sync pivot, stop as well
+ // because the following batch needs different insertion.
+ // This simplifies handling the switchover in d.process.
+ if bnum == q.fastSyncPivot+1 && i > 0 {
+ return i
}
}
- // If we've just inserted the fast sync pivot, stop as the following batch needs different insertion
- if q.mode == FastSync && result.Header.Number.Uint64() == q.fastSyncPivot+1 && len(results) > 0 {
- break
- }
- results = append(results, result)
-
- hash := result.Header.Hash()
- delete(q.blockDonePool, hash)
- delete(q.receiptDonePool, hash)
- }
- // Delete the results from the slice and let them be garbage collected
- // without this slice trick the results would stay in memory until nil
- // would be assigned to them.
- copy(q.resultCache, q.resultCache[len(results):])
- for k, n := len(q.resultCache)-len(results), len(q.resultCache); k < n; k++ {
- q.resultCache[k] = nil
}
- q.resultOffset += uint64(len(results))
-
- return results
+ return len(q.resultCache)
}
// ReserveBlocks reserves a set of block hashes for the given peer, skipping any
@@ -501,7 +499,7 @@ func (q *queue) reserveHashes(p *peer, count int, taskQueue *prque.Prque, taskGe
for proc := 0; (allowance == 0 || proc < allowance) && len(send) < count && !taskQueue.Empty(); proc++ {
hash, priority := taskQueue.Pop()
- if p.ignored.Has(hash) {
+ if p.Lacks(hash.(common.Hash)) {
skip[hash.(common.Hash)] = int(priority)
} else {
send[hash.(common.Hash)] = int(priority)
@@ -584,6 +582,7 @@ func (q *queue) reserveHeaders(p *peer, count int, taskPool map[common.Hash]*typ
// If we're the first to request this task, initialise the result container
index := int(header.Number.Int64() - int64(q.resultOffset))
if index >= len(q.resultCache) || index < 0 {
+ common.Report("index allocation went beyond available resultCache space")
return nil, false, errInvalidChain
}
if q.resultCache[index] == nil {
@@ -607,7 +606,7 @@ func (q *queue) reserveHeaders(p *peer, count int, taskPool map[common.Hash]*typ
continue
}
// Otherwise unless the peer is known not to have the data, add to the retrieve list
- if p.ignored.Has(header.Hash()) {
+ if p.Lacks(header.Hash()) {
skip = append(skip, header)
} else {
send = append(send, header)
@@ -617,6 +616,10 @@ func (q *queue) reserveHeaders(p *peer, count int, taskPool map[common.Hash]*typ
for _, header := range skip {
taskQueue.Push(header, -float32(header.Number.Uint64()))
}
+ if progress {
+ // Wake WaitResults, resultCache was modified
+ q.active.Signal()
+ }
// Assemble and return the block download request
if len(send) == 0 {
return nil, progress, nil
@@ -700,7 +703,7 @@ func (q *queue) Revoke(peerId string) {
// ExpireBlocks checks for in flight requests that exceeded a timeout allowance,
// canceling them and returning the responsible peers for penalisation.
-func (q *queue) ExpireBlocks(timeout time.Duration) []string {
+func (q *queue) ExpireBlocks(timeout time.Duration) map[string]int {
q.lock.Lock()
defer q.lock.Unlock()
@@ -709,7 +712,7 @@ func (q *queue) ExpireBlocks(timeout time.Duration) []string {
// ExpireBodies checks for in flight block body requests that exceeded a timeout
// allowance, canceling them and returning the responsible peers for penalisation.
-func (q *queue) ExpireBodies(timeout time.Duration) []string {
+func (q *queue) ExpireBodies(timeout time.Duration) map[string]int {
q.lock.Lock()
defer q.lock.Unlock()
@@ -718,7 +721,7 @@ func (q *queue) ExpireBodies(timeout time.Duration) []string {
// ExpireReceipts checks for in flight receipt requests that exceeded a timeout
// allowance, canceling them and returning the responsible peers for penalisation.
-func (q *queue) ExpireReceipts(timeout time.Duration) []string {
+func (q *queue) ExpireReceipts(timeout time.Duration) map[string]int {
q.lock.Lock()
defer q.lock.Unlock()
@@ -727,7 +730,7 @@ func (q *queue) ExpireReceipts(timeout time.Duration) []string {
// ExpireNodeData checks for in flight node data requests that exceeded a timeout
// allowance, canceling them and returning the responsible peers for penalisation.
-func (q *queue) ExpireNodeData(timeout time.Duration) []string {
+func (q *queue) ExpireNodeData(timeout time.Duration) map[string]int {
q.lock.Lock()
defer q.lock.Unlock()
@@ -737,12 +740,12 @@ func (q *queue) ExpireNodeData(timeout time.Duration) []string {
// expire is the generic check that move expired tasks from a pending pool back
// into a task pool, returning all entities caught with expired tasks.
//
-// Note, this method expects the queue lock to be already held for writing. The
+// Note, this method expects the queue lock to be already held. The
// reason the lock is not obtained in here is because the parameters already need
// to access the queue, so they already need a lock anyway.
-func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest, taskQueue *prque.Prque, timeoutMeter metrics.Meter) []string {
+func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest, taskQueue *prque.Prque, timeoutMeter metrics.Meter) map[string]int {
// Iterate over the expired requests and return each to the queue
- peers := []string{}
+ expiries := make(map[string]int)
for id, request := range pendPool {
if time.Since(request.Time) > timeout {
// Update the metrics with the timeout
@@ -755,25 +758,32 @@ func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest,
for _, header := range request.Headers {
taskQueue.Push(header, -float32(header.Number.Uint64()))
}
- peers = append(peers, id)
+ // Add the peer to the expiry report along the the number of failed requests
+ expirations := len(request.Hashes)
+ if expirations < len(request.Headers) {
+ expirations = len(request.Headers)
+ }
+ expiries[id] = expirations
}
}
// Remove the expired requests from the pending pool
- for _, id := range peers {
+ for id, _ := range expiries {
delete(pendPool, id)
}
- return peers
+ return expiries
}
-// DeliverBlocks injects a block retrieval response into the download queue.
-func (q *queue) DeliverBlocks(id string, blocks []*types.Block) error {
+// DeliverBlocks injects a block retrieval response into the download queue. The
+// method returns the number of blocks accepted from the delivery and also wakes
+// any threads waiting for data delivery.
+func (q *queue) DeliverBlocks(id string, blocks []*types.Block) (int, error) {
q.lock.Lock()
defer q.lock.Unlock()
// Short circuit if the blocks were never requested
request := q.blockPendPool[id]
if request == nil {
- return errNoFetchesPending
+ return 0, errNoFetchesPending
}
blockReqTimer.UpdateSince(request.Time)
delete(q.blockPendPool, id)
@@ -781,11 +791,11 @@ func (q *queue) DeliverBlocks(id string, blocks []*types.Block) error {
// If no blocks were retrieved, mark them as unavailable for the origin peer
if len(blocks) == 0 {
for hash, _ := range request.Hashes {
- request.Peer.ignored.Add(hash)
+ request.Peer.MarkLacking(hash)
}
}
// Iterate over the downloaded blocks and add each of them
- errs := make([]error, 0)
+ accepted, errs := 0, make([]error, 0)
for _, block := range blocks {
// Skip any blocks that were not requested
hash := block.Hash()
@@ -808,29 +818,33 @@ func (q *queue) DeliverBlocks(id string, blocks []*types.Block) error {
delete(request.Hashes, hash)
delete(q.hashPool, hash)
+ accepted++
}
// Return all failed or missing fetches to the queue
for hash, index := range request.Hashes {
q.hashQueue.Push(hash, float32(index))
}
+ // Wake up WaitResults
+ if accepted > 0 {
+ q.active.Signal()
+ }
// If none of the blocks were good, it's a stale delivery
switch {
case len(errs) == 0:
- return nil
-
+ return accepted, nil
case len(errs) == 1 && (errs[0] == errInvalidChain || errs[0] == errInvalidBlock):
- return errs[0]
-
+ return accepted, errs[0]
case len(errs) == len(blocks):
- return errStaleDelivery
-
+ return accepted, errStaleDelivery
default:
- return fmt.Errorf("multiple failures: %v", errs)
+ return accepted, fmt.Errorf("multiple failures: %v", errs)
}
}
// DeliverBodies injects a block body retrieval response into the results queue.
-func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, uncleLists [][]*types.Header) error {
+// The method returns the number of blocks bodies accepted from the delivery and
+// also wakes any threads waiting for data delivery.
+func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, uncleLists [][]*types.Header) (int, error) {
q.lock.Lock()
defer q.lock.Unlock()
@@ -846,7 +860,9 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, uncleLi
}
// DeliverReceipts injects a receipt retrieval response into the results queue.
-func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) error {
+// The method returns the number of transaction receipts accepted from the delivery
+// and also wakes any threads waiting for data delivery.
+func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) (int, error) {
q.lock.Lock()
defer q.lock.Unlock()
@@ -865,26 +881,29 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) error
// Note, this method expects the queue lock to be already held for writing. The
// reason the lock is not obtained in here is because the parameters already need
// to access the queue, so they already need a lock anyway.
-func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque, pendPool map[string]*fetchRequest,
- donePool map[common.Hash]struct{}, reqTimer metrics.Timer, results int, reconstruct func(header *types.Header, index int, result *fetchResult) error) error {
+func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque,
+ pendPool map[string]*fetchRequest, donePool map[common.Hash]struct{}, reqTimer metrics.Timer,
+ results int, reconstruct func(header *types.Header, index int, result *fetchResult) error) (int, error) {
+
// Short circuit if the data was never requested
request := pendPool[id]
if request == nil {
- return errNoFetchesPending
+ return 0, errNoFetchesPending
}
reqTimer.UpdateSince(request.Time)
delete(pendPool, id)
// If no data items were retrieved, mark them as unavailable for the origin peer
if results == 0 {
- for hash, _ := range request.Headers {
- request.Peer.ignored.Add(hash)
+ for _, header := range request.Headers {
+ request.Peer.MarkLacking(header.Hash())
}
}
// Assemble each of the results with their headers and retrieved data parts
var (
- failure error
- useful bool
+ accepted int
+ failure error
+ useful bool
)
for i, header := range request.Headers {
// Short circuit assembly if no more fetch results are found
@@ -904,6 +923,7 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQ
donePool[header.Hash()] = struct{}{}
q.resultCache[index].Pending--
useful = true
+ accepted++
// Clean up a successful fetch
request.Headers[i] = nil
@@ -915,28 +935,32 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQ
taskQueue.Push(header, -float32(header.Number.Uint64()))
}
}
+ // Wake up WaitResults
+ if accepted > 0 {
+ q.active.Signal()
+ }
// If none of the data was good, it's a stale delivery
switch {
case failure == nil || failure == errInvalidChain:
- return failure
-
+ return accepted, failure
case useful:
- return fmt.Errorf("partial failure: %v", failure)
-
+ return accepted, fmt.Errorf("partial failure: %v", failure)
default:
- return errStaleDelivery
+ return accepted, errStaleDelivery
}
}
// DeliverNodeData injects a node state data retrieval response into the queue.
-func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(error, int)) error {
+// The method returns the number of node state entries originally requested, and
+// the number of them actually accepted from the delivery.
+func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(error, int)) (int, error) {
q.lock.Lock()
defer q.lock.Unlock()
// Short circuit if the data was never requested
request := q.statePendPool[id]
if request == nil {
- return errNoFetchesPending
+ return 0, errNoFetchesPending
}
stateReqTimer.UpdateSince(request.Time)
delete(q.statePendPool, id)
@@ -944,14 +968,14 @@ func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(error, i
// If no data was retrieved, mark their hashes as unavailable for the origin peer
if len(data) == 0 {
for hash, _ := range request.Hashes {
- request.Peer.ignored.Add(hash)
+ request.Peer.MarkLacking(hash)
}
}
// Iterate over the downloaded data and verify each of them
- errs := make([]error, 0)
+ accepted, errs := 0, make([]error, 0)
process := []trie.SyncResult{}
for _, blob := range data {
- // Skip any blocks that were not requested
+ // Skip any state trie entires that were not requested
hash := common.BytesToHash(crypto.Sha3(blob))
if _, ok := request.Hashes[hash]; !ok {
errs = append(errs, fmt.Errorf("non-requested state data %x", hash))
@@ -959,6 +983,7 @@ func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(error, i
}
// Inject the next state trie item into the processing queue
process = append(process, trie.SyncResult{hash, blob})
+ accepted++
delete(request.Hashes, hash)
delete(q.stateTaskPool, hash)
@@ -976,19 +1001,21 @@ func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(error, i
// If none of the data items were good, it's a stale delivery
switch {
case len(errs) == 0:
- return nil
-
+ return accepted, nil
case len(errs) == len(request.Hashes):
- return errStaleDelivery
-
+ return accepted, errStaleDelivery
default:
- return fmt.Errorf("multiple failures: %v", errs)
+ return accepted, fmt.Errorf("multiple failures: %v", errs)
}
}
// deliverNodeData is the asynchronous node data processor that injects a batch
// of sync results into the state scheduler.
func (q *queue) deliverNodeData(results []trie.SyncResult, callback func(error, int)) {
+ // Wake up WaitResults after the state has been written because it
+ // might be waiting for the pivot block state to get completed.
+ defer q.active.Signal()
+
// Process results one by one to permit task fetches in between
for i, result := range results {
q.stateSchedLock.Lock()
diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go
index a5418e2e7..5772114b3 100644
--- a/eth/filters/filter_test.go
+++ b/eth/filters/filter_test.go
@@ -64,7 +64,7 @@ func BenchmarkMipmaps(b *testing.B) {
}
// store the receipts
- err := core.PutReceipts(db, receipts)
+ err := core.WriteReceipts(db, receipts)
if err != nil {
b.Fatal(err)
}
@@ -78,7 +78,7 @@ func BenchmarkMipmaps(b *testing.B) {
if err := core.WriteHeadBlockHash(db, block.Hash()); err != nil {
b.Fatalf("failed to insert block number: %v", err)
}
- if err := core.PutBlockReceipts(db, block.Hash(), receipts[i]); err != nil {
+ if err := core.WriteBlockReceipts(db, block.Hash(), receipts[i]); err != nil {
b.Fatal("error writing block receipts:", err)
}
}
@@ -163,7 +163,7 @@ func TestFilters(t *testing.T) {
}
// store the receipts
- err := core.PutReceipts(db, receipts)
+ err := core.WriteReceipts(db, receipts)
if err != nil {
t.Fatal(err)
}
@@ -180,7 +180,7 @@ func TestFilters(t *testing.T) {
if err := core.WriteHeadBlockHash(db, block.Hash()); err != nil {
t.Fatalf("failed to insert block number: %v", err)
}
- if err := core.PutBlockReceipts(db, block.Hash(), receipts[i]); err != nil {
+ if err := core.WriteBlockReceipts(db, block.Hash(), receipts[i]); err != nil {
t.Fatal("error writing block receipts:", err)
}
}
diff --git a/eth/gasprice.go b/eth/gasprice.go
index b752c22dd..e0de89e62 100644
--- a/eth/gasprice.go
+++ b/eth/gasprice.go
@@ -166,7 +166,7 @@ func (self *GasPriceOracle) processBlock(block *types.Block) {
func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int {
gasUsed := big.NewInt(0)
- receipts := self.eth.BlockProcessor().GetBlockReceipts(block.Hash())
+ receipts := core.GetBlockReceipts(self.eth.ChainDb(), block.Hash())
if len(receipts) > 0 {
if cgu := receipts[len(receipts)-1].CumulativeGasUsed; cgu != nil {
gasUsed = receipts[len(receipts)-1].CumulativeGasUsed
diff --git a/eth/handler.go b/eth/handler.go
index 7dc7de80e..d8c5b4b64 100644
--- a/eth/handler.go
+++ b/eth/handler.go
@@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/p2p"
+ "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -55,6 +56,8 @@ type hashFetcherFn func(common.Hash) error
type blockFetcherFn func([]common.Hash) error
type ProtocolManager struct {
+ networkId int
+
fastSync bool
txpool txPool
blockchain *core.BlockChain
@@ -91,6 +94,7 @@ func NewProtocolManager(fastSync bool, networkId int, mux *event.TypeMux, txpool
}
// Create the protocol manager with the base fields
manager := &ProtocolManager{
+ networkId: networkId,
fastSync: fastSync,
eventMux: mux,
txpool: txpool,
@@ -111,14 +115,23 @@ func NewProtocolManager(fastSync bool, networkId int, mux *event.TypeMux, txpool
// Compatible; initialise the sub-protocol
version := version // Closure for the run
manager.SubProtocols = append(manager.SubProtocols, p2p.Protocol{
- Name: "eth",
+ Name: ProtocolName,
Version: version,
Length: ProtocolLengths[i],
Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
- peer := manager.newPeer(int(version), networkId, p, rw)
+ peer := manager.newPeer(int(version), p, rw)
manager.newPeerCh <- peer
return manager.handle(peer)
},
+ NodeInfo: func() interface{} {
+ return manager.NodeInfo()
+ },
+ PeerInfo: func(id discover.NodeID) interface{} {
+ if p := manager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil {
+ return p.Info()
+ }
+ return nil
+ },
})
}
if len(manager.SubProtocols) == 0 {
@@ -188,8 +201,8 @@ func (pm *ProtocolManager) Stop() {
glog.V(logger.Info).Infoln("Ethereum protocol handler stopped")
}
-func (pm *ProtocolManager) newPeer(pv, nv int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
- return newPeer(pv, nv, p, newMeteredMsgWriter(rw))
+func (pm *ProtocolManager) newPeer(pv int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
+ return newPeer(pv, p, newMeteredMsgWriter(rw))
}
// handle is the callback invoked to manage the life cycle of an eth peer. When
@@ -199,7 +212,7 @@ func (pm *ProtocolManager) handle(p *peer) error {
// Execute the Ethereum handshake
td, head, genesis := pm.blockchain.Status()
- if err := p.Handshake(td, head, genesis); err != nil {
+ if err := p.Handshake(pm.networkId, td, head, genesis); err != nil {
glog.V(logger.Debug).Infof("%v: handshake failed: %v", p, err)
return err
}
@@ -730,3 +743,22 @@ func (self *ProtocolManager) txBroadcastLoop() {
self.BroadcastTx(event.Tx.Hash(), event.Tx)
}
}
+
+// EthNodeInfo represents a short summary of the Ethereum sub-protocol metadata known
+// about the host peer.
+type EthNodeInfo struct {
+ Network int `json:"network"` // Ethereum network ID (0=Olympic, 1=Frontier, 2=Morden)
+ Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain
+ Genesis string `json:"genesis"` // SHA3 hash of the host's genesis block
+ Head string `json:"head"` // SHA3 hash of the host's best owned block
+}
+
+// NodeInfo retrieves some protocol metadata about the running host node.
+func (self *ProtocolManager) NodeInfo() *EthNodeInfo {
+ return &EthNodeInfo{
+ Network: self.networkId,
+ Difficulty: self.blockchain.GetTd(self.blockchain.CurrentBlock().Hash()),
+ Genesis: fmt.Sprintf("%x", self.blockchain.Genesis().Hash()),
+ Head: fmt.Sprintf("%x", self.blockchain.CurrentBlock().Hash()),
+ }
+}
diff --git a/eth/helper_test.go b/eth/helper_test.go
index 16907be8b..bbd1fb818 100644
--- a/eth/helper_test.go
+++ b/eth/helper_test.go
@@ -35,9 +35,7 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core
db, _ = ethdb.NewMemDatabase()
genesis = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{testBankAddress, testBankFunds})
blockchain, _ = core.NewBlockChain(db, pow, evmux)
- blockproc = core.NewBlockProcessor(db, pow, blockchain, evmux)
)
- blockchain.SetProcessor(blockproc)
chain, _ := core.GenerateChain(genesis, db, blocks, generator)
if _, err := blockchain.InsertChain(chain); err != nil {
panic(err)
@@ -117,7 +115,7 @@ func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*te
var id discover.NodeID
rand.Read(id[:])
- peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net)
+ peer := pm.newPeer(version, p2p.NewPeer(id, name, nil), net)
// Start the peer on a new thread
errc := make(chan error, 1)
diff --git a/eth/peer.go b/eth/peer.go
index 695e910f6..15ba22ff5 100644
--- a/eth/peer.go
+++ b/eth/peer.go
@@ -44,38 +44,51 @@ const (
handshakeTimeout = 5 * time.Second
)
+// PeerInfo represents a short summary of the Ethereum sub-protocol metadata known
+// about a connected peer.
+type PeerInfo struct {
+ Version int `json:"version"` // Ethereum protocol version negotiated
+ Difficulty *big.Int `json:"difficulty"` // Total difficulty of the peer's blockchain
+ Head string `json:"head"` // SHA3 hash of the peer's best owned block
+}
+
type peer struct {
- *p2p.Peer
+ id string
+ *p2p.Peer
rw p2p.MsgReadWriter
version int // Protocol version negotiated
- network int // Network ID being on
-
- id string
-
- head common.Hash
- td *big.Int
- lock sync.RWMutex
+ head common.Hash
+ td *big.Int
+ lock sync.RWMutex
knownTxs *set.Set // Set of transaction hashes known to be known by this peer
knownBlocks *set.Set // Set of block hashes known to be known by this peer
}
-func newPeer(version, network int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
+func newPeer(version int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
id := p.ID()
return &peer{
Peer: p,
rw: rw,
version: version,
- network: network,
id: fmt.Sprintf("%x", id[:8]),
knownTxs: set.New(),
knownBlocks: set.New(),
}
}
+// Info gathers and returns a collection of metadata known about a peer.
+func (p *peer) Info() *PeerInfo {
+ return &PeerInfo{
+ Version: p.version,
+ Difficulty: p.Td(),
+ Head: fmt.Sprintf("%x", p.Head()),
+ }
+}
+
// Head retrieves a copy of the current head (most recent) hash of the peer.
func (p *peer) Head() (hash common.Hash) {
p.lock.RLock()
@@ -268,20 +281,22 @@ func (p *peer) RequestReceipts(hashes []common.Hash) error {
// Handshake executes the eth protocol handshake, negotiating version number,
// network IDs, difficulties, head and genesis blocks.
-func (p *peer) Handshake(td *big.Int, head common.Hash, genesis common.Hash) error {
+func (p *peer) Handshake(network int, td *big.Int, head common.Hash, genesis common.Hash) error {
+ // Send out own handshake in a new thread
errc := make(chan error, 2)
var status statusData // safe to read after two values have been received from errc
+
go func() {
errc <- p2p.Send(p.rw, StatusMsg, &statusData{
ProtocolVersion: uint32(p.version),
- NetworkId: uint32(p.network),
+ NetworkId: uint32(network),
TD: td,
CurrentBlock: head,
GenesisBlock: genesis,
})
}()
go func() {
- errc <- p.readStatus(&status, genesis)
+ errc <- p.readStatus(network, &status, genesis)
}()
timeout := time.NewTimer(handshakeTimeout)
defer timeout.Stop()
@@ -299,7 +314,7 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, genesis common.Hash) err
return nil
}
-func (p *peer) readStatus(status *statusData, genesis common.Hash) (err error) {
+func (p *peer) readStatus(network int, status *statusData, genesis common.Hash) (err error) {
msg, err := p.rw.ReadMsg()
if err != nil {
return err
@@ -317,8 +332,8 @@ func (p *peer) readStatus(status *statusData, genesis common.Hash) (err error) {
if status.GenesisBlock != genesis {
return errResp(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, genesis)
}
- if int(status.NetworkId) != p.network {
- return errResp(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, p.network)
+ if int(status.NetworkId) != network {
+ return errResp(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, network)
}
if int(status.ProtocolVersion) != p.version {
return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.version)
diff --git a/eth/protocol.go b/eth/protocol.go
index 410347ed3..808ac0601 100644
--- a/eth/protocol.go
+++ b/eth/protocol.go
@@ -33,6 +33,9 @@ const (
eth63 = 63
)
+// Official short name of the protocol used during capability negotiation.
+var ProtocolName = "eth"
+
// Supported versions of the eth protocol (first is primary).
var ProtocolVersions = []uint{eth63, eth62, eth61}
diff --git a/eth/sync.go b/eth/sync.go
index bbf2abc04..dd8aef8e4 100644
--- a/eth/sync.go
+++ b/eth/sync.go
@@ -175,10 +175,6 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
}
// If fast sync was enabled, and we synced up, disable it
if pm.fastSync {
- // Wait until all pending imports finish processing
- for pm.downloader.Synchronising() {
- time.Sleep(100 * time.Millisecond)
- }
// Disable fast sync if we indeed have something in our chain
if pm.blockchain.CurrentBlock().NumberU64() > 0 {
glog.V(logger.Info).Infof("fast sync complete, auto disabling")
diff --git a/eth/sync_test.go b/eth/sync_test.go
index f3a6718ab..afd90c9b6 100644
--- a/eth/sync_test.go
+++ b/eth/sync_test.go
@@ -40,8 +40,8 @@ func TestFastSyncDisabling(t *testing.T) {
// Sync up the two peers
io1, io2 := p2p.MsgPipe()
- go pmFull.handle(pmFull.newPeer(63, NetworkId, p2p.NewPeer(discover.NodeID{}, "empty", nil), io2))
- go pmEmpty.handle(pmEmpty.newPeer(63, NetworkId, p2p.NewPeer(discover.NodeID{}, "full", nil), io1))
+ go pmFull.handle(pmFull.newPeer(63, p2p.NewPeer(discover.NodeID{}, "empty", nil), io2))
+ go pmEmpty.handle(pmEmpty.newPeer(63, p2p.NewPeer(discover.NodeID{}, "full", nil), io1))
time.Sleep(250 * time.Millisecond)
pmEmpty.synchronise(pmEmpty.peers.BestPeer())
diff --git a/event/filter/filter_test.go b/event/filter/filter_test.go
index 0cd26bfc9..dcc911245 100644
--- a/event/filter/filter_test.go
+++ b/event/filter/filter_test.go
@@ -21,35 +21,40 @@ import (
"time"
)
+// Simple test to check if baseline matching/mismatching filtering works.
func TestFilters(t *testing.T) {
- var success bool
- var failure bool
-
fm := New()
fm.Start()
+
+ // Register two filters to catch posted data
+ first := make(chan struct{})
fm.Install(Generic{
Str1: "hello",
Fn: func(data interface{}) {
- success = data.(bool)
+ first <- struct{}{}
},
})
+ second := make(chan struct{})
fm.Install(Generic{
Str1: "hello1",
Str2: "hello",
Fn: func(data interface{}) {
- failure = true
+ second <- struct{}{}
},
})
+ // Post an event that should only match the first filter
fm.Notify(Generic{Str1: "hello"}, true)
fm.Stop()
- time.Sleep(10 * time.Millisecond) // yield to the notifier
-
- if !success {
- t.Error("expected 'hello' to be posted")
+ // Ensure only the mathcing filters fire
+ select {
+ case <-first:
+ case <-time.After(100 * time.Millisecond):
+ t.Error("matching filter timed out")
}
-
- if failure {
- t.Error("hello1 was triggered")
+ select {
+ case <-second:
+ t.Error("mismatching filter fired")
+ case <-time.After(100 * time.Millisecond):
}
}
diff --git a/jsre/jsre_test.go b/jsre/jsre_test.go
index 8450f546c..ffb6999db 100644
--- a/jsre/jsre_test.go
+++ b/jsre/jsre_test.go
@@ -85,7 +85,7 @@ func TestNatto(t *testing.T) {
if err != nil {
t.Errorf("expected no error, got %v", err)
}
- time.Sleep(time.Millisecond * 10)
+ time.Sleep(100 * time.Millisecond)
val, err := jsre.Run("msg")
if err != nil {
t.Errorf("expected no error, got %v", err)
diff --git a/miner/worker.go b/miner/worker.go
index 2d072ef60..754a6fc48 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -100,7 +100,7 @@ type worker struct {
eth core.Backend
chain *core.BlockChain
- proc *core.BlockProcessor
+ proc core.Validator
chainDb ethdb.Database
coinbase common.Address
@@ -131,7 +131,7 @@ func newWorker(coinbase common.Address, eth core.Backend) *worker {
recv: make(chan *Result, resultQueueSize),
gasPrice: new(big.Int),
chain: eth.BlockChain(),
- proc: eth.BlockProcessor(),
+ proc: eth.BlockChain().Validator(),
possibleUncles: make(map[common.Hash]*types.Block),
coinbase: coinbase,
txQueue: make(map[common.Hash]*types.Transaction),
@@ -244,7 +244,7 @@ func (self *worker) update() {
// Apply transaction to the pending state if we're not mining
if atomic.LoadInt32(&self.mining) == 0 {
self.currentMu.Lock()
- self.current.commitTransactions(types.Transactions{ev.Tx}, self.gasPrice, self.proc)
+ self.current.commitTransactions(types.Transactions{ev.Tx}, self.gasPrice, self.chain)
self.currentMu.Unlock()
}
}
@@ -290,7 +290,9 @@ func (self *worker) wait() {
glog.V(logger.Error).Infoln("Invalid block found during mining")
continue
}
- if err := core.ValidateHeader(self.eth.BlockProcessor().Pow, block.Header(), parent.Header(), true, false); err != nil && err != core.BlockFutureErr {
+
+ auxValidator := self.eth.BlockChain().AuxValidator()
+ if err := core.ValidateHeader(auxValidator, block.Header(), parent.Header(), true, false); err != nil && err != core.BlockFutureErr {
glog.V(logger.Error).Infoln("Invalid header on mined block:", err)
continue
}
@@ -300,12 +302,23 @@ func (self *worker) wait() {
glog.V(logger.Error).Infoln("error writing block to chain", err)
continue
}
+
+ // update block hash since it is now available and not when the receipt/log of individual transactions were created
+ for _, r := range work.receipts {
+ for _, l := range r.Logs {
+ l.BlockHash = block.Hash()
+ }
+ }
+ for _, log := range work.state.Logs() {
+ log.BlockHash = block.Hash()
+ }
+
// check if canon block and write transactions
if stat == core.CanonStatTy {
// This puts transactions in a extra db for rpc
- core.PutTransactions(self.chainDb, block, block.Transactions())
+ core.WriteTransactions(self.chainDb, block)
// store the receipts
- core.PutReceipts(self.chainDb, work.receipts)
+ core.WriteReceipts(self.chainDb, work.receipts)
// Write map map bloom filters
core.WriteMipmapBloom(self.chainDb, block.NumberU64(), work.receipts)
}
@@ -318,7 +331,7 @@ func (self *worker) wait() {
self.mux.Post(core.ChainHeadEvent{block})
self.mux.Post(logs)
}
- if err := core.PutBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil {
+ if err := core.WriteBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil {
glog.V(logger.Warn).Infoln("error writing block receipts:", err)
}
}(block, work.state.Logs(), work.receipts)
@@ -516,7 +529,7 @@ func (self *worker) commitNewWork() {
transactions := append(singleTxOwner, multiTxOwner...)
*/
- work.commitTransactions(transactions, self.gasPrice, self.proc)
+ work.commitTransactions(transactions, self.gasPrice, self.chain)
self.eth.TxPool().RemoveTransactions(work.lowGasTxs)
// compute uncles for the new block.
@@ -575,9 +588,8 @@ func (self *worker) commitUncle(work *Work, uncle *types.Header) error {
return nil
}
-func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *big.Int, proc *core.BlockProcessor) {
+func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *big.Int, bc *core.BlockChain) {
gp := new(core.GasPool).AddGas(env.header.GasLimit)
-
for _, tx := range transactions {
// We can skip err. It has already been validated in the tx pool
from, _ := tx.From()
@@ -615,7 +627,7 @@ func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *b
env.state.StartRecord(tx.Hash(), common.Hash{}, 0)
- err := env.commitTransaction(tx, proc, gp)
+ err := env.commitTransaction(tx, bc, gp)
switch {
case core.IsGasLimitErr(err):
// ignore the transactor so no nonce errors will be thrown for this account
@@ -635,9 +647,9 @@ func (env *Work) commitTransactions(transactions types.Transactions, gasPrice *b
}
}
-func (env *Work) commitTransaction(tx *types.Transaction, proc *core.BlockProcessor, gp *core.GasPool) error {
+func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) error {
snap := env.state.Copy()
- receipt, _, err := proc.ApplyTransaction(gp, env.state, env.header, tx, env.header.GasUsed, true)
+ receipt, _, _, err := core.ApplyTransaction(bc, gp, env.state, env.header, tx, env.header.GasUsed)
if err != nil {
env.state.Set(snap)
return err
diff --git a/p2p/peer.go b/p2p/peer.go
index 1b3b19c79..72ed4069c 100644
--- a/p2p/peer.go
+++ b/p2p/peer.go
@@ -359,3 +359,49 @@ func (rw *protoRW) ReadMsg() (Msg, error) {
return Msg{}, io.EOF
}
}
+
+// PeerInfo represents a short summary of the information known about a connected
+// peer. Sub-protocol independent fields are contained and initialized here, with
+// protocol specifics delegated to all connected sub-protocols.
+type PeerInfo struct {
+ ID string `json:"id"` // Unique node identifier (also the encryption key)
+ Name string `json:"name"` // Name of the node, including client type, version, OS, custom data
+ Caps []string `json:"caps"` // Sum-protocols advertised by this particular peer
+ Network struct {
+ LocalAddress string `json:"localAddress"` // Local endpoint of the TCP data connection
+ RemoteAddress string `json:"remoteAddress"` // Remote endpoint of the TCP data connection
+ } `json:"network"`
+ Protocols map[string]interface{} `json:"protocols"` // Sub-protocol specific metadata fields
+}
+
+// Info gathers and returns a collection of metadata known about a peer.
+func (p *Peer) Info() *PeerInfo {
+ // Gather the protocol capabilities
+ var caps []string
+ for _, cap := range p.Caps() {
+ caps = append(caps, cap.String())
+ }
+ // Assemble the generic peer metadata
+ info := &PeerInfo{
+ ID: p.ID().String(),
+ Name: p.Name(),
+ Caps: caps,
+ Protocols: make(map[string]interface{}),
+ }
+ info.Network.LocalAddress = p.LocalAddr().String()
+ info.Network.RemoteAddress = p.RemoteAddr().String()
+
+ // Gather all the running protocol infos
+ for _, proto := range p.running {
+ protoInfo := interface{}("unknown")
+ if query := proto.Protocol.PeerInfo; query != nil {
+ if metadata := query(p.ID()); metadata != nil {
+ protoInfo = metadata
+ } else {
+ protoInfo = "handshake"
+ }
+ }
+ info.Protocols[proto.Name] = protoInfo
+ }
+ return info
+}
diff --git a/p2p/protocol.go b/p2p/protocol.go
index ac0c3d942..ee747ba23 100644
--- a/p2p/protocol.go
+++ b/p2p/protocol.go
@@ -16,7 +16,11 @@
package p2p
-import "fmt"
+import (
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/p2p/discover"
+)
// Protocol represents a P2P subprotocol implementation.
type Protocol struct {
@@ -39,6 +43,15 @@ type Protocol struct {
// any protocol-level error (such as an I/O error) that is
// encountered.
Run func(peer *Peer, rw MsgReadWriter) error
+
+ // NodeInfo is an optional helper method to retrieve protocol specific metadata
+ // about the host node.
+ NodeInfo func() interface{}
+
+ // PeerInfo is an optional helper method to retrieve protocol specific metadata
+ // about a certain peer in the network. If an info retrieval function is set,
+ // but returns nil, it is assumed that the protocol handshake is still running.
+ PeerInfo func(id discover.NodeID) interface{}
}
func (p Protocol) cap() Cap {
diff --git a/p2p/server.go b/p2p/server.go
index 6060adc71..ee670b10e 100644
--- a/p2p/server.go
+++ b/p2p/server.go
@@ -689,3 +689,66 @@ func (srv *Server) runPeer(p *Peer) {
NumConnections: srv.PeerCount(),
})
}
+
+// NodeInfo represents a short summary of the information known about the host.
+type NodeInfo struct {
+ ID string `json:"id"` // Unique node identifier (also the encryption key)
+ Name string `json:"name"` // Name of the node, including client type, version, OS, custom data
+ Enode string `json:"enode"` // Enode URL for adding this peer from remote peers
+ IP string `json:"ip"` // IP address of the node
+ Ports struct {
+ Discovery int `json:"discovery"` // UDP listening port for discovery protocol
+ Listener int `json:"listener"` // TCP listening port for RLPx
+ } `json:"ports"`
+ ListenAddr string `json:"listenAddr"`
+ Protocols map[string]interface{} `json:"protocols"`
+}
+
+// Info gathers and returns a collection of metadata known about the host.
+func (srv *Server) NodeInfo() *NodeInfo {
+ node := srv.Self()
+
+ // Gather and assemble the generic node infos
+ info := &NodeInfo{
+ Name: srv.Name,
+ Enode: node.String(),
+ ID: node.ID.String(),
+ IP: node.IP.String(),
+ ListenAddr: srv.ListenAddr,
+ Protocols: make(map[string]interface{}),
+ }
+ info.Ports.Discovery = int(node.UDP)
+ info.Ports.Listener = int(node.TCP)
+
+ // Gather all the running protocol infos (only once per protocol type)
+ for _, proto := range srv.Protocols {
+ if _, ok := info.Protocols[proto.Name]; !ok {
+ nodeInfo := interface{}("unknown")
+ if query := proto.NodeInfo; query != nil {
+ nodeInfo = proto.NodeInfo()
+ }
+ info.Protocols[proto.Name] = nodeInfo
+ }
+ }
+ return info
+}
+
+// PeersInfo returns an array of metadata objects describing connected peers.
+func (srv *Server) PeersInfo() []*PeerInfo {
+ // Gather all the generic and sub-protocol specific infos
+ infos := make([]*PeerInfo, 0, srv.PeerCount())
+ for _, peer := range srv.Peers() {
+ if peer != nil {
+ infos = append(infos, peer.Info())
+ }
+ }
+ // Sort the result array alphabetically by node identifier
+ for i := 0; i < len(infos); i++ {
+ for j := i + 1; j < len(infos); j++ {
+ if infos[i].ID > infos[j].ID {
+ infos[i], infos[j] = infos[j], infos[i]
+ }
+ }
+ }
+ return infos
+}
diff --git a/rpc/api/admin.go b/rpc/api/admin.go
index eb08fbc5d..c11662577 100644
--- a/rpc/api/admin.go
+++ b/rpc/api/admin.go
@@ -137,11 +137,11 @@ func (self *adminApi) AddPeer(req *shared.Request) (interface{}, error) {
}
func (self *adminApi) Peers(req *shared.Request) (interface{}, error) {
- return self.ethereum.PeersInfo(), nil
+ return self.ethereum.Network().PeersInfo(), nil
}
func (self *adminApi) NodeInfo(req *shared.Request) (interface{}, error) {
- return self.ethereum.NodeInfo(), nil
+ return self.ethereum.Network().NodeInfo(), nil
}
func (self *adminApi) DataDir(req *shared.Request) (interface{}, error) {
diff --git a/rpc/api/args_test.go b/rpc/api/args_test.go
index 23ae2930d..130315bd9 100644
--- a/rpc/api/args_test.go
+++ b/rpc/api/args_test.go
@@ -1394,13 +1394,10 @@ func TestBlockFilterArgsDefaults(t *testing.T) {
}
func TestBlockFilterArgsWords(t *testing.T) {
- input := `[{
- "fromBlock": "latest",
- "toBlock": "pending"
- }]`
+ input := `[{"fromBlock": "latest", "toBlock": "latest"}]`
expected := new(BlockFilterArgs)
expected.Earliest = -1
- expected.Latest = -2
+ expected.Latest = -1
args := new(BlockFilterArgs)
if err := json.Unmarshal([]byte(input), &args); err != nil {
@@ -1411,8 +1408,9 @@ func TestBlockFilterArgsWords(t *testing.T) {
t.Errorf("Earliest shoud be %#v but is %#v", expected.Earliest, args.Earliest)
}
- if expected.Latest != args.Latest {
- t.Errorf("Latest shoud be %#v but is %#v", expected.Latest, args.Latest)
+ input = `[{"toBlock": "pending"}]`
+ if err := json.Unmarshal([]byte(input), &args); err == nil {
+ t.Errorf("Pending isn't currently supported and should raise an unsupported error")
}
}
diff --git a/rpc/api/debug.go b/rpc/api/debug.go
index d2cbc7f19..a6faa335e 100644
--- a/rpc/api/debug.go
+++ b/rpc/api/debug.go
@@ -22,6 +22,7 @@ import (
"time"
"github.com/ethereum/ethash"
+ "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth"
@@ -166,11 +167,30 @@ func (self *debugApi) ProcessBlock(req *shared.Request) (interface{}, error) {
defer func() { vm.Debug = old }()
vm.Debug = true
- _, err := self.ethereum.BlockProcessor().RetryProcess(block)
- if err == nil {
- return true, nil
+ var (
+ blockchain = self.ethereum.BlockChain()
+ validator = blockchain.Validator()
+ processor = blockchain.Processor()
+ )
+
+ err := core.ValidateHeader(blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash()), true, false)
+ if err != nil {
+ return false, err
}
- return false, err
+ statedb, err := state.New(blockchain.GetBlock(block.ParentHash()).Root(), self.ethereum.ChainDb())
+ if err != nil {
+ return false, err
+ }
+ receipts, _, usedGas, err := processor.Process(block, statedb)
+ if err != nil {
+ return false, err
+ }
+ err = validator.ValidateState(block, blockchain.GetBlock(block.ParentHash()), statedb, receipts, usedGas)
+ if err != nil {
+ return false, err
+ }
+
+ return true, nil
}
func (self *debugApi) SeedHash(req *shared.Request) (interface{}, error) {
diff --git a/rpc/api/eth.go b/rpc/api/eth.go
index b84ae31da..db7a643d8 100644
--- a/rpc/api/eth.go
+++ b/rpc/api/eth.go
@@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/natspec"
"github.com/ethereum/go-ethereum/eth"
+ "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc/codec"
"github.com/ethereum/go-ethereum/rpc/shared"
"github.com/ethereum/go-ethereum/xeth"
@@ -70,8 +71,10 @@ var (
"eth_getCode": (*ethApi).GetData,
"eth_getNatSpec": (*ethApi).GetNatSpec,
"eth_sign": (*ethApi).Sign,
- "eth_sendRawTransaction": (*ethApi).SendRawTransaction,
+ "eth_sendRawTransaction": (*ethApi).SubmitTransaction,
+ "eth_submitTransaction": (*ethApi).SubmitTransaction,
"eth_sendTransaction": (*ethApi).SendTransaction,
+ "eth_signTransaction": (*ethApi).SignTransaction,
"eth_transact": (*ethApi).SendTransaction,
"eth_estimateGas": (*ethApi).EstimateGas,
"eth_call": (*ethApi).Call,
@@ -285,7 +288,7 @@ func (self *ethApi) Sign(req *shared.Request) (interface{}, error) {
return v, nil
}
-func (self *ethApi) SendRawTransaction(req *shared.Request) (interface{}, error) {
+func (self *ethApi) SubmitTransaction(req *shared.Request) (interface{}, error) {
args := new(NewDataArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
return nil, shared.NewDecodeParamError(err.Error())
@@ -298,6 +301,45 @@ func (self *ethApi) SendRawTransaction(req *shared.Request) (interface{}, error)
return v, nil
}
+// JsonTransaction is returned as response by the JSON RPC. It contains the
+// signed RLP encoded transaction as Raw and the signed transaction object as Tx.
+type JsonTransaction struct {
+ Raw string `json:"raw"`
+ Tx *tx `json:"tx"`
+}
+
+func (self *ethApi) SignTransaction(req *shared.Request) (interface{}, error) {
+ args := new(NewTxArgs)
+ if err := self.codec.Decode(req.Params, &args); err != nil {
+ return nil, shared.NewDecodeParamError(err.Error())
+ }
+
+ // nonce may be nil ("guess" mode)
+ var nonce string
+ if args.Nonce != nil {
+ nonce = args.Nonce.String()
+ }
+
+ var gas, price string
+ if args.Gas != nil {
+ gas = args.Gas.String()
+ }
+ if args.GasPrice != nil {
+ price = args.GasPrice.String()
+ }
+ tx, err := self.xeth.SignTransaction(args.From, args.To, nonce, args.Value.String(), gas, price, args.Data)
+ if err != nil {
+ return nil, err
+ }
+
+ data, err := rlp.EncodeToBytes(tx)
+ if err != nil {
+ return nil, err
+ }
+
+ return JsonTransaction{"0x" + common.Bytes2Hex(data), newTx(tx)}, nil
+}
+
func (self *ethApi) SendTransaction(req *shared.Request) (interface{}, error) {
args := new(NewTxArgs)
if err := self.codec.Decode(req.Params, &args); err != nil {
diff --git a/rpc/api/eth_args.go b/rpc/api/eth_args.go
index 457350d74..ed3d761f1 100644
--- a/rpc/api/eth_args.go
+++ b/rpc/api/eth_args.go
@@ -722,6 +722,13 @@ func (args *BlockFilterArgs) UnmarshalJSON(b []byte) (err error) {
return err
}
}
+
+ if num == -2 {
+ return fmt.Errorf("\"pending\" is unsupported")
+ } else if num < -2 {
+ return fmt.Errorf("Invalid to block number")
+ }
+
args.Latest = num
if obj[0].Limit == nil {
diff --git a/rpc/api/eth_js.go b/rpc/api/eth_js.go
index 75c103c9d..dfc104ad8 100644
--- a/rpc/api/eth_js.go
+++ b/rpc/api/eth_js.go
@@ -36,11 +36,23 @@ web3._extend({
params: 3,
inputFormatter: [web3._extend.formatters.inputTransactionFormatter, web3._extend.utils.fromDecimal, web3._extend.utils.fromDecimal]
}),
- new web3._extend.Method({
- name: 'getNatSpec',
- call: 'eth_getNatSpec',
- params: 1,
- inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
+ new web3._extend.Method({
+ name: 'getNatSpec',
+ call: 'eth_getNatSpec',
+ params: 1,
+ inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
+ }),
+ new web3._extend.Method({
+ name: 'signTransaction',
+ call: 'eth_signTransaction',
+ params: 1,
+ inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
+ }),
+ new web3._extend.Method({
+ name: 'submitTransaction',
+ call: 'eth_submitTransaction',
+ params: 1,
+ inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
})
],
properties:
diff --git a/rpc/api/utils.go b/rpc/api/utils.go
index 5a3ade46b..8351e88d3 100644
--- a/rpc/api/utils.go
+++ b/rpc/api/utils.go
@@ -130,7 +130,7 @@ var (
},
"shh": []string{
"post",
- "newIdentify",
+ "newIdentity",
"hasIdentity",
"newGroup",
"addToGroup",
diff --git a/tests/files/BlockchainTests/bcStateTest.json b/tests/files/BlockchainTests/bcStateTest.json
index 42a178883..60a15a426 100644
--- a/tests/files/BlockchainTests/bcStateTest.json
+++ b/tests/files/BlockchainTests/bcStateTest.json
@@ -1,4 +1,631 @@
{
+ "CallingCanonicalContractFromFork" : {
+ "blocks" : [
+ {
+ "blockHeader" : {
+ "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1",
+ "difficulty" : "0x020000",
+ "extraData" : "0x",
+ "gasLimit" : "0x2fefd8",
+ "gasUsed" : "0x5208",
+ "hash" : "08b3bb56fceaec4428db828727f8ca3eccdf8dd6dc88fea30cac27d3fe8e8bc5",
+ "mixHash" : "d32d3ce5a29831d92e4d13bbad10d98b7aa3e268a261be29e6126922a2b65ce6",
+ "nonce" : "5f767835b991d998",
+ "number" : "0x01",
+ "parentHash" : "11538dc3be8edb7ac03d9ab9c58ee0348da65149545a42629322e5d577cfb337",
+ "receiptTrie" : "c741e9eaf5604d654d46a98cb267ecad8d26090f5a401ec1ac75097974fe83a5",
+ "stateRoot" : "9e502a6b6dbf7dfd743afe836af2d74e42fdfb0a58a18512d8c984d8f60612a1",
+ "timestamp" : "0x563500da",
+ "transactionsTrie" : "f80217763f8d00269566918ebd3c7729465f8b9818a0f437bf215a8190884d5d",
+ "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
+ },
+ "blocknumber" : "1",
+ "chainname" : "A",
+ "rlp" : "0xf90261f901f9a011538dc3be8edb7ac03d9ab9c58ee0348da65149545a42629322e5d577cfb337a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a09e502a6b6dbf7dfd743afe836af2d74e42fdfb0a58a18512d8c984d8f60612a1a0f80217763f8d00269566918ebd3c7729465f8b9818a0f437bf215a8190884d5da0c741e9eaf5604d654d46a98cb267ecad8d26090f5a401ec1ac75097974fe83a5b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd882520884563500da80a0d32d3ce5a29831d92e4d13bbad10d98b7aa3e268a261be29e6126922a2b65ce6885f767835b991d998f862f86080018304cb2f94a95e7baea6a6c7c4c2dfeb977efac326af552d870a801ca0886931af2e4e440628e6e862e50944534ff3068c38cabb908dc708fa3736bfe0a03de874195b35641546d6c48409cca106127e5260a67fe7ed21a35dfb1f7a08b5c0",
+ "transactions" : [
+ {
+ "data" : "0x",
+ "gasLimit" : "0x04cb2f",
+ "gasPrice" : "0x01",
+ "nonce" : "0x00",
+ "r" : "0x886931af2e4e440628e6e862e50944534ff3068c38cabb908dc708fa3736bfe0",
+ "s" : "0x3de874195b35641546d6c48409cca106127e5260a67fe7ed21a35dfb1f7a08b5",
+ "to" : "a95e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "v" : "0x1c",
+ "value" : "0x0a"
+ }
+ ],
+ "uncleHeaders" : [
+ ]
+ },
+ {
+ "blockHeader" : {
+ "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1",
+ "difficulty" : "0x020040",
+ "extraData" : "0x",
+ "gasLimit" : "0x2fefd8",
+ "gasUsed" : "0xc0e3",
+ "hash" : "e3818b1951de8955f7e82faec0ce6116a68831398fe37d5d903946096769916f",
+ "mixHash" : "1005a7308338af66d2d078df0bbcb722aa23f02a565c1eb64c5cc49dcf197680",
+ "nonce" : "7a19e455210a4238",
+ "number" : "0x02",
+ "parentHash" : "08b3bb56fceaec4428db828727f8ca3eccdf8dd6dc88fea30cac27d3fe8e8bc5",
+ "receiptTrie" : "50d84f1a0c328748578441d1e31280248c629d8e83e3415e6a0493147f065435",
+ "stateRoot" : "f6116978d1ac80e6a6b27996b35410d388b51a7898250b709a1320a0414e1ead",
+ "timestamp" : "0x563500dd",
+ "transactionsTrie" : "73605c813df801dea03161bc2f66993a59f86babc47efa9d0e952bc79a26fabd",
+ "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
+ },
+ "blocknumber" : "2",
+ "chainname" : "A",
+ "rlp" : "0xf902ccf901f9a008b3bb56fceaec4428db828727f8ca3eccdf8dd6dc88fea30cac27d3fe8e8bc5a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0f6116978d1ac80e6a6b27996b35410d388b51a7898250b709a1320a0414e1eada073605c813df801dea03161bc2f66993a59f86babc47efa9d0e952bc79a26fabda050d84f1a0c328748578441d1e31280248c629d8e83e3415e6a0493147f065435b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302004002832fefd882c0e384563500dd80a01005a7308338af66d2d078df0bbcb722aa23f02a565c1eb64c5cc49dcf197680887a19e455210a4238f8cdf8cb01018304cb2f8080b87e6060604052606e8060106000396000f360606040526000357c010000000000000000000000000000000000000000000000000000000090048063c0406226146037576035565b005b60406004506056565b6040518082815260200191505060405180910390f35b6000600560006000508190555060059050606b565b90561ca0b5a59222c12d3a9d52fddef1c7881a0dc7e72f5aed83be3cd7d7fdf868ec92a2a0551fea14e12dc99b6d78884bf5f0edae2da6ad4102d955057251d5d2aa8f54a5c0",
+ "transactions" : [
+ {
+ "data" : "0x6060604052606e8060106000396000f360606040526000357c010000000000000000000000000000000000000000000000000000000090048063c0406226146037576035565b005b60406004506056565b6040518082815260200191505060405180910390f35b6000600560006000508190555060059050606b565b9056",
+ "gasLimit" : "0x04cb2f",
+ "gasPrice" : "0x01",
+ "nonce" : "0x01",
+ "r" : "0xb5a59222c12d3a9d52fddef1c7881a0dc7e72f5aed83be3cd7d7fdf868ec92a2",
+ "s" : "0x551fea14e12dc99b6d78884bf5f0edae2da6ad4102d955057251d5d2aa8f54a5",
+ "to" : "",
+ "v" : "0x1c",
+ "value" : "0x00"
+ }
+ ],
+ "uncleHeaders" : [
+ ]
+ },
+ {
+ "blockHeader" : {
+ "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1",
+ "difficulty" : "0x020000",
+ "extraData" : "0x",
+ "gasLimit" : "0x2fefd8",
+ "gasUsed" : "0x0169ee",
+ "hash" : "79423c013354dae6981d6739e3aaadc0a56b475ba241eb6c49494eee065f0aae",
+ "mixHash" : "f06fb582b789bb3c80c68b6151c91e96ed722ce38ba356e09d77d1378176bdb6",
+ "nonce" : "44585532f822bbd1",
+ "number" : "0x03",
+ "parentHash" : "e3818b1951de8955f7e82faec0ce6116a68831398fe37d5d903946096769916f",
+ "receiptTrie" : "1425974449f0fb4847ac5307c8a8d22b240387fc68993f0b9c485b118959eb10",
+ "stateRoot" : "92fd648d9e8a574b0b7d70bb9bff176067f319d9753871f9cf3c27d704cc9cc7",
+ "timestamp" : "0x563500f5",
+ "transactionsTrie" : "9a5ba001326af3fd741edc763f3f0830c71a28e55a0a461d455792c2ad4918fe",
+ "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
+ },
+ "blocknumber" : "3",
+ "chainname" : "A",
+ "rlp" : "0xf902c4f901faa0e3818b1951de8955f7e82faec0ce6116a68831398fe37d5d903946096769916fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a092fd648d9e8a574b0b7d70bb9bff176067f319d9753871f9cf3c27d704cc9cc7a09a5ba001326af3fd741edc763f3f0830c71a28e55a0a461d455792c2ad4918fea01425974449f0fb4847ac5307c8a8d22b240387fc68993f0b9c485b118959eb10b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000003832fefd8830169ee84563500f580a0f06fb582b789bb3c80c68b6151c91e96ed722ce38ba356e09d77d1378176bdb68844585532f822bbd1f8c4f86002018304cb2f94095e7baea6a6c7c4c2dfeb977efac326af552d870a801ca015eb1cc916728b9799e55c489857727669afb2986433d5f54cde11faaed9f0eea05d36f6d06c34aae8d0a2a5895c8ba4a17ad46a5fa59f361cb3e7e01a23030e38f86003018304cb2f94095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba0a7b7f2fa93025fc1e6aa18c1aa07c32a456439754e196cb74f2f7d12cf3e840da02078cf840fb25fc3d858b2a85b622f21be0588b5c5d81d433427f6470e06a4a7c0",
+ "transactions" : [
+ {
+ "data" : "0x",
+ "gasLimit" : "0x04cb2f",
+ "gasPrice" : "0x01",
+ "nonce" : "0x02",
+ "r" : "0x15eb1cc916728b9799e55c489857727669afb2986433d5f54cde11faaed9f0ee",
+ "s" : "0x5d36f6d06c34aae8d0a2a5895c8ba4a17ad46a5fa59f361cb3e7e01a23030e38",
+ "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "v" : "0x1c",
+ "value" : "0x0a"
+ },
+ {
+ "data" : "0x",
+ "gasLimit" : "0x04cb2f",
+ "gasPrice" : "0x01",
+ "nonce" : "0x03",
+ "r" : "0xa7b7f2fa93025fc1e6aa18c1aa07c32a456439754e196cb74f2f7d12cf3e840d",
+ "s" : "0x2078cf840fb25fc3d858b2a85b622f21be0588b5c5d81d433427f6470e06a4a7",
+ "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "v" : "0x1b",
+ "value" : "0x0a"
+ }
+ ],
+ "uncleHeaders" : [
+ ]
+ },
+ {
+ "blockHeader" : {
+ "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1",
+ "difficulty" : "0x020000",
+ "extraData" : "0x",
+ "gasLimit" : "0x2fefd8",
+ "gasUsed" : "0x5208",
+ "hash" : "e0efde98e5a863413548be4bd0c7d95c403eb429f6480bbc54acd9ae993e6b29",
+ "mixHash" : "07eb27e1868f9956f1d60a12b653f1c8b6818e3ac8a25eaf8b1e4d91cb9b63b8",
+ "nonce" : "00b284c1d142e0ae",
+ "number" : "0x01",
+ "parentHash" : "11538dc3be8edb7ac03d9ab9c58ee0348da65149545a42629322e5d577cfb337",
+ "receiptTrie" : "f8b9bd0dc083e4c553e1a34ab265f74754b999e34b87ce68f034e4bd775ec3cc",
+ "stateRoot" : "8d6a64bdf95c29dcc72ccae2affaf8842208c7387434a53153991e6368f1c30a",
+ "timestamp" : "0x563500fb",
+ "transactionsTrie" : "da4ff51d21fb53978f91cc0bae0c01cfe4ed3485b999e4ef55d16441198223b5",
+ "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
+ },
+ "blocknumber" : "1",
+ "chainname" : "B",
+ "rlp" : "0xf90261f901f9a011538dc3be8edb7ac03d9ab9c58ee0348da65149545a42629322e5d577cfb337a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a08d6a64bdf95c29dcc72ccae2affaf8842208c7387434a53153991e6368f1c30aa0da4ff51d21fb53978f91cc0bae0c01cfe4ed3485b999e4ef55d16441198223b5a0f8b9bd0dc083e4c553e1a34ab265f74754b999e34b87ce68f034e4bd775ec3ccb90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd882520884563500fb80a007eb27e1868f9956f1d60a12b653f1c8b6818e3ac8a25eaf8b1e4d91cb9b63b88800b284c1d142e0aef862f86080018304cb2f94a95e7baea6a6c7c4c2dfeb977efac326af552d8764801ba08d9a64385e3a24e9f6fdd49256c9f7d23cd501deaa55c5b4283fc58b2a601039a02518fdccd38aa1caaf4b49c907f750961f325d3d70104a22700b16d9af0d0557c0",
+ "transactions" : [
+ {
+ "data" : "0x",
+ "gasLimit" : "0x04cb2f",
+ "gasPrice" : "0x01",
+ "nonce" : "0x00",
+ "r" : "0x8d9a64385e3a24e9f6fdd49256c9f7d23cd501deaa55c5b4283fc58b2a601039",
+ "s" : "0x2518fdccd38aa1caaf4b49c907f750961f325d3d70104a22700b16d9af0d0557",
+ "to" : "a95e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "v" : "0x1b",
+ "value" : "0x64"
+ }
+ ],
+ "uncleHeaders" : [
+ ]
+ },
+ {
+ "blockHeader" : {
+ "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1",
+ "difficulty" : "0x020040",
+ "extraData" : "0x",
+ "gasLimit" : "0x2fefd8",
+ "gasUsed" : "0x01025f",
+ "hash" : "96ede2056e2a24ae0fd06a7396eacd31d3309b4da2d96b0eeab4a33e7cbc4df3",
+ "mixHash" : "9969bbd7cfb5b96d099f1cdf644fb40db340df09ceafec9305283a1900a42163",
+ "nonce" : "e56a1b4bf99b697e",
+ "number" : "0x02",
+ "parentHash" : "e0efde98e5a863413548be4bd0c7d95c403eb429f6480bbc54acd9ae993e6b29",
+ "receiptTrie" : "f18320a96d546803b8d0012e76cece8c69b214a2e4e5bf6e4134d638b16ab8e7",
+ "stateRoot" : "a9bb042145dda02ecae18f10f60f8d139933c7e2aa03747c9a6e50a1d823becd",
+ "timestamp" : "0x563500fc",
+ "transactionsTrie" : "7d90b54544a650e638e06d360a7dc2eee226c40373df61476b792ca3eccae35b",
+ "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
+ },
+ "blocknumber" : "2",
+ "chainname" : "B",
+ "rlp" : "0xf90262f901faa0e0efde98e5a863413548be4bd0c7d95c403eb429f6480bbc54acd9ae993e6b29a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0a9bb042145dda02ecae18f10f60f8d139933c7e2aa03747c9a6e50a1d823becda07d90b54544a650e638e06d360a7dc2eee226c40373df61476b792ca3eccae35ba0f18320a96d546803b8d0012e76cece8c69b214a2e4e5bf6e4134d638b16ab8e7b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302004002832fefd88301025f84563500fc80a09969bbd7cfb5b96d099f1cdf644fb40db340df09ceafec9305283a1900a4216388e56a1b4bf99b697ef862f86001018304bb2b94095e7baea6a6c7c4c2dfeb977efac326af552d8764801ca0b66e1a2ee26689bdbb87af26141f167ded19e6b2041407bf4f80a36ce30a1b1ba0084e5def3deddc4ba0bd7dd8cebf184a8c3a3c2d5d9af5cb642bdf819d2e0be4c0",
+ "transactions" : [
+ {
+ "data" : "0x",
+ "gasLimit" : "0x04bb2b",
+ "gasPrice" : "0x01",
+ "nonce" : "0x01",
+ "r" : "0xb66e1a2ee26689bdbb87af26141f167ded19e6b2041407bf4f80a36ce30a1b1b",
+ "s" : "0x084e5def3deddc4ba0bd7dd8cebf184a8c3a3c2d5d9af5cb642bdf819d2e0be4",
+ "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "v" : "0x1c",
+ "value" : "0x64"
+ }
+ ],
+ "uncleHeaders" : [
+ ]
+ },
+ {
+ "blockHeader" : {
+ "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1",
+ "difficulty" : "0x020080",
+ "extraData" : "0x",
+ "gasLimit" : "0x2fefd8",
+ "gasUsed" : "0x661f",
+ "hash" : "9bada2849d859f3b9167460ad6a271334634a701f45ed993ceb21713f3b1ac90",
+ "mixHash" : "bdec38b76b56d641e8f66d8d25ac4b9e974770840eddf2b739a6c06f0b3cddd4",
+ "nonce" : "b74dd27326e79e4e",
+ "number" : "0x03",
+ "parentHash" : "96ede2056e2a24ae0fd06a7396eacd31d3309b4da2d96b0eeab4a33e7cbc4df3",
+ "receiptTrie" : "e9ac391e5acbde6dcfd8b2818b7624c8a8898130b39c6189ad1578a634e3c9af",
+ "stateRoot" : "6f2a96e71df732554404f93d6aa1000eb4fd7160d86b9223f72d6cb74247b5e3",
+ "timestamp" : "0x56350100",
+ "transactionsTrie" : "2bcc69b52ae6b6e2aca544608d302ea49078786bdc26a35220b67c81fa39b996",
+ "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
+ },
+ "blocknumber" : "3",
+ "chainname" : "B",
+ "rlp" : "0xf90261f901f9a096ede2056e2a24ae0fd06a7396eacd31d3309b4da2d96b0eeab4a33e7cbc4df3a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a06f2a96e71df732554404f93d6aa1000eb4fd7160d86b9223f72d6cb74247b5e3a02bcc69b52ae6b6e2aca544608d302ea49078786bdc26a35220b67c81fa39b996a0e9ac391e5acbde6dcfd8b2818b7624c8a8898130b39c6189ad1578a634e3c9afb90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefd882661f845635010080a0bdec38b76b56d641e8f66d8d25ac4b9e974770840eddf2b739a6c06f0b3cddd488b74dd27326e79e4ef862f86002018304bb2b94095e7baea6a6c7c4c2dfeb977efac326af552d8764801ba09e440c52e3bbb810164fb44c2f5b1882b5e9ae50a072737fe74a0a9152f7b973a070d7c1e0cb08639654481bdb953f7f73e4c16f75db1f7bb62fa2ce82bc4801c5c0",
+ "transactions" : [
+ {
+ "data" : "0x",
+ "gasLimit" : "0x04bb2b",
+ "gasPrice" : "0x01",
+ "nonce" : "0x02",
+ "r" : "0x9e440c52e3bbb810164fb44c2f5b1882b5e9ae50a072737fe74a0a9152f7b973",
+ "s" : "0x70d7c1e0cb08639654481bdb953f7f73e4c16f75db1f7bb62fa2ce82bc4801c5",
+ "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "v" : "0x1b",
+ "value" : "0x64"
+ }
+ ],
+ "uncleHeaders" : [
+ ]
+ }
+ ],
+ "genesisBlockHeader" : {
+ "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1",
+ "difficulty" : "0x020000",
+ "extraData" : "0x42",
+ "gasLimit" : "0x2fefd8",
+ "gasUsed" : "0x00",
+ "hash" : "11538dc3be8edb7ac03d9ab9c58ee0348da65149545a42629322e5d577cfb337",
+ "mixHash" : "7f405d76c550214b6b136eab04f4ccc018cdc4332aaff9a56ec082639e1280c1",
+ "nonce" : "8ab5a8979e9af8ec",
+ "number" : "0x00",
+ "parentHash" : "0000000000000000000000000000000000000000000000000000000000000000",
+ "receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "stateRoot" : "c6f1b420417bb444dff74db6be80197fee3ad9829cd462cfbe316af263556604",
+ "timestamp" : "0x54c98c81",
+ "transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
+ },
+ "genesisRLP" : "0xf901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0c6f1b420417bb444dff74db6be80197fee3ad9829cd462cfbe316af263556604a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080832fefd8808454c98c8142a07f405d76c550214b6b136eab04f4ccc018cdc4332aaff9a56ec082639e1280c1888ab5a8979e9af8ecc0c0",
+ "lastblockhash" : "9bada2849d859f3b9167460ad6a271334634a701f45ed993ceb21713f3b1ac90",
+ "postState" : {
+ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x09184e72a0c8",
+ "code" : "0x63c0406226600052604060006004601c600073ec0e71ad0a90ffe1909d27dac207f7680abba42d620249f0f15060005160015401600155",
+ "nonce" : "0x00",
+ "storage" : {
+ "0x01" : "0x018080c44c"
+ }
+ },
+ "8888f1f195afa192cfee860698584c030f4c9db1" : {
+ "balance" : "0xd02ab486ceddba86",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
+ "balance" : "0x09184e70e44e",
+ "code" : "0x",
+ "nonce" : "0x03",
+ "storage" : {
+ }
+ },
+ "a95e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x64",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "ec0e71ad0a90ffe1909d27dac207f7680abba42d" : {
+ "balance" : "0x00",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ }
+ },
+ "pre" : {
+ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x09184e72a000",
+ "code" : "0x63c0406226600052604060006004601c600073ec0e71ad0a90ffe1909d27dac207f7680abba42d620249f0f15060005160015401600155",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
+ "balance" : "0x09184e72a000",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ }
+ }
+ },
+ "CallingCanonicalContractFromFork_CALLCODE" : {
+ "blocks" : [
+ {
+ "blockHeader" : {
+ "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1",
+ "difficulty" : "0x020000",
+ "extraData" : "0x",
+ "gasLimit" : "0x2fefd8",
+ "gasUsed" : "0x5208",
+ "hash" : "0ad1f4a43ccdcb63b96e7b311587288463717733b64545c0333ccbdc089688d5",
+ "mixHash" : "d464d3d8c2cf63b4ebdf6ad3218091b22e1a422407dfe7ea3822f8ce1abee51f",
+ "nonce" : "0e656c789a5bfc31",
+ "number" : "0x01",
+ "parentHash" : "e4a20be00ea735fa164d36b5445910a424fe1daa7104ae1ac4b4493c83f4141f",
+ "receiptTrie" : "f8cb5639e2463803ae389081e9857729b32ad5b48fb02240727315eb175b10e3",
+ "stateRoot" : "62b7f69b376d3aafc54235bc38e5e94b5972b663b4a76b3742ff513c67f1eb57",
+ "timestamp" : "0x5635010b",
+ "transactionsTrie" : "f80217763f8d00269566918ebd3c7729465f8b9818a0f437bf215a8190884d5d",
+ "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
+ },
+ "blocknumber" : "1",
+ "chainname" : "A",
+ "rlp" : "0xf90261f901f9a0e4a20be00ea735fa164d36b5445910a424fe1daa7104ae1ac4b4493c83f4141fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a062b7f69b376d3aafc54235bc38e5e94b5972b663b4a76b3742ff513c67f1eb57a0f80217763f8d00269566918ebd3c7729465f8b9818a0f437bf215a8190884d5da0f8cb5639e2463803ae389081e9857729b32ad5b48fb02240727315eb175b10e3b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845635010b80a0d464d3d8c2cf63b4ebdf6ad3218091b22e1a422407dfe7ea3822f8ce1abee51f880e656c789a5bfc31f862f86080018304cb2f94a95e7baea6a6c7c4c2dfeb977efac326af552d870a801ca0886931af2e4e440628e6e862e50944534ff3068c38cabb908dc708fa3736bfe0a03de874195b35641546d6c48409cca106127e5260a67fe7ed21a35dfb1f7a08b5c0",
+ "transactions" : [
+ {
+ "data" : "0x",
+ "gasLimit" : "0x04cb2f",
+ "gasPrice" : "0x01",
+ "nonce" : "0x00",
+ "r" : "0x886931af2e4e440628e6e862e50944534ff3068c38cabb908dc708fa3736bfe0",
+ "s" : "0x3de874195b35641546d6c48409cca106127e5260a67fe7ed21a35dfb1f7a08b5",
+ "to" : "a95e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "v" : "0x1c",
+ "value" : "0x0a"
+ }
+ ],
+ "uncleHeaders" : [
+ ]
+ },
+ {
+ "blockHeader" : {
+ "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1",
+ "difficulty" : "0x020040",
+ "extraData" : "0x",
+ "gasLimit" : "0x2fefd8",
+ "gasUsed" : "0xc0e3",
+ "hash" : "bc38e64180176dc9671f4f955427578094be1a7f5682fb0b85547ee5586f0347",
+ "mixHash" : "a4bb1397f2bbb7b1dd44e429ad3769b85509d3bddd5f3f689f759df02490e6ec",
+ "nonce" : "230961e372de074e",
+ "number" : "0x02",
+ "parentHash" : "0ad1f4a43ccdcb63b96e7b311587288463717733b64545c0333ccbdc089688d5",
+ "receiptTrie" : "26fe765a9578e9cfb12c8190b5ecdc381db59701306072a628c383ef1e18600d",
+ "stateRoot" : "3abf60a0b953f13e0f59e28b70cd5d2b4f706b2da26a7d2e0b3706ed52347b64",
+ "timestamp" : "0x5635010d",
+ "transactionsTrie" : "73605c813df801dea03161bc2f66993a59f86babc47efa9d0e952bc79a26fabd",
+ "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
+ },
+ "blocknumber" : "2",
+ "chainname" : "A",
+ "rlp" : "0xf902ccf901f9a00ad1f4a43ccdcb63b96e7b311587288463717733b64545c0333ccbdc089688d5a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a03abf60a0b953f13e0f59e28b70cd5d2b4f706b2da26a7d2e0b3706ed52347b64a073605c813df801dea03161bc2f66993a59f86babc47efa9d0e952bc79a26fabda026fe765a9578e9cfb12c8190b5ecdc381db59701306072a628c383ef1e18600db90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302004002832fefd882c0e3845635010d80a0a4bb1397f2bbb7b1dd44e429ad3769b85509d3bddd5f3f689f759df02490e6ec88230961e372de074ef8cdf8cb01018304cb2f8080b87e6060604052606e8060106000396000f360606040526000357c010000000000000000000000000000000000000000000000000000000090048063c0406226146037576035565b005b60406004506056565b6040518082815260200191505060405180910390f35b6000600560006000508190555060059050606b565b90561ca0b5a59222c12d3a9d52fddef1c7881a0dc7e72f5aed83be3cd7d7fdf868ec92a2a0551fea14e12dc99b6d78884bf5f0edae2da6ad4102d955057251d5d2aa8f54a5c0",
+ "transactions" : [
+ {
+ "data" : "0x6060604052606e8060106000396000f360606040526000357c010000000000000000000000000000000000000000000000000000000090048063c0406226146037576035565b005b60406004506056565b6040518082815260200191505060405180910390f35b6000600560006000508190555060059050606b565b9056",
+ "gasLimit" : "0x04cb2f",
+ "gasPrice" : "0x01",
+ "nonce" : "0x01",
+ "r" : "0xb5a59222c12d3a9d52fddef1c7881a0dc7e72f5aed83be3cd7d7fdf868ec92a2",
+ "s" : "0x551fea14e12dc99b6d78884bf5f0edae2da6ad4102d955057251d5d2aa8f54a5",
+ "to" : "",
+ "v" : "0x1c",
+ "value" : "0x00"
+ }
+ ],
+ "uncleHeaders" : [
+ ]
+ },
+ {
+ "blockHeader" : {
+ "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1",
+ "difficulty" : "0x020000",
+ "extraData" : "0x",
+ "gasLimit" : "0x2fefd8",
+ "gasUsed" : "0x0169ee",
+ "hash" : "62b2eb4d783709651581d6b29268e8d27ef0e5698db3a47b77391f072e2f1da0",
+ "mixHash" : "6b26b2021169267ae324b57e08acd817311f9b4f17caf5534594c8e8317b23cf",
+ "nonce" : "eb64fbe044331a43",
+ "number" : "0x03",
+ "parentHash" : "bc38e64180176dc9671f4f955427578094be1a7f5682fb0b85547ee5586f0347",
+ "receiptTrie" : "f18604ae1541250e60873accc7ef540ef0c2d643fe412f8c0d71dd96719060e3",
+ "stateRoot" : "34aa6cf4783c74854567e87a0da34cda048d974efcd66a87f80ea7677b319bb5",
+ "timestamp" : "0x56350125",
+ "transactionsTrie" : "9a5ba001326af3fd741edc763f3f0830c71a28e55a0a461d455792c2ad4918fe",
+ "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
+ },
+ "blocknumber" : "3",
+ "chainname" : "A",
+ "rlp" : "0xf902c4f901faa0bc38e64180176dc9671f4f955427578094be1a7f5682fb0b85547ee5586f0347a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a034aa6cf4783c74854567e87a0da34cda048d974efcd66a87f80ea7677b319bb5a09a5ba001326af3fd741edc763f3f0830c71a28e55a0a461d455792c2ad4918fea0f18604ae1541250e60873accc7ef540ef0c2d643fe412f8c0d71dd96719060e3b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000003832fefd8830169ee845635012580a06b26b2021169267ae324b57e08acd817311f9b4f17caf5534594c8e8317b23cf88eb64fbe044331a43f8c4f86002018304cb2f94095e7baea6a6c7c4c2dfeb977efac326af552d870a801ca015eb1cc916728b9799e55c489857727669afb2986433d5f54cde11faaed9f0eea05d36f6d06c34aae8d0a2a5895c8ba4a17ad46a5fa59f361cb3e7e01a23030e38f86003018304cb2f94095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba0a7b7f2fa93025fc1e6aa18c1aa07c32a456439754e196cb74f2f7d12cf3e840da02078cf840fb25fc3d858b2a85b622f21be0588b5c5d81d433427f6470e06a4a7c0",
+ "transactions" : [
+ {
+ "data" : "0x",
+ "gasLimit" : "0x04cb2f",
+ "gasPrice" : "0x01",
+ "nonce" : "0x02",
+ "r" : "0x15eb1cc916728b9799e55c489857727669afb2986433d5f54cde11faaed9f0ee",
+ "s" : "0x5d36f6d06c34aae8d0a2a5895c8ba4a17ad46a5fa59f361cb3e7e01a23030e38",
+ "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "v" : "0x1c",
+ "value" : "0x0a"
+ },
+ {
+ "data" : "0x",
+ "gasLimit" : "0x04cb2f",
+ "gasPrice" : "0x01",
+ "nonce" : "0x03",
+ "r" : "0xa7b7f2fa93025fc1e6aa18c1aa07c32a456439754e196cb74f2f7d12cf3e840d",
+ "s" : "0x2078cf840fb25fc3d858b2a85b622f21be0588b5c5d81d433427f6470e06a4a7",
+ "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "v" : "0x1b",
+ "value" : "0x0a"
+ }
+ ],
+ "uncleHeaders" : [
+ ]
+ },
+ {
+ "blockHeader" : {
+ "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1",
+ "difficulty" : "0x020000",
+ "extraData" : "0x",
+ "gasLimit" : "0x2fefd8",
+ "gasUsed" : "0x5208",
+ "hash" : "2bd5b44505f34000b3eb344a4884e7fee6f320a2ee50d64c150a804c66e09f7b",
+ "mixHash" : "e6057734a7878e44f7a825f57d1a750cc468a6fe4426501b4cd1254e8905962e",
+ "nonce" : "5ec9808a5ccff56b",
+ "number" : "0x01",
+ "parentHash" : "e4a20be00ea735fa164d36b5445910a424fe1daa7104ae1ac4b4493c83f4141f",
+ "receiptTrie" : "6e53faca3f080356ccc2032164edd80302217bb9febba7ef38f0198b32cfc598",
+ "stateRoot" : "6b3b817b850fbe23731c5f9f0a49780cb371aff4cd34dddf231b0ba0159a556a",
+ "timestamp" : "0x5635012a",
+ "transactionsTrie" : "da4ff51d21fb53978f91cc0bae0c01cfe4ed3485b999e4ef55d16441198223b5",
+ "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
+ },
+ "blocknumber" : "1",
+ "chainname" : "B",
+ "rlp" : "0xf90261f901f9a0e4a20be00ea735fa164d36b5445910a424fe1daa7104ae1ac4b4493c83f4141fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a06b3b817b850fbe23731c5f9f0a49780cb371aff4cd34dddf231b0ba0159a556aa0da4ff51d21fb53978f91cc0bae0c01cfe4ed3485b999e4ef55d16441198223b5a06e53faca3f080356ccc2032164edd80302217bb9febba7ef38f0198b32cfc598b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845635012a80a0e6057734a7878e44f7a825f57d1a750cc468a6fe4426501b4cd1254e8905962e885ec9808a5ccff56bf862f86080018304cb2f94a95e7baea6a6c7c4c2dfeb977efac326af552d8764801ba08d9a64385e3a24e9f6fdd49256c9f7d23cd501deaa55c5b4283fc58b2a601039a02518fdccd38aa1caaf4b49c907f750961f325d3d70104a22700b16d9af0d0557c0",
+ "transactions" : [
+ {
+ "data" : "0x",
+ "gasLimit" : "0x04cb2f",
+ "gasPrice" : "0x01",
+ "nonce" : "0x00",
+ "r" : "0x8d9a64385e3a24e9f6fdd49256c9f7d23cd501deaa55c5b4283fc58b2a601039",
+ "s" : "0x2518fdccd38aa1caaf4b49c907f750961f325d3d70104a22700b16d9af0d0557",
+ "to" : "a95e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "v" : "0x1b",
+ "value" : "0x64"
+ }
+ ],
+ "uncleHeaders" : [
+ ]
+ },
+ {
+ "blockHeader" : {
+ "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1",
+ "difficulty" : "0x020040",
+ "extraData" : "0x",
+ "gasLimit" : "0x2fefd8",
+ "gasUsed" : "0xa0b7",
+ "hash" : "cdb6183fee864e854f72db8f37f6d4df928a42c48d3640df108334eb7e8f0bc8",
+ "mixHash" : "ea68ae38156b9889fc171d035f97ff43cc3a1cd01d54578cb695e50502402a4d",
+ "nonce" : "cac4a75c76fff06d",
+ "number" : "0x02",
+ "parentHash" : "2bd5b44505f34000b3eb344a4884e7fee6f320a2ee50d64c150a804c66e09f7b",
+ "receiptTrie" : "8f9c0d4061c84b9911274a5367ea468825d8290f1cd3c0f00df7e0d284fe17c7",
+ "stateRoot" : "6e4b2007f4452ded67217523cea559188d156ef0d711eff7c4206f534cd67f25",
+ "timestamp" : "0x5635012c",
+ "transactionsTrie" : "7d90b54544a650e638e06d360a7dc2eee226c40373df61476b792ca3eccae35b",
+ "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
+ },
+ "blocknumber" : "2",
+ "chainname" : "B",
+ "rlp" : "0xf90261f901f9a02bd5b44505f34000b3eb344a4884e7fee6f320a2ee50d64c150a804c66e09f7ba01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a06e4b2007f4452ded67217523cea559188d156ef0d711eff7c4206f534cd67f25a07d90b54544a650e638e06d360a7dc2eee226c40373df61476b792ca3eccae35ba08f9c0d4061c84b9911274a5367ea468825d8290f1cd3c0f00df7e0d284fe17c7b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302004002832fefd882a0b7845635012c80a0ea68ae38156b9889fc171d035f97ff43cc3a1cd01d54578cb695e50502402a4d88cac4a75c76fff06df862f86001018304bb2b94095e7baea6a6c7c4c2dfeb977efac326af552d8764801ca0b66e1a2ee26689bdbb87af26141f167ded19e6b2041407bf4f80a36ce30a1b1ba0084e5def3deddc4ba0bd7dd8cebf184a8c3a3c2d5d9af5cb642bdf819d2e0be4c0",
+ "transactions" : [
+ {
+ "data" : "0x",
+ "gasLimit" : "0x04bb2b",
+ "gasPrice" : "0x01",
+ "nonce" : "0x01",
+ "r" : "0xb66e1a2ee26689bdbb87af26141f167ded19e6b2041407bf4f80a36ce30a1b1b",
+ "s" : "0x084e5def3deddc4ba0bd7dd8cebf184a8c3a3c2d5d9af5cb642bdf819d2e0be4",
+ "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "v" : "0x1c",
+ "value" : "0x64"
+ }
+ ],
+ "uncleHeaders" : [
+ ]
+ },
+ {
+ "blockHeader" : {
+ "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1",
+ "difficulty" : "0x020080",
+ "extraData" : "0x",
+ "gasLimit" : "0x2fefd8",
+ "gasUsed" : "0x661f",
+ "hash" : "7918ad3bbd992c246a06fbe2b5fd343429ecb36146035991934e5c71ae08d505",
+ "mixHash" : "ccce38b6c51ae562b753b0ffa3040b1de06da2991aee235f8a30274221f878bc",
+ "nonce" : "f5caf918a5c656f6",
+ "number" : "0x03",
+ "parentHash" : "cdb6183fee864e854f72db8f37f6d4df928a42c48d3640df108334eb7e8f0bc8",
+ "receiptTrie" : "4c3cb4b6fa89b2488e6abb64dbb714214c36daa178d85dcdeb19c5a93b52c127",
+ "stateRoot" : "0c8878763b3e881717a22d0ab6bcb93316e61958bf81aaf5a8d4a496d3827ef7",
+ "timestamp" : "0x56350130",
+ "transactionsTrie" : "2bcc69b52ae6b6e2aca544608d302ea49078786bdc26a35220b67c81fa39b996",
+ "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
+ },
+ "blocknumber" : "3",
+ "chainname" : "B",
+ "rlp" : "0xf90261f901f9a0cdb6183fee864e854f72db8f37f6d4df928a42c48d3640df108334eb7e8f0bc8a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a00c8878763b3e881717a22d0ab6bcb93316e61958bf81aaf5a8d4a496d3827ef7a02bcc69b52ae6b6e2aca544608d302ea49078786bdc26a35220b67c81fa39b996a04c3cb4b6fa89b2488e6abb64dbb714214c36daa178d85dcdeb19c5a93b52c127b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302008003832fefd882661f845635013080a0ccce38b6c51ae562b753b0ffa3040b1de06da2991aee235f8a30274221f878bc88f5caf918a5c656f6f862f86002018304bb2b94095e7baea6a6c7c4c2dfeb977efac326af552d8764801ba09e440c52e3bbb810164fb44c2f5b1882b5e9ae50a072737fe74a0a9152f7b973a070d7c1e0cb08639654481bdb953f7f73e4c16f75db1f7bb62fa2ce82bc4801c5c0",
+ "transactions" : [
+ {
+ "data" : "0x",
+ "gasLimit" : "0x04bb2b",
+ "gasPrice" : "0x01",
+ "nonce" : "0x02",
+ "r" : "0x9e440c52e3bbb810164fb44c2f5b1882b5e9ae50a072737fe74a0a9152f7b973",
+ "s" : "0x70d7c1e0cb08639654481bdb953f7f73e4c16f75db1f7bb62fa2ce82bc4801c5",
+ "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "v" : "0x1b",
+ "value" : "0x64"
+ }
+ ],
+ "uncleHeaders" : [
+ ]
+ }
+ ],
+ "genesisBlockHeader" : {
+ "bloom" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "coinbase" : "8888f1f195afa192cfee860698584c030f4c9db1",
+ "difficulty" : "0x020000",
+ "extraData" : "0x42",
+ "gasLimit" : "0x2fefd8",
+ "gasUsed" : "0x00",
+ "hash" : "e4a20be00ea735fa164d36b5445910a424fe1daa7104ae1ac4b4493c83f4141f",
+ "mixHash" : "18ff07126cac06690dab5957eb31b24a93d48fcefdc041ac9d452eb114308e19",
+ "nonce" : "a7284375c24d0f33",
+ "number" : "0x00",
+ "parentHash" : "0000000000000000000000000000000000000000000000000000000000000000",
+ "receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "stateRoot" : "e54c7b09c9fff198fe133bc102afb1a630d3615e28756e67317df7afc4d0dc31",
+ "timestamp" : "0x54c98c81",
+ "transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
+ },
+ "genesisRLP" : "0xf901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0e54c7b09c9fff198fe133bc102afb1a630d3615e28756e67317df7afc4d0dc31a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000080832fefd8808454c98c8142a018ff07126cac06690dab5957eb31b24a93d48fcefdc041ac9d452eb114308e1988a7284375c24d0f33c0c0",
+ "lastblockhash" : "7918ad3bbd992c246a06fbe2b5fd343429ecb36146035991934e5c71ae08d505",
+ "postState" : {
+ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x09184e72a0c8",
+ "code" : "0x63c0406226600052604060006004601c600073ec0e71ad0a90ffe1909d27dac207f7680abba42d620249f0f25060005160015401600155",
+ "nonce" : "0x00",
+ "storage" : {
+ "0x01" : "0x018080c44c"
+ }
+ },
+ "8888f1f195afa192cfee860698584c030f4c9db1" : {
+ "balance" : "0xd02ab486cedd58de",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
+ "balance" : "0x09184e7145f6",
+ "code" : "0x",
+ "nonce" : "0x03",
+ "storage" : {
+ }
+ },
+ "a95e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x64",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ }
+ },
+ "pre" : {
+ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x09184e72a000",
+ "code" : "0x63c0406226600052604060006004601c600073ec0e71ad0a90ffe1909d27dac207f7680abba42d620249f0f25060005160015401600155",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
+ "balance" : "0x09184e72a000",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ }
+ }
+ },
"OOGStateCopyContainingDeletedContract" : {
"blocks" : [
{
@@ -9,18 +636,18 @@
"extraData" : "0x",
"gasLimit" : "0x2fefd8",
"gasUsed" : "0x021ed0",
- "hash" : "014b39d133f9d4c8c7bdcee836d4fe604c756631b7898b460a617a421543e280",
- "mixHash" : "da18471f32318c815ec959b86d97c24c65fae58e3a902ebd5b7c284392560ad8",
- "nonce" : "1ac71d069dca27a7",
+ "hash" : "d417b685c098fea4b52fe20f0ab30c657ffa823f566132ce8f2a22e6a37133ed",
+ "mixHash" : "13a07c7ffeecf8c0a263457462c6582f7808e268c894e8321a8783b76c862d5d",
+ "nonce" : "258881cb5a28da1e",
"number" : "0x01",
- "parentHash" : "86cd7aadbf4f477ea464777479de88b172152fedacf5c7890c28b78254b6db5f",
+ "parentHash" : "e9d7655b8d6f19e3f8db65e178aeb2b21f28ee999224552738085f5d144f2f83",
"receiptTrie" : "3e21fb330e6981a4657f97fc2ace223c75f17d83f0fa017586d5b872b48b4824",
"stateRoot" : "042cf0272a105f572ee8a5a3014aef7fff760fc3ce18c2aedac0cc55c152a4b9",
- "timestamp" : "0x561bbe06",
+ "timestamp" : "0x56350135",
"transactionsTrie" : "5c3eb7e26c39308ede0b5a0b9b403fb89c3369deda106c32faf7d0def6f421d2",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
- "rlp" : "0xf902eef901faa086cd7aadbf4f477ea464777479de88b172152fedacf5c7890c28b78254b6db5fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0042cf0272a105f572ee8a5a3014aef7fff760fc3ce18c2aedac0cc55c152a4b9a05c3eb7e26c39308ede0b5a0b9b403fb89c3369deda106c32faf7d0def6f421d2a03e21fb330e6981a4657f97fc2ace223c75f17d83f0fa017586d5b872b48b4824b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008303863001832fefd883021ed084561bbe0680a0da18471f32318c815ec959b86d97c24c65fae58e3a902ebd5b7c284392560ad8881ac71d069dca27a7f8eef864800a830493e09464306ec3f51a26dcf19f5da0c043040f54f4eca501840c5feb5d1ba00cf2cc4de3013273d0aae3cf36cdb6cf152573f7a5b99fe2c514a845bbb98a93a048f4aa20b37303bf4f2c0e7e5f6c178814f99ab4d3d98cf9382185f1ae256b7ff886010a830493e0942e0de3fc10a88911ff857126db1a5f0da6f251738203eaa4fc49c80e00000000000000000000000064306ec3f51a26dcf19f5da0c043040f54f4eca51ca0c9f11f1b4aedd9c1d99a6e2aea6f9ce90bdd6bb6063193715fdb43e77029346fa03440044e3aa54293e887f1751146bf915e73c39eae7da82b75a6d2c7a31d252bc0",
+ "rlp" : "0xf902eef901faa0e9d7655b8d6f19e3f8db65e178aeb2b21f28ee999224552738085f5d144f2f83a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0042cf0272a105f572ee8a5a3014aef7fff760fc3ce18c2aedac0cc55c152a4b9a05c3eb7e26c39308ede0b5a0b9b403fb89c3369deda106c32faf7d0def6f421d2a03e21fb330e6981a4657f97fc2ace223c75f17d83f0fa017586d5b872b48b4824b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008303863001832fefd883021ed0845635013580a013a07c7ffeecf8c0a263457462c6582f7808e268c894e8321a8783b76c862d5d88258881cb5a28da1ef8eef864800a830493e09464306ec3f51a26dcf19f5da0c043040f54f4eca501840c5feb5d1ba00cf2cc4de3013273d0aae3cf36cdb6cf152573f7a5b99fe2c514a845bbb98a93a048f4aa20b37303bf4f2c0e7e5f6c178814f99ab4d3d98cf9382185f1ae256b7ff886010a830493e0942e0de3fc10a88911ff857126db1a5f0da6f251738203eaa4fc49c80e00000000000000000000000064306ec3f51a26dcf19f5da0c043040f54f4eca51ca0c9f11f1b4aedd9c1d99a6e2aea6f9ce90bdd6bb6063193715fdb43e77029346fa03440044e3aa54293e887f1751146bf915e73c39eae7da82b75a6d2c7a31d252bc0",
"transactions" : [
{
"data" : "0x0c5feb5d",
@@ -56,9 +683,9 @@
"extraData" : "0x42",
"gasLimit" : "0x2fefd8",
"gasUsed" : "0x00",
- "hash" : "86cd7aadbf4f477ea464777479de88b172152fedacf5c7890c28b78254b6db5f",
- "mixHash" : "74b52d9518d9bf2a1ca37de5defc10ced5f94712cf550af50abf15907f4f4450",
- "nonce" : "bffdbdd2a34e324c",
+ "hash" : "e9d7655b8d6f19e3f8db65e178aeb2b21f28ee999224552738085f5d144f2f83",
+ "mixHash" : "34e0efd31ff30732e7d4e0786a89c28e2c2c0229bdc061854ddd32678c818614",
+ "nonce" : "c7fd8fd9192ddfc0",
"number" : "0x00",
"parentHash" : "0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
@@ -67,8 +694,8 @@
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
- "genesisRLP" : "0xf901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a02b9f478fe39744a8c17eb48ae7bf86f6a47031a823a632f9bd661b59978aeefda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830386a080832fefd8808454c98c8142a074b52d9518d9bf2a1ca37de5defc10ced5f94712cf550af50abf15907f4f445088bffdbdd2a34e324cc0c0",
- "lastblockhash" : "014b39d133f9d4c8c7bdcee836d4fe604c756631b7898b460a617a421543e280",
+ "genesisRLP" : "0xf901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a02b9f478fe39744a8c17eb48ae7bf86f6a47031a823a632f9bd661b59978aeefda056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830386a080832fefd8808454c98c8142a034e0efd31ff30732e7d4e0786a89c28e2c2c0229bdc061854ddd32678c81861488c7fd8fd9192ddfc0c0c0",
+ "lastblockhash" : "d417b685c098fea4b52fe20f0ab30c657ffa823f566132ce8f2a22e6a37133ed",
"postState" : {
"2e0de3fc10a88911ff857126db1a5f0da6f25173" : {
"balance" : "0x03eb",
@@ -134,18 +761,18 @@
"extraData" : "0x",
"gasLimit" : "0x2fefd8",
"gasUsed" : "0xcdc7",
- "hash" : "31a2017106ea3824e38473942bd2e3b2821c8b3180eb64b25f9beb00a8a1fcb7",
- "mixHash" : "a713c7d424b0945b3e2212c9710efe7c704750fa5553c6b7eb86d2a1a19d1f79",
- "nonce" : "89725425afc4aff2",
+ "hash" : "6c127a1d91e8b8a0fb8a599de33543f45e7117ae331b39c0888d3633a46fc2e1",
+ "mixHash" : "3b80d86b0152f2d4dce75c4fbd80d65c4f89316942c929ac072bfe93246649d2",
+ "nonce" : "78f64b89b638a158",
"number" : "0x01",
- "parentHash" : "577f0011f92e09511e4ab256b72e1ac11610c3728179e0b7bd76aad4426adb92",
+ "parentHash" : "8eac9bbc9794f3a90c6d846528c4f505204797b19accafee93a909ee2fe075f4",
"receiptTrie" : "56e592ae6cf92b6c205e50d9cdbf1d3c5fe7f9fbc2bf219b93855107518e7e7f",
"stateRoot" : "7564aa479e81b3f9a05b0f99193fdd7367ccc0e74d140249d943ce0df904ba02",
- "timestamp" : "0x561bbe0b",
+ "timestamp" : "0x56350139",
"transactionsTrie" : "fcfe9f2203bd98342867117fa3de299a09578371efd04fc9e76a46f7f1fda4bb",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
- "rlp" : "0xf9032ef901f9a0577f0011f92e09511e4ab256b72e1ac11610c3728179e0b7bd76aad4426adb92a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a07564aa479e81b3f9a05b0f99193fdd7367ccc0e74d140249d943ce0df904ba02a0fcfe9f2203bd98342867117fa3de299a09578371efd04fc9e76a46f7f1fda4bba056e592ae6cf92b6c205e50d9cdbf1d3c5fe7f9fbc2bf219b93855107518e7e7fb90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008303863001832fefd882cdc784561bbe0b80a0a713c7d424b0945b3e2212c9710efe7c704750fa5553c6b7eb86d2a1a19d1f798889725425afc4aff2f9012ef866800a8307a120948888f1f195afa192cfee860698584c030f4c9db18203e9840c55699c1ba091fc4c402ced19b984e953546d3fc786c46a79c9f0c7918b8f3343dc529ef0e5a0546d89230c90ca8bf7988a826430d4771ab3a67cc0f3cb8019d67ab10ec10524f861010a82c3509400000000000000000000000000000000000000008203e8801ba0b03ab16ed211bf447ac030216ab088f18367ee51303545d2957990e9d3a28f10a07f18dd055139f7ac5558997b80ccae799ab6fbad2326799db509a9d4e5a52d72f861020a82c3509400000000000000000000000000000000000000008203ea801ba00925abd1221d388622138f4bae46803313f297001e96fec22dc4268fca5b5a82a055cd8142bcec39f80b359aa089f6a70568d23a67048026703981fad9339ef5d4c0",
+ "rlp" : "0xf9032ef901f9a08eac9bbc9794f3a90c6d846528c4f505204797b19accafee93a909ee2fe075f4a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a07564aa479e81b3f9a05b0f99193fdd7367ccc0e74d140249d943ce0df904ba02a0fcfe9f2203bd98342867117fa3de299a09578371efd04fc9e76a46f7f1fda4bba056e592ae6cf92b6c205e50d9cdbf1d3c5fe7f9fbc2bf219b93855107518e7e7fb90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008303863001832fefd882cdc7845635013980a03b80d86b0152f2d4dce75c4fbd80d65c4f89316942c929ac072bfe93246649d28878f64b89b638a158f9012ef866800a8307a120948888f1f195afa192cfee860698584c030f4c9db18203e9840c55699c1ba091fc4c402ced19b984e953546d3fc786c46a79c9f0c7918b8f3343dc529ef0e5a0546d89230c90ca8bf7988a826430d4771ab3a67cc0f3cb8019d67ab10ec10524f861010a82c3509400000000000000000000000000000000000000008203e8801ba0b03ab16ed211bf447ac030216ab088f18367ee51303545d2957990e9d3a28f10a07f18dd055139f7ac5558997b80ccae799ab6fbad2326799db509a9d4e5a52d72f861020a82c3509400000000000000000000000000000000000000008203ea801ba00925abd1221d388622138f4bae46803313f297001e96fec22dc4268fca5b5a82a055cd8142bcec39f80b359aa089f6a70568d23a67048026703981fad9339ef5d4c0",
"transactions" : [
{
"data" : "0x0c55699c",
@@ -192,9 +819,9 @@
"extraData" : "0x42",
"gasLimit" : "0x2fefd8",
"gasUsed" : "0x00",
- "hash" : "577f0011f92e09511e4ab256b72e1ac11610c3728179e0b7bd76aad4426adb92",
- "mixHash" : "7d92760f89c04bc4d1b3716423d0cc5c89244a513e960979149cea0b5162ea91",
- "nonce" : "a35294f86be5ddb3",
+ "hash" : "8eac9bbc9794f3a90c6d846528c4f505204797b19accafee93a909ee2fe075f4",
+ "mixHash" : "5a19e3e9b4eea5cfe73795f2a499d99ad6e959445d71bf272dbe18d65cbd8927",
+ "nonce" : "c06ebf5900068792",
"number" : "0x00",
"parentHash" : "0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
@@ -203,8 +830,8 @@
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
- "genesisRLP" : "0xf901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a04941fba20142b10d43d0a893dfa4f5eedcbcb4b55c8554efd71e226624d9b37ca056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830386a080832fefd8808454c98c8142a07d92760f89c04bc4d1b3716423d0cc5c89244a513e960979149cea0b5162ea9188a35294f86be5ddb3c0c0",
- "lastblockhash" : "31a2017106ea3824e38473942bd2e3b2821c8b3180eb64b25f9beb00a8a1fcb7",
+ "genesisRLP" : "0xf901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a04941fba20142b10d43d0a893dfa4f5eedcbcb4b55c8554efd71e226624d9b37ca056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830386a080832fefd8808454c98c8142a05a19e3e9b4eea5cfe73795f2a499d99ad6e959445d71bf272dbe18d65cbd892788c06ebf5900068792c0c0",
+ "lastblockhash" : "6c127a1d91e8b8a0fb8a599de33543f45e7117ae331b39c0888d3633a46fc2e1",
"postState" : {
"0000000000000000000000000000000000000000" : {
"balance" : "0x07d2",
@@ -255,18 +882,18 @@
"extraData" : "0x",
"gasLimit" : "0x2fefd8",
"gasUsed" : "0x2906",
- "hash" : "8ef175b4e1686b5356e5dcd67d011ba533930c5b9c71299ed290a0760094e471",
- "mixHash" : "d09d59d77aa2d58a4c0467c2e154e4196c48d4e798053707672566b4939bd5d8",
- "nonce" : "e1801fd770254ee7",
+ "hash" : "063cc5824b146fe167103c10f9752f94832017e8df1cf008387b7eeed4b57d90",
+ "mixHash" : "9f93156773ad3198bd97392034b6fd6145115431860bbfd1b56cd091d16f54d6",
+ "nonce" : "2c38c3edd4a8925a",
"number" : "0x01",
- "parentHash" : "db30263a5f8d24f8b8eed1a1145188e137f0b54c4383cb7691a1a792abf2b33e",
+ "parentHash" : "24b1e2b08b6f4cbe86f29681003cff67ff65ae5ac826743734c2a20f29be124d",
"receiptTrie" : "45a1e5d92294ba129a8dcd41456fd5ffc2eadb690b16916783910b77d05e61c8",
"stateRoot" : "2634dc0c8fab13c3e11d813a506945f50b03f86221b1713ee7a33229da24f943",
- "timestamp" : "0x561bbe10",
+ "timestamp" : "0x5635013d",
"transactionsTrie" : "53d5b71a8fbb9590de82d69dfa4ac31923b0c8afce0d30d0d8d1e931f25030dc",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
- "rlp" : "0xf90260f901f9a0db30263a5f8d24f8b8eed1a1145188e137f0b54c4383cb7691a1a792abf2b33ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a02634dc0c8fab13c3e11d813a506945f50b03f86221b1713ee7a33229da24f943a053d5b71a8fbb9590de82d69dfa4ac31923b0c8afce0d30d0d8d1e931f25030dca045a1e5d92294ba129a8dcd41456fd5ffc2eadb690b16916783910b77d05e61c8b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008303863001832fefd882290684561bbe1080a0d09d59d77aa2d58a4c0467c2e154e4196c48d4e798053707672566b4939bd5d888e1801fd770254ee7f861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba0f3266921c93d600c43f6fa4724b7abae079b35b9e95df592f95f9f3445e94c88a012f977552ebdb7a492cf35f3106df16ccb4576ebad4113056ee1f52cbe4978c1c0",
+ "rlp" : "0xf90260f901f9a024b1e2b08b6f4cbe86f29681003cff67ff65ae5ac826743734c2a20f29be124da01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a02634dc0c8fab13c3e11d813a506945f50b03f86221b1713ee7a33229da24f943a053d5b71a8fbb9590de82d69dfa4ac31923b0c8afce0d30d0d8d1e931f25030dca045a1e5d92294ba129a8dcd41456fd5ffc2eadb690b16916783910b77d05e61c8b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008303863001832fefd8822906845635013d80a09f93156773ad3198bd97392034b6fd6145115431860bbfd1b56cd091d16f54d6882c38c3edd4a8925af861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba0f3266921c93d600c43f6fa4724b7abae079b35b9e95df592f95f9f3445e94c88a012f977552ebdb7a492cf35f3106df16ccb4576ebad4113056ee1f52cbe4978c1c0",
"transactions" : [
{
"data" : "0x",
@@ -291,18 +918,18 @@
"extraData" : "0x",
"gasLimit" : "0x2fefd8",
"gasUsed" : "0x5208",
- "hash" : "931242008fdd7af531ca0a94fe481592a2ec46813d67c246e6d53f5a09cdf2b1",
- "mixHash" : "d7cd6cc9a86a3f8818843fa8ee25c55f62f491d6246bb248613c810d7b3c5423",
- "nonce" : "f32510acc0379d94",
+ "hash" : "e25d932ffcff8693f7b4bd621b6f27cd1ebe0584d6fc65bd7d21ab55473cb22c",
+ "mixHash" : "3602c3d7ec0bf7e052f3e4e6e3376745516487e716e1d13a7525fb96f872875f",
+ "nonce" : "70ea52ba60044044",
"number" : "0x02",
- "parentHash" : "8ef175b4e1686b5356e5dcd67d011ba533930c5b9c71299ed290a0760094e471",
+ "parentHash" : "063cc5824b146fe167103c10f9752f94832017e8df1cf008387b7eeed4b57d90",
"receiptTrie" : "6952621e59f670b82f765b40711cfbc3fff5eb3ca56c6c1509cb8bf915ae94da",
"stateRoot" : "600002151e3bc50cd8136dcbfbc8dedf3ec063710d46e5ba1cc6f5d1796f1ea5",
- "timestamp" : "0x561bbe13",
+ "timestamp" : "0x56350141",
"transactionsTrie" : "326814571a40c9c7db48527b6819d6a25c03735dd63a9762911729510d07a45c",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
- "rlp" : "0xf90262f901f9a08ef175b4e1686b5356e5dcd67d011ba533930c5b9c71299ed290a0760094e471a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0600002151e3bc50cd8136dcbfbc8dedf3ec063710d46e5ba1cc6f5d1796f1ea5a0326814571a40c9c7db48527b6819d6a25c03735dd63a9762911729510d07a45ca06952621e59f670b82f765b40711cfbc3fff5eb3ca56c6c1509cb8bf915ae94dab9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830386a002832fefd882520884561bbe1380a0d7cd6cc9a86a3f8818843fa8ee25c55f62f491d6246bb248613c810d7b3c542388f32510acc0379d94f863f861010a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d878203e8801ba0823762ef8e6fc0498753553d5defe18004462e636cf76eb06515c7652aac3040a07239c31a3df7ea1e894d71558ac36179c97446bc630f3f4b8d035ee436b6ad46c0",
+ "rlp" : "0xf90262f901f9a0063cc5824b146fe167103c10f9752f94832017e8df1cf008387b7eeed4b57d90a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0600002151e3bc50cd8136dcbfbc8dedf3ec063710d46e5ba1cc6f5d1796f1ea5a0326814571a40c9c7db48527b6819d6a25c03735dd63a9762911729510d07a45ca06952621e59f670b82f765b40711cfbc3fff5eb3ca56c6c1509cb8bf915ae94dab9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830386a002832fefd8825208845635014180a03602c3d7ec0bf7e052f3e4e6e3376745516487e716e1d13a7525fb96f872875f8870ea52ba60044044f863f861010a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d878203e8801ba0823762ef8e6fc0498753553d5defe18004462e636cf76eb06515c7652aac3040a07239c31a3df7ea1e894d71558ac36179c97446bc630f3f4b8d035ee436b6ad46c0",
"transactions" : [
{
"data" : "0x",
@@ -327,9 +954,9 @@
"extraData" : "0x42",
"gasLimit" : "0x2fefd8",
"gasUsed" : "0x00",
- "hash" : "db30263a5f8d24f8b8eed1a1145188e137f0b54c4383cb7691a1a792abf2b33e",
- "mixHash" : "30ff6ec57e2a78935672f5bc465560aaea35ec4d0205d6782b5f8d75c8223204",
- "nonce" : "0727621fcf9d0354",
+ "hash" : "24b1e2b08b6f4cbe86f29681003cff67ff65ae5ac826743734c2a20f29be124d",
+ "mixHash" : "f2f6fd4d5804fd2757e4b0b435433731f995ca5724e5b815270d3df71dc3ae03",
+ "nonce" : "a97c07a113c1d438",
"number" : "0x00",
"parentHash" : "0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
@@ -338,8 +965,8 @@
"transactionsTrie" : "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
},
- "genesisRLP" : "0xf901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0c53ee8f6624173f7efcc6c8a9bd54181fb079a52e0e1a78e16de4a6a5b74071ba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830386a080832fefd8808454c98c8142a030ff6ec57e2a78935672f5bc465560aaea35ec4d0205d6782b5f8d75c8223204880727621fcf9d0354c0c0",
- "lastblockhash" : "931242008fdd7af531ca0a94fe481592a2ec46813d67c246e6d53f5a09cdf2b1",
+ "genesisRLP" : "0xf901fcf901f7a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0c53ee8f6624173f7efcc6c8a9bd54181fb079a52e0e1a78e16de4a6a5b74071ba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830386a080832fefd8808454c98c8142a0f2f6fd4d5804fd2757e4b0b435433731f995ca5724e5b815270d3df71dc3ae0388a97c07a113c1d438c0c0",
+ "lastblockhash" : "e25d932ffcff8693f7b4bd621b6f27cd1ebe0584d6fc65bd7d21ab55473cb22c",
"postState" : {
"0000000000000000000000000000000000000080" : {
"balance" : "0x0186aa",
diff --git a/tests/files/StateTests/stPreCompiledContracts.json b/tests/files/StateTests/stPreCompiledContracts.json
index 7af011873..981e66b66 100644
--- a/tests/files/StateTests/stPreCompiledContracts.json
+++ b/tests/files/StateTests/stPreCompiledContracts.json
@@ -3498,6 +3498,150 @@
"value" : "0x0186a0"
}
},
+ "CallEcrecoverCheckLength" : {
+ "env" : {
+ "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
+ "currentDifficulty" : "0x0100",
+ "currentGasLimit" : "0x989680",
+ "currentNumber" : "0x00",
+ "currentTimestamp" : "0x01",
+ "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
+ },
+ "logs" : [
+ ],
+ "out" : "0x",
+ "post" : {
+ "0000000000000000000000000000000000000001" : {
+ "balance" : "0x00",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x0132b3a0",
+ "code" : "0x7f11223344556677889900112233445566778899001122334455667788990011226080527f18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c600052601c6020527f73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f6040527feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549606052602060806080600060006001620493e0f16002556080516000556080600155",
+ "nonce" : "0x00",
+ "storage" : {
+ "0x00" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
+ "0x01" : "0x80",
+ "0x02" : "0x01"
+ }
+ },
+ "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
+ "balance" : "0x01aa53",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
+ "balance" : "0x0de0b6b3a760cf0d",
+ "code" : "0x",
+ "nonce" : "0x01",
+ "storage" : {
+ }
+ }
+ },
+ "postStateRoot" : "64f7a0ea764350db949d9a9f9ec5e5400acef5bad92ed340011266791ad5b74b",
+ "pre" : {
+ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x01312d00",
+ "code" : "0x7f11223344556677889900112233445566778899001122334455667788990011226080527f18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c600052601c6020527f73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f6040527feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549606052602060806080600060006001620493e0f16002556080516000556080600155",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
+ "balance" : "0x0de0b6b3a7640000",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ }
+ },
+ "transaction" : {
+ "data" : "",
+ "gasLimit" : "0x37ba90",
+ "gasPrice" : "0x01",
+ "nonce" : "0x00",
+ "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
+ "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "value" : "0x0186a0"
+ }
+ },
+ "CallEcrecoverCheckLengthWrongV" : {
+ "env" : {
+ "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
+ "currentDifficulty" : "0x0100",
+ "currentGasLimit" : "0x989680",
+ "currentNumber" : "0x00",
+ "currentTimestamp" : "0x01",
+ "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
+ },
+ "logs" : [
+ ],
+ "out" : "0x",
+ "post" : {
+ "0000000000000000000000000000000000000001" : {
+ "balance" : "0x00",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x0132b3a0",
+ "code" : "0x7f11223344556677889900112233445566778899001122334455667788990011226080527f18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c600052601d6020527f73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f6040527feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549606052602060806080600060006001620493e0f16002556080516000556080600155",
+ "nonce" : "0x00",
+ "storage" : {
+ "0x00" : "0x1122334455667788990011223344556677889900112233445566778899001122",
+ "0x01" : "0x80",
+ "0x02" : "0x01"
+ }
+ },
+ "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
+ "balance" : "0x01aa53",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
+ "balance" : "0x0de0b6b3a760cf0d",
+ "code" : "0x",
+ "nonce" : "0x01",
+ "storage" : {
+ }
+ }
+ },
+ "postStateRoot" : "67af1f3bd8f8619c936e56b87b5910c8beeb8035be00fddeeb3346a5aa31e230",
+ "pre" : {
+ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x01312d00",
+ "code" : "0x7f11223344556677889900112233445566778899001122334455667788990011226080527f18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c600052601d6020527f73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f6040527feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549606052602060806080600060006001620493e0f16002556080516000556080600155",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
+ "balance" : "0x0de0b6b3a7640000",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ }
+ },
+ "transaction" : {
+ "data" : "",
+ "gasLimit" : "0x37ba90",
+ "gasPrice" : "0x01",
+ "nonce" : "0x00",
+ "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
+ "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "value" : "0x0186a0"
+ }
+ },
"CallEcrecoverH_prefixed0" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
diff --git a/tests/files/StateTests/stSystemOperationsTest.json b/tests/files/StateTests/stSystemOperationsTest.json
index 7f7cb1220..94ca013e3 100644
--- a/tests/files/StateTests/stSystemOperationsTest.json
+++ b/tests/files/StateTests/stSystemOperationsTest.json
@@ -18676,6 +18676,61 @@
"value" : "0x0186a0"
}
},
+ "suicideSendEtherPostDeath" : {
+ "env" : {
+ "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
+ "currentDifficulty" : "0x0100",
+ "currentGasLimit" : "0x989680",
+ "currentNumber" : "0x00",
+ "currentTimestamp" : "0x01",
+ "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
+ },
+ "logs" : [
+ ],
+ "out" : "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "post" : {
+ "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" : {
+ "balance" : "0x2aa8",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
+ "balance" : "0x0de0b6b3a7624eb8",
+ "code" : "0x",
+ "nonce" : "0x01",
+ "storage" : {
+ }
+ }
+ },
+ "postStateRoot" : "9f3c63ff818c14e4b56e5fa1fc03a76725f598bcac256668185ec51dfc1d7f5f",
+ "pre" : {
+ "095e7baea6a6c7c4c2dfeb977efac326af552d87" : {
+ "balance" : "0x0de0b6b3a7640000",
+ "code" : "0x60606040526000357c01000000000000000000000000000000000000000000000000000000009004806335f46994146100445780634d536fe31461005157610042565b005b61004f600450610072565b005b61005c60045061008d565b6040518082815260200191505060405180910390f35b3073ffffffffffffffffffffffffffffffffffffffff16ff5b565b600060003073ffffffffffffffffffffffffffffffffffffffff166335f46994604051817c01000000000000000000000000000000000000000000000000000000000281526004018090506000604051808303816000876161da5a03f115610002575050503073ffffffffffffffffffffffffffffffffffffffff163190503373ffffffffffffffffffffffffffffffffffffffff16600082604051809050600060405180830381858888f1935050505050809150610147565b509056",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ },
+ "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
+ "balance" : "0x0de0b6b3a7640000",
+ "code" : "0x",
+ "nonce" : "0x00",
+ "storage" : {
+ }
+ }
+ },
+ "transaction" : {
+ "data" : "0x4d536fe3",
+ "gasLimit" : "0x2dc6c0",
+ "gasPrice" : "0x01",
+ "nonce" : "0x00",
+ "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
+ "to" : "095e7baea6a6c7c4c2dfeb977efac326af552d87",
+ "value" : "0x0186a0"
+ }
+ },
"suicideSendEtherToMe" : {
"env" : {
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
diff --git a/tests/init.go b/tests/init.go
index 3f8b8c684..a86970499 100644
--- a/tests/init.go
+++ b/tests/init.go
@@ -56,13 +56,16 @@ var (
VmSkipTests = []string{}
)
+// Disable reporting bad blocks for the tests
+func init() {
+ core.DisableBadBlockReporting = true
+}
+
func readJson(reader io.Reader, value interface{}) error {
data, err := ioutil.ReadAll(reader)
if err != nil {
return fmt.Errorf("Error reading JSON file", err.Error())
}
-
- core.DisableBadBlockReporting = true
if err = json.Unmarshal(data, &value); err != nil {
if syntaxerr, ok := err.(*json.SyntaxError); ok {
line := findLine(data, syntaxerr.Offset)
diff --git a/whisper/whisper_test.go b/whisper/whisper_test.go
index b5a919984..1a9a8667a 100644
--- a/whisper/whisper_test.go
+++ b/whisper/whisper_test.go
@@ -189,13 +189,22 @@ func TestMessageExpiration(t *testing.T) {
t.Fatalf("failed to inject message: %v", err)
}
// Check that the message is inside the cache
- if _, ok := node.messages[envelope.Hash()]; !ok {
+ node.poolMu.RLock()
+ _, found := node.messages[envelope.Hash()]
+ node.poolMu.RUnlock()
+
+ if !found {
t.Fatalf("message not found in cache")
}
// Wait for expiration and check cache again
time.Sleep(time.Second) // wait for expiration
time.Sleep(expirationCycle) // wait for cleanup cycle
- if _, ok := node.messages[envelope.Hash()]; ok {
+
+ node.poolMu.RLock()
+ _, found = node.messages[envelope.Hash()]
+ node.poolMu.RUnlock()
+
+ if found {
t.Fatalf("message not expired from cache")
}
}
diff --git a/xeth/xeth.go b/xeth/xeth.go
index 35e6dd52d..19c42a9a3 100644
--- a/xeth/xeth.go
+++ b/xeth/xeth.go
@@ -322,44 +322,11 @@ func (self *XEth) EthBlockByHash(strHash string) *types.Block {
return block
}
-func (self *XEth) EthTransactionByHash(hash string) (tx *types.Transaction, blhash common.Hash, blnum *big.Int, txi uint64) {
- // Due to increasing return params and need to determine if this is from transaction pool or
- // some chain, this probably needs to be refactored for more expressiveness
- data, _ := self.backend.ChainDb().Get(common.FromHex(hash))
- if len(data) != 0 {
- dtx := new(types.Transaction)
- if err := rlp.DecodeBytes(data, dtx); err != nil {
- glog.V(logger.Error).Infoln(err)
- return
- }
- tx = dtx
- } else { // check pending transactions
- tx = self.backend.TxPool().GetTransaction(common.HexToHash(hash))
- }
-
- // meta
- var txExtra struct {
- BlockHash common.Hash
- BlockIndex uint64
- Index uint64
- }
-
- v, dberr := self.backend.ChainDb().Get(append(common.FromHex(hash), 0x0001))
- // TODO check specifically for ErrNotFound
- if dberr != nil {
- return
- }
- r := bytes.NewReader(v)
- err := rlp.Decode(r, &txExtra)
- if err == nil {
- blhash = txExtra.BlockHash
- blnum = big.NewInt(int64(txExtra.BlockIndex))
- txi = txExtra.Index
- } else {
- glog.V(logger.Error).Infoln(err)
+func (self *XEth) EthTransactionByHash(hash string) (*types.Transaction, common.Hash, uint64, uint64) {
+ if tx, hash, number, index := core.GetTransaction(self.backend.ChainDb(), common.HexToHash(hash)); tx != nil {
+ return tx, hash, number, index
}
-
- return
+ return self.backend.TxPool().GetTransaction(common.HexToHash(hash)), common.Hash{}, 0, 0
}
func (self *XEth) BlockByNumber(num int64) *Block {
@@ -379,7 +346,7 @@ func (self *XEth) CurrentBlock() *types.Block {
}
func (self *XEth) GetBlockReceipts(bhash common.Hash) types.Receipts {
- return self.backend.BlockProcessor().GetBlockReceipts(bhash)
+ return core.GetBlockReceipts(self.backend.ChainDb(), bhash)
}
func (self *XEth) GetTxReceipt(txhash common.Hash) *types.Receipt {
@@ -912,6 +879,60 @@ func (self *XEth) Frontend() Frontend {
return self.frontend
}
+func (self *XEth) SignTransaction(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (*types.Transaction, error) {
+ if len(toStr) > 0 && toStr != "0x" && !isAddress(toStr) {
+ return nil, errors.New("Invalid address")
+ }
+
+ var (
+ from = common.HexToAddress(fromStr)
+ to = common.HexToAddress(toStr)
+ value = common.Big(valueStr)
+ gas *big.Int
+ price *big.Int
+ data []byte
+ contractCreation bool
+ )
+
+ if len(gasStr) == 0 {
+ gas = DefaultGas()
+ } else {
+ gas = common.Big(gasStr)
+ }
+
+ if len(gasPriceStr) == 0 {
+ price = self.DefaultGasPrice()
+ } else {
+ price = common.Big(gasPriceStr)
+ }
+
+ data = common.FromHex(codeStr)
+ if len(toStr) == 0 {
+ contractCreation = true
+ }
+
+ var nonce uint64
+ if len(nonceStr) != 0 {
+ nonce = common.Big(nonceStr).Uint64()
+ } else {
+ state := self.backend.TxPool().State()
+ nonce = state.GetNonce(from)
+ }
+ var tx *types.Transaction
+ if contractCreation {
+ tx = types.NewContractCreation(nonce, value, gas, price, data)
+ } else {
+ tx = types.NewTransaction(nonce, to, value, gas, price, data)
+ }
+
+ signed, err := self.sign(tx, from, false)
+ if err != nil {
+ return nil, err
+ }
+
+ return signed, nil
+}
+
func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
// this minimalistic recoding is enough (works for natspec.js)