Merge branch 'develop' into release/1.3.2

Conflicts:
	VERSION
	cmd/geth/main.go
pull/2985/head v1.3.2
Jeffrey Wilcke 9 years ago
commit 5490437942
  1. 67
      Makefile
  2. 2
      VERSION
  3. 12
      accounts/abi/abi.go
  4. 75
      accounts/abi/abi_test.go
  5. 14
      accounts/abi/type.go
  6. 35
      cmd/geth/main.go
  7. 1
      cmd/geth/usage.go
  8. 10
      cmd/utils/cmd.go
  9. 6
      cmd/utils/flags.go
  10. 41
      cmd/utils/legalese.go
  11. 1
      core/bench_test.go
  12. 460
      core/block_processor.go
  13. 243
      core/block_validator.go
  14. 6
      core/block_validator_test.go
  15. 136
      core/blockchain.go
  16. 147
      core/blockchain_test.go
  17. 11
      core/chain_makers.go
  18. 9
      core/chain_makers_test.go
  19. 178
      core/database_util.go
  20. 162
      core/database_util_test.go
  21. 46
      core/gaspool.go
  22. 2
      core/genesis.go
  23. 107
      core/state_processor.go
  24. 171
      core/transaction_util.go
  25. 70
      core/types.go
  26. 11
      core/vm/runtime/doc.go
  27. 106
      core/vm/runtime/env.go
  28. 121
      core/vm/runtime/runtime.go
  29. 26
      core/vm/runtime/runtime_example_test.go
  30. 120
      core/vm/runtime/runtime_test.go
  31. 33
      crypto/secp256k1/panic_cb.go
  32. 98
      crypto/secp256k1/secp256.go
  33. 15
      crypto/secp256k1/secp256_test.go
  34. 88
      eth/backend.go
  35. 4
      eth/backend_test.go
  36. 480
      eth/downloader/downloader.go
  37. 99
      eth/downloader/downloader_test.go
  38. 236
      eth/downloader/peer.go
  39. 291
      eth/downloader/queue.go
  40. 8
      eth/filters/filter_test.go
  41. 2
      eth/gasprice.go
  42. 42
      eth/handler.go
  43. 4
      eth/helper_test.go
  44. 41
      eth/peer.go
  45. 3
      eth/protocol.go
  46. 4
      eth/sync.go
  47. 4
      eth/sync_test.go
  48. 29
      event/filter/filter_test.go
  49. 2
      jsre/jsre_test.go
  50. 38
      miner/worker.go
  51. 46
      p2p/peer.go
  52. 15
      p2p/protocol.go
  53. 63
      p2p/server.go
  54. 4
      rpc/api/admin.go
  55. 12
      rpc/api/args_test.go
  56. 26
      rpc/api/debug.go
  57. 46
      rpc/api/eth.go
  58. 7
      rpc/api/eth_args.go
  59. 12
      rpc/api/eth_js.go
  60. 2
      rpc/api/utils.go
  61. 705
      tests/files/BlockchainTests/bcStateTest.json
  62. 144
      tests/files/StateTests/stPreCompiledContracts.json
  63. 55
      tests/files/StateTests/stSystemOperationsTest.json
  64. 7
      tests/init.go
  65. 13
      whisper/whisper_test.go
  66. 97
      xeth/xeth.go

@ -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."

@ -1 +1 @@
1.3.1
1.3.2

@ -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...)

@ -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")
}
}

@ -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())
}

@ -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>`)

@ -69,6 +69,7 @@ var AppHelpFlagGroups = []flagGroup{
utils.GenesisFileFlag,
utils.IdentityFlag,
utils.FastSyncFlag,
utils.LightKDFFlag,
utils.CacheFlag,
utils.BlockchainVersionFlag,
},

@ -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.

@ -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
}

@ -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 USERS 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?`
)

@ -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()

@ -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
}

@ -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
}

@ -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.")

@ -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!)
@ -93,8 +111,13 @@ type BlockChain struct {
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])
@ -1027,6 +1086,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
var (
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)
}

@ -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 {

@ -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.

@ -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))

@ -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
}

@ -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)
}
}

@ -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()
}

@ -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 {

@ -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)
}

@ -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
}

@ -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
}

@ -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

@ -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)
}

@ -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
}

@ -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]
}

@ -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)
}
}
}

@ -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))
}

@ -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,12 +229,11 @@ 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]))
}
serialized_pubkey_ptr := (*C.uchar)(unsafe.Pointer(&bytes65[0]))
var output_len C.size_t
C.secp256k1_ec_pubkey_serialize( // always returns 1
context,
@ -285,4 +244,13 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
)
return bytes65, nil
}
func checkSignature(sig []byte) error {
if len(sig) != 65 {
return ErrInvalidSignatureLen
}
if sig[64] >= 4 {
return ErrInvalidRecoveryID
}
return nil
}

@ -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) {

@ -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) {
@ -555,7 +484,6 @@ func (s *Ethereum) Miner() *miner.Miner { return s.miner }
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 }

@ -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)
}
}

@ -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{})
@ -366,22 +358,14 @@ func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err e
d.syncStatsLock.Unlock()
// 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()
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
// Deliver the received chunk of data and check chain validity
accepted, err := deliver(packet)
if err == errInvalidChain {
return err
}
// 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)
}
// All was successful, promote the peer and potentially start processing
peer.Promote()
setIdle(peer)
// 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))
go d.process()
case errInvalidChain:
// The hash chain is invalid (blocks are not ordered properly), abort
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))
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)
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
}

@ -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)
@ -1215,7 +1226,6 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol int) {
{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
@ -1228,6 +1238,8 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol int) {
{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)
}
}
}

@ -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
@ -92,10 +96,7 @@ func newPeer(id string, version int, head common.Hash,
return &peer{
id: id,
head: head,
blockCapacity: 1,
receiptCapacity: 1,
stateCapacity: 1,
ignored: set.New(),
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
}
}
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
}
// 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
}
// 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]
}
}

@ -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
}
// 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
}
if q.PendingNodeData() > 0 {
return nil
nproc := q.countProcessableItems()
for nproc == 0 && !q.closed {
q.active.Wait()
nproc = q.countProcessableItems()
}
// 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
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)
}
// 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
}
// 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
return i
}
// 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
}
// 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
}
return i
}
if !safe {
break
}
}
// 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
// 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
}
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,24 +881,27 @@ 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 (
accepted int
failure error
useful bool
)
@ -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()

@ -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)
}
}

@ -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

@ -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()),
}
}

@ -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)

@ -44,16 +44,21 @@ 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
@ -62,20 +67,28 @@ type peer struct {
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)

@ -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}

@ -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")

@ -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())

@ -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):
}
}

@ -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)

@ -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

@ -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
}

@ -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 {

@ -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
}

@ -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) {

@ -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")
}
}

@ -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,13 +167,32 @@ 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
}
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) {
args := new(BlockNumArg)
if err := self.codec.Decode(req.Params, &args); err != nil {

@ -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 {

@ -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 {

@ -41,6 +41,18 @@ web3._extend({
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:

@ -130,7 +130,7 @@ var (
},
"shh": []string{
"post",
"newIdentify",
"newIdentity",
"hasIdentity",
"newGroup",
"addToGroup",

@ -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",

@ -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",

@ -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",

@ -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)

@ -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")
}
}

@ -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
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
}
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)
}
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)

Loading…
Cancel
Save