Merge branch 'develop' into release/1.3.2
Conflicts: VERSION cmd/geth/main.go
This commit is contained in:
commit
5490437942
67
Makefile
67
Makefile
|
@ -2,9 +2,17 @@
|
|||
# with Go source code. If you know what GOPATH is then you probably
|
||||
# don't need to bother with make.
|
||||
|
||||
.PHONY: geth geth-cross geth-linux geth-darwin geth-windows geth-android evm all test travis-test-with-coverage xgo clean
|
||||
.PHONY: geth geth-cross evm all test travis-test-with-coverage xgo clean
|
||||
.PHONY: geth-linux geth-linux-arm geth-linux-386 geth-linux-amd64
|
||||
.PHONY: geth-darwin geth-darwin-386 geth-darwin-amd64
|
||||
.PHONY: geth-windows geth-windows-386 geth-windows-amd64
|
||||
.PHONY: geth-android geth-android-16 geth-android-21
|
||||
|
||||
GOBIN = build/bin
|
||||
|
||||
CROSSDEPS = https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2
|
||||
GO ?= latest
|
||||
|
||||
geth:
|
||||
build/env.sh go install -v $(shell build/flags.sh) ./cmd/geth
|
||||
@echo "Done building."
|
||||
|
@ -14,26 +22,67 @@ geth-cross: geth-linux geth-darwin geth-windows geth-android
|
|||
@echo "Full cross compilation done:"
|
||||
@ls -l $(GOBIN)/geth-*
|
||||
|
||||
geth-linux: xgo
|
||||
build/env.sh $(GOBIN)/xgo --dest=$(GOBIN) --deps=https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2 --targets=linux/* -v $(shell build/flags.sh) ./cmd/geth
|
||||
geth-linux: xgo geth-linux-arm geth-linux-386 geth-linux-amd64
|
||||
@echo "Linux cross compilation done:"
|
||||
@ls -l $(GOBIN)/geth-linux-*
|
||||
|
||||
geth-darwin: xgo
|
||||
build/env.sh $(GOBIN)/xgo --dest=$(GOBIN) --deps=https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2 --targets=darwin/* -v $(shell build/flags.sh) ./cmd/geth
|
||||
geth-linux-arm: xgo
|
||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=linux/arm -v $(shell build/flags.sh) ./cmd/geth
|
||||
@echo "Linux ARM cross compilation done:"
|
||||
@ls -l $(GOBIN)/geth-linux-* | grep arm
|
||||
|
||||
geth-linux-386: xgo
|
||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=linux/386 -v $(shell build/flags.sh) ./cmd/geth
|
||||
@echo "Linux 386 cross compilation done:"
|
||||
@ls -l $(GOBIN)/geth-linux-* | grep 386
|
||||
|
||||
geth-linux-amd64: xgo
|
||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=linux/amd64 -v $(shell build/flags.sh) ./cmd/geth
|
||||
@echo "Linux amd64 cross compilation done:"
|
||||
@ls -l $(GOBIN)/geth-linux-* | grep amd64
|
||||
|
||||
geth-darwin: xgo geth-darwin-386 geth-darwin-amd64
|
||||
@echo "Darwin cross compilation done:"
|
||||
@ls -l $(GOBIN)/geth-darwin-*
|
||||
|
||||
geth-windows: xgo
|
||||
build/env.sh $(GOBIN)/xgo --dest=$(GOBIN) --deps=https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2 --targets=windows/* -v $(shell build/flags.sh) ./cmd/geth
|
||||
geth-darwin-386: xgo
|
||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=darwin/386 -v $(shell build/flags.sh) ./cmd/geth
|
||||
@echo "Darwin 386 cross compilation done:"
|
||||
@ls -l $(GOBIN)/geth-darwin-* | grep 386
|
||||
|
||||
geth-darwin-amd64: xgo
|
||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=darwin/amd64 -v $(shell build/flags.sh) ./cmd/geth
|
||||
@echo "Darwin amd64 cross compilation done:"
|
||||
@ls -l $(GOBIN)/geth-darwin-* | grep amd64
|
||||
|
||||
geth-windows: xgo geth-windows-386 geth-windows-amd64
|
||||
@echo "Windows cross compilation done:"
|
||||
@ls -l $(GOBIN)/geth-windows-*
|
||||
|
||||
geth-android: xgo
|
||||
build/env.sh $(GOBIN)/xgo --dest=$(GOBIN) --deps=https://gmplib.org/download/gmp/gmp-6.0.0a.tar.bz2 --targets=android-16/*,android-21/* -v $(shell build/flags.sh) ./cmd/geth
|
||||
geth-windows-386: xgo
|
||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=windows/386 -v $(shell build/flags.sh) ./cmd/geth
|
||||
@echo "Windows 386 cross compilation done:"
|
||||
@ls -l $(GOBIN)/geth-windows-* | grep 386
|
||||
|
||||
geth-windows-amd64: xgo
|
||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=windows/amd64 -v $(shell build/flags.sh) ./cmd/geth
|
||||
@echo "Windows amd64 cross compilation done:"
|
||||
@ls -l $(GOBIN)/geth-windows-* | grep amd64
|
||||
|
||||
geth-android: xgo geth-android-16 geth-android-21
|
||||
@echo "Android cross compilation done:"
|
||||
@ls -l $(GOBIN)/geth-android-*
|
||||
|
||||
geth-android-16: xgo
|
||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=android-16/* -v $(shell build/flags.sh) ./cmd/geth
|
||||
@echo "Android 16 cross compilation done:"
|
||||
@ls -l $(GOBIN)/geth-android-16-*
|
||||
|
||||
geth-android-21: xgo
|
||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --deps=$(CROSSDEPS) --targets=android-21/* -v $(shell build/flags.sh) ./cmd/geth
|
||||
@echo "Android 21 cross compilation done:"
|
||||
@ls -l $(GOBIN)/geth-android-21-*
|
||||
|
||||
evm:
|
||||
build/env.sh $(GOROOT)/bin/go install -v $(shell build/flags.sh) ./cmd/evm
|
||||
@echo "Done building."
|
||||
|
|
|
@ -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 USER’S SOLE
|
||||
RISK. THE USER REPRESENTS THAT THE USER HAS AN ADEQUATE UNDERSTANDING OF THE RISKS, USAGE AND
|
||||
INTRICACIES OF CRYPTOGRAPHIC TOKENS AND BLOCKCHAIN-BASED OPEN SOURCE SOFTWARE, ETH PLATFORM AND ETH.
|
||||
THE USER ACKNOWLEDGES AND AGREES THAT, TO THE FULLEST EXTENT PERMITTED BY ANY APPLICABLE LAW, THE
|
||||
DISCLAIMERS OF LIABILITY CONTAINED HEREIN APPLY TO ANY AND ALL DAMAGES OR INJURY WHATSOEVER CAUSED
|
||||
BY OR RELATED TO RISKS OF, USE OF, OR INABILITY TO USE, ETH OR THE ETHEREUM PLATFORM UNDER ANY CAUSE
|
||||
OR ACTION WHATSOEVER OF ANY KIND IN ANY JURISDICTION, INCLUDING, WITHOUT LIMITATION, ACTIONS FOR
|
||||
BREACH OF WARRANTY, BREACH OF CONTRACT OR TORT (INCLUDING NEGLIGENCE) AND THAT NEITHER STIFTUNG
|
||||
ETHEREUM NOR ETHEREUM TEAM SHALL BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR
|
||||
CONSEQUENTIAL DAMAGES, INCLUDING FOR LOSS OF PROFITS, GOODWILL OR DATA. SOME JURISDICTIONS DO NOT
|
||||
ALLOW THE EXCLUSION OF CERTAIN WARRANTIES OR THE LIMITATION OR EXCLUSION OF LIABILITY FOR CERTAIN
|
||||
TYPES OF DAMAGES. THEREFORE, SOME OF THE ABOVE LIMITATIONS IN THIS SECTION MAY NOT APPLY TO A USER.
|
||||
IN PARTICULAR, NOTHING IN THESE TERMS SHALL AFFECT THE STATUTORY RIGHTS OF ANY USER OR EXCLUDE
|
||||
INJURY ARISING FROM ANY WILLFUL MISCONDUCT OR FRAUD OF STIFTUNG ETHEREUM.
|
||||
|
||||
Do you accept this agreement?`
|
||||
)
|
|
@ -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!)
|
||||
|
@ -91,10 +109,15 @@ type BlockChain struct {
|
|||
procInterrupt int32 // interrupt signaler for block processing
|
||||
wg sync.WaitGroup
|
||||
|
||||
pow pow.PoW
|
||||
rand *mrand.Rand
|
||||
pow pow.PoW
|
||||
rand *mrand.Rand
|
||||
processor Processor
|
||||
validator Validator
|
||||
}
|
||||
|
||||
// NewBlockChain returns a fully initialised block chain using information
|
||||
// available in the database. It initialiser the default Ethereum Validator and
|
||||
// Processor.
|
||||
func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) {
|
||||
headerCache, _ := lru.New(headerCacheLimit)
|
||||
bodyCache, _ := lru.New(bodyCacheLimit)
|
||||
|
@ -121,6 +144,8 @@ func NewBlockChain(chainDb ethdb.Database, pow pow.PoW, mux *event.TypeMux) (*Bl
|
|||
return nil, err
|
||||
}
|
||||
bc.rand = mrand.New(mrand.NewSource(seed.Int64()))
|
||||
bc.SetValidator(NewBlockValidator(bc, pow))
|
||||
bc.SetProcessor(NewStateProcessor(bc))
|
||||
|
||||
bc.genesisBlock = bc.GetBlockByNumber(0)
|
||||
if bc.genesisBlock == nil {
|
||||
|
@ -292,6 +317,7 @@ func (self *BlockChain) FastSyncCommitHead(hash common.Hash) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GasLimit returns the gas limit of the current HEAD block.
|
||||
func (self *BlockChain) GasLimit() *big.Int {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
@ -299,6 +325,7 @@ func (self *BlockChain) GasLimit() *big.Int {
|
|||
return self.currentBlock.GasLimit()
|
||||
}
|
||||
|
||||
// LastBlockHash return the hash of the HEAD block.
|
||||
func (self *BlockChain) LastBlockHash() common.Hash {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
@ -333,6 +360,8 @@ func (self *BlockChain) CurrentFastBlock() *types.Block {
|
|||
return self.currentFastBlock
|
||||
}
|
||||
|
||||
// Status returns status information about the current chain such as the HEAD Td,
|
||||
// the HEAD hash and the hash of the genesis block.
|
||||
func (self *BlockChain) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
@ -340,10 +369,38 @@ func (self *BlockChain) Status() (td *big.Int, currentBlock common.Hash, genesis
|
|||
return self.GetTd(self.currentBlock.Hash()), self.currentBlock.Hash(), self.genesisBlock.Hash()
|
||||
}
|
||||
|
||||
func (self *BlockChain) SetProcessor(proc types.BlockProcessor) {
|
||||
self.processor = proc
|
||||
// SetProcessor sets the processor required for making state modifications.
|
||||
func (self *BlockChain) SetProcessor(processor Processor) {
|
||||
self.procmu.Lock()
|
||||
defer self.procmu.Unlock()
|
||||
self.processor = processor
|
||||
}
|
||||
|
||||
// SetValidator sets the validator which is used to validate incoming blocks.
|
||||
func (self *BlockChain) SetValidator(validator Validator) {
|
||||
self.procmu.Lock()
|
||||
defer self.procmu.Unlock()
|
||||
self.validator = validator
|
||||
}
|
||||
|
||||
// Validator returns the current validator.
|
||||
func (self *BlockChain) Validator() Validator {
|
||||
self.procmu.RLock()
|
||||
defer self.procmu.RUnlock()
|
||||
return self.validator
|
||||
}
|
||||
|
||||
// Processor returns the current processor.
|
||||
func (self *BlockChain) Processor() Processor {
|
||||
self.procmu.RLock()
|
||||
defer self.procmu.RUnlock()
|
||||
return self.processor
|
||||
}
|
||||
|
||||
// AuxValidator returns the auxiliary validator (Proof of work atm)
|
||||
func (self *BlockChain) AuxValidator() pow.PoW { return self.pow }
|
||||
|
||||
// State returns a new mutable state based on the current HEAD block.
|
||||
func (self *BlockChain) State() (*state.StateDB, error) {
|
||||
return state.New(self.CurrentBlock().Root(), self.chainDb)
|
||||
}
|
||||
|
@ -606,6 +663,8 @@ func (self *BlockChain) GetUnclesInChain(block *types.Block, length int) []*type
|
|||
return uncles
|
||||
}
|
||||
|
||||
// Stop stops the blockchain service. If any imports are currently in progress
|
||||
// it will abort them using the procInterrupt.
|
||||
func (bc *BlockChain) Stop() {
|
||||
if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) {
|
||||
return
|
||||
|
@ -758,9 +817,9 @@ func (self *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int)
|
|||
|
||||
var err error
|
||||
if index == 0 {
|
||||
err = self.processor.ValidateHeader(header, checkPow, false)
|
||||
err = self.Validator().ValidateHeader(header, self.GetHeader(header.ParentHash), checkPow)
|
||||
} else {
|
||||
err = self.processor.ValidateHeaderWithParent(header, chain[index-1], checkPow, false)
|
||||
err = self.Validator().ValidateHeader(header, chain[index-1], checkPow)
|
||||
}
|
||||
if err != nil {
|
||||
errs[index] = err
|
||||
|
@ -913,7 +972,7 @@ func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain
|
|||
glog.Fatal(errs[index])
|
||||
return
|
||||
}
|
||||
if err := PutBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil {
|
||||
if err := WriteBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil {
|
||||
errs[index] = fmt.Errorf("failed to write block receipts: %v", err)
|
||||
atomic.AddInt32(&failed, 1)
|
||||
glog.Fatal(errs[index])
|
||||
|
@ -1025,9 +1084,10 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
// faster than direct delivery and requires much less mutex
|
||||
// acquiring.
|
||||
var (
|
||||
stats struct{ queued, processed, ignored int }
|
||||
events = make([]interface{}, 0, len(chain))
|
||||
tstart = time.Now()
|
||||
stats struct{ queued, processed, ignored int }
|
||||
events = make([]interface{}, 0, len(chain))
|
||||
coalescedLogs vm.Logs
|
||||
tstart = time.Now()
|
||||
|
||||
nonceChecked = make([]bool, len(chain))
|
||||
)
|
||||
|
@ -1057,12 +1117,12 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
|
||||
if BadHashes[block.Hash()] {
|
||||
err := BadHashError(block.Hash())
|
||||
blockErr(block, err)
|
||||
reportBlock(block, err)
|
||||
return i, err
|
||||
}
|
||||
// Call in to the block processor and check for errors. It's likely that if one block fails
|
||||
// all others will fail too (unless a known block is returned).
|
||||
logs, receipts, err := self.processor.Process(block)
|
||||
// Stage 1 validation of the block using the chain's validator
|
||||
// interface.
|
||||
err := self.Validator().ValidateBlock(block)
|
||||
if err != nil {
|
||||
if IsKnownBlockErr(err) {
|
||||
stats.ignored++
|
||||
|
@ -1089,14 +1149,41 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
|||
continue
|
||||
}
|
||||
|
||||
blockErr(block, err)
|
||||
|
||||
go ReportBlock(block, err)
|
||||
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)
|
||||
|
||||
// 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
|
||||
}
|
||||
// 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,20 +229,28 @@ func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) {
|
|||
recoverable_sig_ptr,
|
||||
msg_ptr,
|
||||
)
|
||||
|
||||
if ret == C.int(0) {
|
||||
return nil, errors.New("Failed to recover public key")
|
||||
} else {
|
||||
serialized_pubkey_ptr := (*C.uchar)(unsafe.Pointer(&bytes65[0]))
|
||||
|
||||
var output_len C.size_t
|
||||
C.secp256k1_ec_pubkey_serialize( // always returns 1
|
||||
context,
|
||||
serialized_pubkey_ptr,
|
||||
&output_len,
|
||||
pubkey_ptr,
|
||||
0, // SECP256K1_EC_COMPRESSED
|
||||
)
|
||||
return bytes65, nil
|
||||
}
|
||||
|
||||
serialized_pubkey_ptr := (*C.uchar)(unsafe.Pointer(&bytes65[0]))
|
||||
var output_len C.size_t
|
||||
C.secp256k1_ec_pubkey_serialize( // always returns 1
|
||||
context,
|
||||
serialized_pubkey_ptr,
|
||||
&output_len,
|
||||
pubkey_ptr,
|
||||
0, // SECP256K1_EC_COMPRESSED
|
||||
)
|
||||
return bytes65, nil
|
||||
}
|
||||
|
||||
func checkSignature(sig []byte) error {
|
||||
if len(sig) != 65 {
|
||||
return ErrInvalidSignatureLen
|
||||
}
|
||||
if sig[64] >= 4 {
|
||||
return ErrInvalidRecoveryID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
122
eth/backend.go
122
eth/backend.go
|
@ -65,7 +65,7 @@ const (
|
|||
var (
|
||||
jsonlogger = logger.NewJsonLogger()
|
||||
|
||||
datadirInUseErrNos = []uint{11, 32, 35}
|
||||
datadirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true}
|
||||
portInUseErrRE = regexp.MustCompile("address already in use")
|
||||
|
||||
defaultBootNodes = []*discover.Node{
|
||||
|
@ -231,9 +231,7 @@ type Ethereum struct {
|
|||
chainDb ethdb.Database // Block chain database
|
||||
dappDb ethdb.Database // Dapp database
|
||||
|
||||
//*** SERVICES ***
|
||||
// State manager for processing new blocks and managing the over all states
|
||||
blockProcessor *core.BlockProcessor
|
||||
// Handlers
|
||||
txPool *core.TxPool
|
||||
blockchain *core.BlockChain
|
||||
accountManager *accounts.Manager
|
||||
|
@ -286,15 +284,7 @@ func New(config *Config) (*Ethereum, error) {
|
|||
// Open the chain database and perform any upgrades needed
|
||||
chainDb, err := newdb(filepath.Join(config.DataDir, "chaindata"))
|
||||
if err != nil {
|
||||
var ok bool
|
||||
errno := uint(err.(syscall.Errno))
|
||||
for _, no := range datadirInUseErrNos {
|
||||
if errno == no {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] {
|
||||
err = fmt.Errorf("%v (check if another instance of geth is already running with the same data directory '%s')", err, config.DataDir)
|
||||
}
|
||||
return nil, fmt.Errorf("blockchain db err: %v", err)
|
||||
|
@ -311,14 +301,7 @@ func New(config *Config) (*Ethereum, error) {
|
|||
|
||||
dappDb, err := newdb(filepath.Join(config.DataDir, "dapp"))
|
||||
if err != nil {
|
||||
var ok bool
|
||||
for _, no := range datadirInUseErrNos {
|
||||
if uint(err.(syscall.Errno)) == no {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
if errno, ok := err.(syscall.Errno); ok && datadirInUseErrnos[uint(errno)] {
|
||||
err = fmt.Errorf("%v (check if another instance of geth is already running with the same data directory '%s')", err, config.DataDir)
|
||||
}
|
||||
return nil, fmt.Errorf("dapp db err: %v", err)
|
||||
|
@ -422,8 +405,6 @@ func New(config *Config) (*Ethereum, error) {
|
|||
newPool := core.NewTxPool(eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit)
|
||||
eth.txPool = newPool
|
||||
|
||||
eth.blockProcessor = core.NewBlockProcessor(chainDb, eth.pow, eth.blockchain, eth.EventMux())
|
||||
eth.blockchain.SetProcessor(eth.blockProcessor)
|
||||
if eth.protocolManager, err = NewProtocolManager(config.FastSync, config.NetworkId, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -467,62 +448,10 @@ func New(config *Config) (*Ethereum, error) {
|
|||
return eth, nil
|
||||
}
|
||||
|
||||
type NodeInfo struct {
|
||||
Name string
|
||||
NodeUrl string
|
||||
NodeID string
|
||||
IP string
|
||||
DiscPort int // UDP listening port for discovery protocol
|
||||
TCPPort int // TCP listening port for RLPx
|
||||
Td string
|
||||
ListenAddr string
|
||||
}
|
||||
|
||||
func (s *Ethereum) NodeInfo() *NodeInfo {
|
||||
node := s.net.Self()
|
||||
|
||||
return &NodeInfo{
|
||||
Name: s.Name(),
|
||||
NodeUrl: node.String(),
|
||||
NodeID: node.ID.String(),
|
||||
IP: node.IP.String(),
|
||||
DiscPort: int(node.UDP),
|
||||
TCPPort: int(node.TCP),
|
||||
ListenAddr: s.net.ListenAddr,
|
||||
Td: s.BlockChain().GetTd(s.BlockChain().CurrentBlock().Hash()).String(),
|
||||
}
|
||||
}
|
||||
|
||||
type PeerInfo struct {
|
||||
ID string
|
||||
Name string
|
||||
Caps string
|
||||
RemoteAddress string
|
||||
LocalAddress string
|
||||
}
|
||||
|
||||
func newPeerInfo(peer *p2p.Peer) *PeerInfo {
|
||||
var caps []string
|
||||
for _, cap := range peer.Caps() {
|
||||
caps = append(caps, cap.String())
|
||||
}
|
||||
return &PeerInfo{
|
||||
ID: peer.ID().String(),
|
||||
Name: peer.Name(),
|
||||
Caps: strings.Join(caps, ", "),
|
||||
RemoteAddress: peer.RemoteAddr().String(),
|
||||
LocalAddress: peer.LocalAddr().String(),
|
||||
}
|
||||
}
|
||||
|
||||
// PeersInfo returns an array of PeerInfo objects describing connected peers
|
||||
func (s *Ethereum) PeersInfo() (peersinfo []*PeerInfo) {
|
||||
for _, peer := range s.net.Peers() {
|
||||
if peer != nil {
|
||||
peersinfo = append(peersinfo, newPeerInfo(peer))
|
||||
}
|
||||
}
|
||||
return
|
||||
// Network retrieves the underlying P2P network server. This should eventually
|
||||
// be moved out into a protocol independent package, but for now use an accessor.
|
||||
func (s *Ethereum) Network() *p2p.Server {
|
||||
return s.net
|
||||
}
|
||||
|
||||
func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) {
|
||||
|
@ -552,24 +481,23 @@ func (s *Ethereum) IsMining() bool { return s.miner.Mining() }
|
|||
func (s *Ethereum) Miner() *miner.Miner { return s.miner }
|
||||
|
||||
// func (s *Ethereum) Logger() logger.LogSystem { return s.logger }
|
||||
func (s *Ethereum) Name() string { return s.net.Name }
|
||||
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
|
||||
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
|
||||
func (s *Ethereum) BlockProcessor() *core.BlockProcessor { return s.blockProcessor }
|
||||
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
|
||||
func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper }
|
||||
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
|
||||
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
|
||||
func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb }
|
||||
func (s *Ethereum) IsListening() bool { return true } // Always listening
|
||||
func (s *Ethereum) PeerCount() int { return s.net.PeerCount() }
|
||||
func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() }
|
||||
func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers }
|
||||
func (s *Ethereum) ClientVersion() string { return s.clientVersion }
|
||||
func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
|
||||
func (s *Ethereum) NetVersion() int { return s.netVersionId }
|
||||
func (s *Ethereum) ShhVersion() int { return s.shhVersionId }
|
||||
func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
|
||||
func (s *Ethereum) Name() string { return s.net.Name }
|
||||
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager }
|
||||
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
|
||||
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
|
||||
func (s *Ethereum) Whisper() *whisper.Whisper { return s.whisper }
|
||||
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux }
|
||||
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
|
||||
func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb }
|
||||
func (s *Ethereum) IsListening() bool { return true } // Always listening
|
||||
func (s *Ethereum) PeerCount() int { return s.net.PeerCount() }
|
||||
func (s *Ethereum) Peers() []*p2p.Peer { return s.net.Peers() }
|
||||
func (s *Ethereum) MaxPeers() int { return s.net.MaxPeers }
|
||||
func (s *Ethereum) ClientVersion() string { return s.clientVersion }
|
||||
func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
|
||||
func (s *Ethereum) NetVersion() int { return s.netVersionId }
|
||||
func (s *Ethereum) ShhVersion() int { return s.shhVersionId }
|
||||
func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
|
||||
|
||||
// Start the ethereum
|
||||
func (s *Ethereum) Start() error {
|
||||
|
|
|
@ -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{})
|
||||
|
@ -365,23 +357,15 @@ func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err e
|
|||
d.syncStatsChainHeight = latest
|
||||
d.syncStatsLock.Unlock()
|
||||
|
||||
// Initiate the sync using a concurrent hash and block retrieval algorithm
|
||||
// Initiate the sync using a concurrent hash and block retrieval algorithm
|
||||
d.queue.Prepare(origin+1, d.mode, 0)
|
||||
if d.syncInitHook != nil {
|
||||
d.syncInitHook(origin, latest)
|
||||
}
|
||||
d.queue.Prepare(origin+1, d.mode, 0)
|
||||
|
||||
errc := make(chan error, 2)
|
||||
go func() { errc <- d.fetchHashes61(p, td, origin+1) }()
|
||||
go func() { errc <- d.fetchBlocks61(origin + 1) }()
|
||||
|
||||
// If any fetcher fails, cancel the other
|
||||
if err := <-errc; err != nil {
|
||||
d.cancel()
|
||||
<-errc
|
||||
return err
|
||||
}
|
||||
return <-errc
|
||||
return d.spawnSync(
|
||||
func() error { return d.fetchHashes61(p, td, origin+1) },
|
||||
func() error { return d.fetchBlocks61(origin + 1) },
|
||||
)
|
||||
|
||||
case p.version >= 62:
|
||||
// Look up the sync boundaries: the common ancestor and the target block
|
||||
|
@ -405,7 +389,6 @@ func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err e
|
|||
switch d.mode {
|
||||
case LightSync:
|
||||
pivot = latest
|
||||
|
||||
case FastSync:
|
||||
// Calculate the new fast/slow sync pivot point
|
||||
pivotOffset, err := rand.Int(rand.Reader, big.NewInt(int64(fsPivotInterval)))
|
||||
|
@ -426,34 +409,51 @@ func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err e
|
|||
glog.V(logger.Debug).Infof("Fast syncing until pivot block #%d", pivot)
|
||||
}
|
||||
d.queue.Prepare(origin+1, d.mode, pivot)
|
||||
|
||||
if d.syncInitHook != nil {
|
||||
d.syncInitHook(origin, latest)
|
||||
}
|
||||
errc := make(chan error, 4)
|
||||
go func() { errc <- d.fetchHeaders(p, td, origin+1) }() // Headers are always retrieved
|
||||
go func() { errc <- d.fetchBodies(origin + 1) }() // Bodies are retrieved during normal and fast sync
|
||||
go func() { errc <- d.fetchReceipts(origin + 1) }() // Receipts are retrieved during fast sync
|
||||
go func() { errc <- d.fetchNodeData() }() // Node state data is retrieved during fast sync
|
||||
|
||||
// If any fetcher fails, cancel the others
|
||||
var fail error
|
||||
for i := 0; i < cap(errc); i++ {
|
||||
if err := <-errc; err != nil {
|
||||
if fail == nil {
|
||||
fail = err
|
||||
d.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
return fail
|
||||
return d.spawnSync(
|
||||
func() error { return d.fetchHeaders(p, td, origin+1) }, // Headers are always retrieved
|
||||
func() error { return d.fetchBodies(origin + 1) }, // Bodies are retrieved during normal and fast sync
|
||||
func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during fast sync
|
||||
func() error { return d.fetchNodeData() }, // Node state data is retrieved during fast sync
|
||||
)
|
||||
|
||||
default:
|
||||
// Something very wrong, stop right here
|
||||
glog.V(logger.Error).Infof("Unsupported eth protocol: %d", p.version)
|
||||
return errBadPeer
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// spawnSync runs d.process and all given fetcher functions to completion in
|
||||
// separate goroutines, returning the first error that appears.
|
||||
func (d *Downloader) spawnSync(fetchers ...func() error) error {
|
||||
var wg sync.WaitGroup
|
||||
errc := make(chan error, len(fetchers)+1)
|
||||
wg.Add(len(fetchers) + 1)
|
||||
go func() { defer wg.Done(); errc <- d.process() }()
|
||||
for _, fn := range fetchers {
|
||||
fn := fn
|
||||
go func() { defer wg.Done(); errc <- fn() }()
|
||||
}
|
||||
// Wait for the first error, then terminate the others.
|
||||
var err error
|
||||
for i := 0; i < len(fetchers)+1; i++ {
|
||||
if i == len(fetchers) {
|
||||
// Close the queue when all fetchers have exited.
|
||||
// This will cause the block processor to end when
|
||||
// it has processed the queue.
|
||||
d.queue.Close()
|
||||
}
|
||||
if err = <-errc; err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
d.queue.Close()
|
||||
d.cancel()
|
||||
wg.Wait()
|
||||
return err
|
||||
}
|
||||
|
||||
// cancel cancels all of the operations and resets the queue. It returns true
|
||||
|
@ -470,12 +470,10 @@ func (d *Downloader) cancel() {
|
|||
}
|
||||
}
|
||||
d.cancelLock.Unlock()
|
||||
|
||||
// Reset the queue
|
||||
d.queue.Reset()
|
||||
}
|
||||
|
||||
// Terminate interrupts the downloader, canceling all pending operations.
|
||||
// The downloader cannot be reused after calling Terminate.
|
||||
func (d *Downloader) Terminate() {
|
||||
atomic.StoreInt32(&d.interrupt, 1)
|
||||
d.cancel()
|
||||
|
@ -489,21 +487,12 @@ func (d *Downloader) fetchHeight61(p *peer) (uint64, error) {
|
|||
// Request the advertised remote head block and wait for the response
|
||||
go p.getBlocks([]common.Hash{p.head})
|
||||
|
||||
timeout := time.After(blockSoftTTL)
|
||||
timeout := time.After(hashTTL)
|
||||
for {
|
||||
select {
|
||||
case <-d.cancelCh:
|
||||
return 0, errCancelBlockFetch
|
||||
|
||||
case <-d.headerCh:
|
||||
// Out of bounds eth/62 block headers received, ignore them
|
||||
|
||||
case <-d.bodyCh:
|
||||
// Out of bounds eth/62 block bodies received, ignore them
|
||||
|
||||
case <-d.hashCh:
|
||||
// Out of bounds hashes received, ignore them
|
||||
|
||||
case packet := <-d.blockCh:
|
||||
// Discard anything not from the origin peer
|
||||
if packet.PeerId() != p.id {
|
||||
|
@ -521,6 +510,16 @@ func (d *Downloader) fetchHeight61(p *peer) (uint64, error) {
|
|||
case <-timeout:
|
||||
glog.V(logger.Debug).Infof("%v: head block timeout", p)
|
||||
return 0, errTimeout
|
||||
|
||||
case <-d.hashCh:
|
||||
// Out of bounds hashes received, ignore them
|
||||
|
||||
case <-d.headerCh:
|
||||
case <-d.bodyCh:
|
||||
case <-d.stateCh:
|
||||
case <-d.receiptCh:
|
||||
// Ignore eth/{62,63} packets because this is eth/61.
|
||||
// These can arrive as a late delivery from a previous sync.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -571,18 +570,19 @@ func (d *Downloader) findAncestor61(p *peer) (uint64, error) {
|
|||
}
|
||||
}
|
||||
|
||||
case <-timeout:
|
||||
glog.V(logger.Debug).Infof("%v: head hash timeout", p)
|
||||
return 0, errTimeout
|
||||
|
||||
case <-d.blockCh:
|
||||
// Out of bounds blocks received, ignore them
|
||||
|
||||
case <-d.headerCh:
|
||||
// Out of bounds eth/62 block headers received, ignore them
|
||||
|
||||
case <-d.bodyCh:
|
||||
// Out of bounds eth/62 block bodies received, ignore them
|
||||
|
||||
case <-timeout:
|
||||
glog.V(logger.Debug).Infof("%v: head hash timeout", p)
|
||||
return 0, errTimeout
|
||||
case <-d.stateCh:
|
||||
case <-d.receiptCh:
|
||||
// Ignore eth/{62,63} packets because this is eth/61.
|
||||
// These can arrive as a late delivery from a previous sync.
|
||||
}
|
||||
}
|
||||
// If the head fetch already found an ancestor, return
|
||||
|
@ -631,18 +631,19 @@ func (d *Downloader) findAncestor61(p *peer) (uint64, error) {
|
|||
}
|
||||
start = check
|
||||
|
||||
case <-timeout:
|
||||
glog.V(logger.Debug).Infof("%v: search hash timeout", p)
|
||||
return 0, errTimeout
|
||||
|
||||
case <-d.blockCh:
|
||||
// Out of bounds blocks received, ignore them
|
||||
|
||||
case <-d.headerCh:
|
||||
// Out of bounds eth/62 block headers received, ignore them
|
||||
|
||||
case <-d.bodyCh:
|
||||
// Out of bounds eth/62 block bodies received, ignore them
|
||||
|
||||
case <-timeout:
|
||||
glog.V(logger.Debug).Infof("%v: search hash timeout", p)
|
||||
return 0, errTimeout
|
||||
case <-d.stateCh:
|
||||
case <-d.receiptCh:
|
||||
// Ignore eth/{62,63} packets because this is eth/61.
|
||||
// These can arrive as a late delivery from a previous sync.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -676,12 +677,6 @@ func (d *Downloader) fetchHashes61(p *peer, td *big.Int, from uint64) error {
|
|||
case <-d.cancelCh:
|
||||
return errCancelHashFetch
|
||||
|
||||
case <-d.headerCh:
|
||||
// Out of bounds eth/62 block headers received, ignore them
|
||||
|
||||
case <-d.bodyCh:
|
||||
// Out of bounds eth/62 block bodies received, ignore them
|
||||
|
||||
case packet := <-d.hashCh:
|
||||
// Make sure the active peer is giving us the hashes
|
||||
if packet.PeerId() != p.id {
|
||||
|
@ -750,6 +745,13 @@ func (d *Downloader) fetchHashes61(p *peer, td *big.Int, from uint64) error {
|
|||
glog.V(logger.Debug).Infof("%v: hash request timed out", p)
|
||||
hashTimeoutMeter.Mark(1)
|
||||
return errTimeout
|
||||
|
||||
case <-d.headerCh:
|
||||
case <-d.bodyCh:
|
||||
case <-d.stateCh:
|
||||
case <-d.receiptCh:
|
||||
// Ignore eth/{62,63} packets because this is eth/61.
|
||||
// These can arrive as a late delivery from a previous sync.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -774,59 +776,31 @@ func (d *Downloader) fetchBlocks61(from uint64) error {
|
|||
case <-d.cancelCh:
|
||||
return errCancelBlockFetch
|
||||
|
||||
case <-d.headerCh:
|
||||
// Out of bounds eth/62 block headers received, ignore them
|
||||
|
||||
case <-d.bodyCh:
|
||||
// Out of bounds eth/62 block bodies received, ignore them
|
||||
|
||||
case packet := <-d.blockCh:
|
||||
// If the peer was previously banned and failed to deliver it's pack
|
||||
// in a reasonable time frame, ignore it's message.
|
||||
if peer := d.peers.Peer(packet.PeerId()); peer != nil {
|
||||
// Deliver the received chunk of blocks, and demote in case of errors
|
||||
blocks := packet.(*blockPack).blocks
|
||||
err := d.queue.DeliverBlocks(peer.id, blocks)
|
||||
switch err {
|
||||
case nil:
|
||||
// If no blocks were delivered, demote the peer (need the delivery above)
|
||||
if len(blocks) == 0 {
|
||||
peer.Demote()
|
||||
peer.SetBlocksIdle()
|
||||
glog.V(logger.Detail).Infof("%s: no blocks delivered", peer)
|
||||
break
|
||||
}
|
||||
// All was successful, promote the peer and potentially start processing
|
||||
peer.Promote()
|
||||
peer.SetBlocksIdle()
|
||||
glog.V(logger.Detail).Infof("%s: delivered %d blocks", peer, len(blocks))
|
||||
go d.process()
|
||||
|
||||
case errInvalidChain:
|
||||
// The hash chain is invalid (blocks are not ordered properly), abort
|
||||
// Deliver the received chunk of blocks and check chain validity
|
||||
accepted, err := d.queue.DeliverBlocks(peer.id, blocks)
|
||||
if err == errInvalidChain {
|
||||
return err
|
||||
|
||||
case errNoFetchesPending:
|
||||
// Peer probably timed out with its delivery but came through
|
||||
// in the end, demote, but allow to to pull from this peer.
|
||||
peer.Demote()
|
||||
peer.SetBlocksIdle()
|
||||
glog.V(logger.Detail).Infof("%s: out of bound delivery", peer)
|
||||
|
||||
case errStaleDelivery:
|
||||
// Delivered something completely else than requested, usually
|
||||
// caused by a timeout and delivery during a new sync cycle.
|
||||
// Don't set it to idle as the original request should still be
|
||||
// in flight.
|
||||
peer.Demote()
|
||||
glog.V(logger.Detail).Infof("%s: stale delivery", peer)
|
||||
|
||||
}
|
||||
// Unless a peer delivered something completely else than requested (usually
|
||||
// caused by a timed out request which came through in the end), set it to
|
||||
// idle. If the delivery's stale, the peer should have already been idled.
|
||||
if err != errStaleDelivery {
|
||||
peer.SetBlocksIdle(accepted)
|
||||
}
|
||||
// Issue a log to the user to see what's going on
|
||||
switch {
|
||||
case err == nil && len(blocks) == 0:
|
||||
glog.V(logger.Detail).Infof("%s: no blocks delivered", peer)
|
||||
case err == nil:
|
||||
glog.V(logger.Detail).Infof("%s: delivered %d blocks", peer, len(blocks))
|
||||
default:
|
||||
// Peer did something semi-useful, demote but keep it around
|
||||
peer.Demote()
|
||||
peer.SetBlocksIdle()
|
||||
glog.V(logger.Detail).Infof("%s: delivery partially failed: %v", peer, err)
|
||||
go d.process()
|
||||
glog.V(logger.Detail).Infof("%s: delivery failed: %v", peer, err)
|
||||
}
|
||||
}
|
||||
// Blocks arrived, try to update the progress
|
||||
|
@ -859,10 +833,15 @@ func (d *Downloader) fetchBlocks61(from uint64) error {
|
|||
return errNoPeers
|
||||
}
|
||||
// Check for block request timeouts and demote the responsible peers
|
||||
for _, pid := range d.queue.ExpireBlocks(blockHardTTL) {
|
||||
for pid, fails := range d.queue.ExpireBlocks(blockTTL) {
|
||||
if peer := d.peers.Peer(pid); peer != nil {
|
||||
peer.Demote()
|
||||
glog.V(logger.Detail).Infof("%s: block delivery timeout", peer)
|
||||
if fails > 1 {
|
||||
glog.V(logger.Detail).Infof("%s: block delivery timeout", peer)
|
||||
peer.SetBlocksIdle(0)
|
||||
} else {
|
||||
glog.V(logger.Debug).Infof("%s: stalling block delivery, dropping", peer)
|
||||
d.dropPeer(pid)
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there's nothing more to fetch, wait or terminate
|
||||
|
@ -909,6 +888,13 @@ func (d *Downloader) fetchBlocks61(from uint64) error {
|
|||
if !throttled && !d.queue.InFlightBlocks() && len(idles) == total {
|
||||
return errPeersUnavailable
|
||||
}
|
||||
|
||||
case <-d.headerCh:
|
||||
case <-d.bodyCh:
|
||||
case <-d.stateCh:
|
||||
case <-d.receiptCh:
|
||||
// Ignore eth/{62,63} packets because this is eth/61.
|
||||
// These can arrive as a late delivery from a previous sync.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -941,18 +927,19 @@ func (d *Downloader) fetchHeight(p *peer) (uint64, error) {
|
|||
}
|
||||
return headers[0].Number.Uint64(), nil
|
||||
|
||||
case <-d.bodyCh:
|
||||
// Out of bounds block bodies received, ignore them
|
||||
|
||||
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
|
||||
|
||||
case <-d.bodyCh:
|
||||
case <-d.stateCh:
|
||||
case <-d.receiptCh:
|
||||
// Out of bounds delivery, ignore
|
||||
|
||||
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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1008,18 +995,19 @@ func (d *Downloader) findAncestor(p *peer) (uint64, error) {
|
|||
}
|
||||
}
|
||||
|
||||
case <-d.bodyCh:
|
||||
// Out of bounds block bodies received, ignore them
|
||||
|
||||
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
|
||||
|
||||
case <-d.bodyCh:
|
||||
case <-d.stateCh:
|
||||
case <-d.receiptCh:
|
||||
// Out of bounds delivery, ignore
|
||||
|
||||
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.
|
||||
}
|
||||
}
|
||||
// If the head fetch already found an ancestor, return
|
||||
|
@ -1068,18 +1056,19 @@ func (d *Downloader) findAncestor(p *peer) (uint64, error) {
|
|||
}
|
||||
start = check
|
||||
|
||||
case <-d.bodyCh:
|
||||
// Out of bounds block bodies received, ignore them
|
||||
|
||||
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
|
||||
|
||||
case <-d.bodyCh:
|
||||
case <-d.stateCh:
|
||||
case <-d.receiptCh:
|
||||
// Out of bounds delivery, ignore
|
||||
|
||||
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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1141,12 +1130,6 @@ func (d *Downloader) fetchHeaders(p *peer, td *big.Int, from uint64) error {
|
|||
case <-d.cancelCh:
|
||||
return errCancelHeaderFetch
|
||||
|
||||
case <-d.hashCh:
|
||||
// Out of bounds eth/61 hashes received, ignore them
|
||||
|
||||
case <-d.blockCh:
|
||||
// Out of bounds eth/61 blocks received, ignore them
|
||||
|
||||
case packet := <-d.headerCh:
|
||||
// Make sure the active peer is giving us the headers
|
||||
if packet.PeerId() != p.id {
|
||||
|
@ -1268,6 +1251,11 @@ func (d *Downloader) fetchHeaders(p *peer, td *big.Int, from uint64) error {
|
|||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
case <-d.hashCh:
|
||||
case <-d.blockCh:
|
||||
// Ignore eth/61 packets because this is eth/62+.
|
||||
// These can arrive as a late delivery from a previous sync.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1279,14 +1267,14 @@ func (d *Downloader) fetchBodies(from uint64) error {
|
|||
glog.V(logger.Debug).Infof("Downloading block bodies from #%d", from)
|
||||
|
||||
var (
|
||||
deliver = func(packet dataPack) error {
|
||||
deliver = func(packet dataPack) (int, error) {
|
||||
pack := packet.(*bodyPack)
|
||||
return d.queue.DeliverBodies(pack.peerId, pack.transactions, pack.uncles)
|
||||
}
|
||||
expire = func() []string { return d.queue.ExpireBodies(bodyHardTTL) }
|
||||
expire = func() map[string]int { return d.queue.ExpireBodies(bodyTTL) }
|
||||
fetch = func(p *peer, req *fetchRequest) error { return p.FetchBodies(req) }
|
||||
capacity = func(p *peer) int { return p.BlockCapacity() }
|
||||
setIdle = func(p *peer) { p.SetBodiesIdle() }
|
||||
setIdle = func(p *peer, accepted int) { p.SetBodiesIdle(accepted) }
|
||||
)
|
||||
err := d.fetchParts(errCancelBodyFetch, d.bodyCh, deliver, d.bodyWakeCh, expire,
|
||||
d.queue.PendingBlocks, d.queue.InFlightBlocks, d.queue.ShouldThrottleBlocks, d.queue.ReserveBodies,
|
||||
|
@ -1303,14 +1291,14 @@ func (d *Downloader) fetchReceipts(from uint64) error {
|
|||
glog.V(logger.Debug).Infof("Downloading receipts from #%d", from)
|
||||
|
||||
var (
|
||||
deliver = func(packet dataPack) error {
|
||||
deliver = func(packet dataPack) (int, error) {
|
||||
pack := packet.(*receiptPack)
|
||||
return d.queue.DeliverReceipts(pack.peerId, pack.receipts)
|
||||
}
|
||||
expire = func() []string { return d.queue.ExpireReceipts(receiptHardTTL) }
|
||||
expire = func() map[string]int { return d.queue.ExpireReceipts(receiptTTL) }
|
||||
fetch = func(p *peer, req *fetchRequest) error { return p.FetchReceipts(req) }
|
||||
capacity = func(p *peer) int { return p.ReceiptCapacity() }
|
||||
setIdle = func(p *peer) { p.SetReceiptsIdle() }
|
||||
setIdle = func(p *peer, accepted int) { p.SetReceiptsIdle(accepted) }
|
||||
)
|
||||
err := d.fetchParts(errCancelReceiptFetch, d.receiptCh, deliver, d.receiptWakeCh, expire,
|
||||
d.queue.PendingReceipts, d.queue.InFlightReceipts, d.queue.ShouldThrottleReceipts, d.queue.ReserveReceipts,
|
||||
|
@ -1327,7 +1315,7 @@ func (d *Downloader) fetchNodeData() error {
|
|||
glog.V(logger.Debug).Infof("Downloading node state data")
|
||||
|
||||
var (
|
||||
deliver = func(packet dataPack) error {
|
||||
deliver = func(packet dataPack) (int, error) {
|
||||
start := time.Now()
|
||||
return d.queue.DeliverNodeData(packet.PeerId(), packet.(*statePack).states, func(err error, delivered int) {
|
||||
if err != nil {
|
||||
|
@ -1336,10 +1324,8 @@ func (d *Downloader) fetchNodeData() error {
|
|||
d.cancel()
|
||||
return
|
||||
}
|
||||
// Processing succeeded, notify state fetcher and processor of continuation
|
||||
if d.queue.PendingNodeData() == 0 {
|
||||
go d.process()
|
||||
} else {
|
||||
// Processing succeeded, notify state fetcher of continuation
|
||||
if d.queue.PendingNodeData() > 0 {
|
||||
select {
|
||||
case d.stateWakeCh <- true:
|
||||
default:
|
||||
|
@ -1348,19 +1334,18 @@ func (d *Downloader) fetchNodeData() error {
|
|||
// Log a message to the user and return
|
||||
d.syncStatsLock.Lock()
|
||||
defer d.syncStatsLock.Unlock()
|
||||
|
||||
d.syncStatsStateDone += uint64(delivered)
|
||||
glog.V(logger.Info).Infof("imported %d state entries in %v: processed %d in total", delivered, time.Since(start), d.syncStatsStateDone)
|
||||
})
|
||||
}
|
||||
expire = func() []string { return d.queue.ExpireNodeData(stateHardTTL) }
|
||||
expire = func() map[string]int { return d.queue.ExpireNodeData(stateTTL) }
|
||||
throttle = func() bool { return false }
|
||||
reserve = func(p *peer, count int) (*fetchRequest, bool, error) {
|
||||
return d.queue.ReserveNodeData(p, count), false, nil
|
||||
}
|
||||
fetch = func(p *peer, req *fetchRequest) error { return p.FetchNodeData(req) }
|
||||
capacity = func(p *peer) int { return p.NodeDataCapacity() }
|
||||
setIdle = func(p *peer) { p.SetNodeDataIdle() }
|
||||
setIdle = func(p *peer, accepted int) { p.SetNodeDataIdle(accepted) }
|
||||
)
|
||||
err := d.fetchParts(errCancelStateFetch, d.stateCh, deliver, d.stateWakeCh, expire,
|
||||
d.queue.PendingNodeData, d.queue.InFlightNodeData, throttle, reserve, nil, fetch,
|
||||
|
@ -1373,10 +1358,10 @@ func (d *Downloader) fetchNodeData() error {
|
|||
// fetchParts iteratively downloads scheduled block parts, taking any available
|
||||
// peers, reserving a chunk of fetch requests for each, waiting for delivery and
|
||||
// also periodically checking for timeouts.
|
||||
func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliver func(packet dataPack) error, wakeCh chan bool,
|
||||
expire func() []string, pending func() int, inFlight func() bool, throttle func() bool, reserve func(*peer, int) (*fetchRequest, bool, error),
|
||||
func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliver func(dataPack) (int, error), wakeCh chan bool,
|
||||
expire func() map[string]int, pending func() int, inFlight func() bool, throttle func() bool, reserve func(*peer, int) (*fetchRequest, bool, error),
|
||||
fetchHook func([]*types.Header), fetch func(*peer, *fetchRequest) error, cancel func(*fetchRequest), capacity func(*peer) int,
|
||||
idle func() ([]*peer, int), setIdle func(*peer), kind string) error {
|
||||
idle func() ([]*peer, int), setIdle func(*peer, int), kind string) error {
|
||||
|
||||
// Create a ticker to detect expired retrieval tasks
|
||||
ticker := time.NewTicker(100 * time.Millisecond)
|
||||
|
@ -1391,57 +1376,29 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
|
|||
case <-d.cancelCh:
|
||||
return errCancel
|
||||
|
||||
case <-d.hashCh:
|
||||
// Out of bounds eth/61 hashes received, ignore them
|
||||
|
||||
case <-d.blockCh:
|
||||
// Out of bounds eth/61 blocks received, ignore them
|
||||
|
||||
case packet := <-deliveryCh:
|
||||
// If the peer was previously banned and failed to deliver it's pack
|
||||
// in a reasonable time frame, ignore it's message.
|
||||
if peer := d.peers.Peer(packet.PeerId()); peer != nil {
|
||||
// Deliver the received chunk of data, and demote in case of errors
|
||||
switch err := deliver(packet); err {
|
||||
case nil:
|
||||
// If no blocks were delivered, demote the peer (need the delivery above to clean internal queue!)
|
||||
if packet.Items() == 0 {
|
||||
peer.Demote()
|
||||
setIdle(peer)
|
||||
glog.V(logger.Detail).Infof("%s: no %s delivered", peer, strings.ToLower(kind))
|
||||
break
|
||||
}
|
||||
// All was successful, promote the peer and potentially start processing
|
||||
peer.Promote()
|
||||
setIdle(peer)
|
||||
glog.V(logger.Detail).Infof("%s: delivered %s %s(s)", peer, packet.Stats(), strings.ToLower(kind))
|
||||
go d.process()
|
||||
|
||||
case errInvalidChain:
|
||||
// The hash chain is invalid (blocks are not ordered properly), abort
|
||||
// Deliver the received chunk of data and check chain validity
|
||||
accepted, err := deliver(packet)
|
||||
if err == errInvalidChain {
|
||||
return err
|
||||
|
||||
case errNoFetchesPending:
|
||||
// Peer probably timed out with its delivery but came through
|
||||
// in the end, demote, but allow to to pull from this peer.
|
||||
peer.Demote()
|
||||
setIdle(peer)
|
||||
glog.V(logger.Detail).Infof("%s: out of bound %s delivery", peer, strings.ToLower(kind))
|
||||
|
||||
case errStaleDelivery:
|
||||
// Delivered something completely else than requested, usually
|
||||
// caused by a timeout and delivery during a new sync cycle.
|
||||
// Don't set it to idle as the original request should still be
|
||||
// in flight.
|
||||
peer.Demote()
|
||||
glog.V(logger.Detail).Infof("%s: %s stale delivery", peer, strings.ToLower(kind))
|
||||
|
||||
}
|
||||
// Unless a peer delivered something completely else than requested (usually
|
||||
// caused by a timed out request which came through in the end), set it to
|
||||
// idle. If the delivery's stale, the peer should have already been idled.
|
||||
if err != errStaleDelivery {
|
||||
setIdle(peer, accepted)
|
||||
}
|
||||
// Issue a log to the user to see what's going on
|
||||
switch {
|
||||
case err == nil && packet.Items() == 0:
|
||||
glog.V(logger.Detail).Infof("%s: no %s delivered", peer, strings.ToLower(kind))
|
||||
case err == nil:
|
||||
glog.V(logger.Detail).Infof("%s: delivered %s %s(s)", peer, packet.Stats(), strings.ToLower(kind))
|
||||
default:
|
||||
// Peer did something semi-useful, demote but keep it around
|
||||
peer.Demote()
|
||||
setIdle(peer)
|
||||
glog.V(logger.Detail).Infof("%s: %s delivery partially failed: %v", peer, strings.ToLower(kind), err)
|
||||
go d.process()
|
||||
glog.V(logger.Detail).Infof("%s: %s delivery failed: %v", peer, strings.ToLower(kind), err)
|
||||
}
|
||||
}
|
||||
// Blocks assembled, try to update the progress
|
||||
|
@ -1474,11 +1431,15 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
|
|||
return errNoPeers
|
||||
}
|
||||
// Check for fetch request timeouts and demote the responsible peers
|
||||
for _, pid := range expire() {
|
||||
for pid, fails := range expire() {
|
||||
if peer := d.peers.Peer(pid); peer != nil {
|
||||
peer.Demote()
|
||||
setIdle(peer)
|
||||
glog.V(logger.Detail).Infof("%s: %s delivery timeout", peer, strings.ToLower(kind))
|
||||
if fails > 1 {
|
||||
glog.V(logger.Detail).Infof("%s: %s delivery timeout", peer, strings.ToLower(kind))
|
||||
setIdle(peer, 0)
|
||||
} else {
|
||||
glog.V(logger.Debug).Infof("%s: stalling %s delivery, dropping", peer, strings.ToLower(kind))
|
||||
d.dropPeer(pid)
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there's nothing more to fetch, wait or terminate
|
||||
|
@ -1508,7 +1469,6 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
|
|||
}
|
||||
if progress {
|
||||
progressed = true
|
||||
go d.process()
|
||||
}
|
||||
if request == nil {
|
||||
continue
|
||||
|
@ -1540,51 +1500,23 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
|
|||
if !progressed && !throttled && !running && len(idles) == total && pending() > 0 {
|
||||
return errPeersUnavailable
|
||||
}
|
||||
|
||||
case <-d.hashCh:
|
||||
case <-d.blockCh:
|
||||
// Ignore eth/61 packets because this is eth/62+.
|
||||
// These can arrive as a late delivery from a previous sync.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// process takes fetch results from the queue and tries to import them into the
|
||||
// chain. The type of import operation will depend on the result contents:
|
||||
// -
|
||||
//
|
||||
// The algorithmic flow is as follows:
|
||||
// - The `processing` flag is swapped to 1 to ensure singleton access
|
||||
// - The current `cancel` channel is retrieved to detect sync abortions
|
||||
// - Blocks are iteratively taken from the cache and inserted into the chain
|
||||
// - When the cache becomes empty, insertion stops
|
||||
// - The `processing` flag is swapped back to 0
|
||||
// - A post-exit check is made whether new blocks became available
|
||||
// - This step is important: it handles a potential race condition between
|
||||
// checking for no more work, and releasing the processing "mutex". In
|
||||
// between these state changes, a block may have arrived, but a processing
|
||||
// attempt denied, so we need to re-enter to ensure the block isn't left
|
||||
// to idle in the cache.
|
||||
func (d *Downloader) process() {
|
||||
// Make sure only one goroutine is ever allowed to process blocks at once
|
||||
if !atomic.CompareAndSwapInt32(&d.processing, 0, 1) {
|
||||
return
|
||||
}
|
||||
// If the processor just exited, but there are freshly pending items, try to
|
||||
// reenter. This is needed because the goroutine spinned up for processing
|
||||
// the fresh results might have been rejected entry to to this present thread
|
||||
// not yet releasing the `processing` state.
|
||||
defer func() {
|
||||
if atomic.LoadInt32(&d.interrupt) == 0 && d.queue.GetHeadResult() != nil {
|
||||
d.process()
|
||||
}
|
||||
}()
|
||||
// Release the lock upon exit (note, before checking for reentry!)
|
||||
// the import statistics to zero.
|
||||
defer atomic.StoreInt32(&d.processing, 0)
|
||||
|
||||
// Repeat the processing as long as there are results to process
|
||||
// chain. The type of import operation will depend on the result contents.
|
||||
func (d *Downloader) process() error {
|
||||
pivot := d.queue.FastSyncPivot()
|
||||
for {
|
||||
// Fetch the next batch of results
|
||||
pivot := d.queue.FastSyncPivot() // Fetch pivot before results to prevent reset race
|
||||
results := d.queue.TakeResults()
|
||||
results := d.queue.WaitResults()
|
||||
if len(results) == 0 {
|
||||
return
|
||||
return nil // queue empty
|
||||
}
|
||||
if d.chainInsertHook != nil {
|
||||
d.chainInsertHook(results)
|
||||
|
@ -1597,7 +1529,7 @@ func (d *Downloader) process() {
|
|||
for len(results) != 0 {
|
||||
// Check for any termination requests
|
||||
if atomic.LoadInt32(&d.interrupt) == 1 {
|
||||
return
|
||||
return errCancelProcessing
|
||||
}
|
||||
// Retrieve the a batch of results to import
|
||||
var (
|
||||
|
@ -1633,8 +1565,7 @@ func (d *Downloader) process() {
|
|||
}
|
||||
if err != nil {
|
||||
glog.V(logger.Debug).Infof("Result #%d [%x…] processing failed: %v", results[index].Header.Number, results[index].Header.Hash().Bytes()[:4], err)
|
||||
d.cancel()
|
||||
return
|
||||
return err
|
||||
}
|
||||
// Shift the results to the next batch
|
||||
results = results[items:]
|
||||
|
@ -1685,19 +1616,16 @@ func (d *Downloader) deliver(id string, destCh chan dataPack, packet dataPack, i
|
|||
dropMeter.Mark(int64(packet.Items()))
|
||||
}
|
||||
}()
|
||||
// Make sure the downloader is active
|
||||
if atomic.LoadInt32(&d.synchronising) == 0 {
|
||||
return errNoSyncActive
|
||||
}
|
||||
// Deliver or abort if the sync is canceled while queuing
|
||||
d.cancelLock.RLock()
|
||||
cancel := d.cancelCh
|
||||
d.cancelLock.RUnlock()
|
||||
|
||||
if cancel == nil {
|
||||
return errNoSyncActive
|
||||
}
|
||||
select {
|
||||
case destCh <- packet:
|
||||
return nil
|
||||
|
||||
case <-cancel:
|
||||
return errNoSyncActive
|
||||
}
|
||||
|
|
|
@ -169,17 +169,7 @@ func (dl *downloadTester) sync(id string, td *big.Int, mode SyncMode) error {
|
|||
}
|
||||
}
|
||||
dl.lock.RUnlock()
|
||||
|
||||
err := dl.downloader.synchronise(id, hash, td, mode)
|
||||
for {
|
||||
// If the queue is empty and processing stopped, break
|
||||
if dl.downloader.queue.Idle() && atomic.LoadInt32(&dl.downloader.processing) == 0 {
|
||||
break
|
||||
}
|
||||
// Otherwise sleep a bit and retry
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
return err
|
||||
return dl.downloader.synchronise(id, hash, td, mode)
|
||||
}
|
||||
|
||||
// hasHeader checks if a header is present in the testers canonical chain.
|
||||
|
@ -701,6 +691,8 @@ func TestCanonicalSynchronisation64Fast(t *testing.T) { testCanonicalSynchronis
|
|||
func TestCanonicalSynchronisation64Light(t *testing.T) { testCanonicalSynchronisation(t, 64, LightSync) }
|
||||
|
||||
func testCanonicalSynchronisation(t *testing.T, protocol int, mode SyncMode) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a small enough block chain to download
|
||||
targetBlocks := blockCacheLimit - 15
|
||||
hashes, headers, blocks, receipts := makeChain(targetBlocks, 0, genesis, nil)
|
||||
|
@ -725,6 +717,8 @@ func TestThrottling64Full(t *testing.T) { testThrottling(t, 64, FullSync) }
|
|||
func TestThrottling64Fast(t *testing.T) { testThrottling(t, 64, FastSync) }
|
||||
|
||||
func testThrottling(t *testing.T, protocol int, mode SyncMode) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a long block chain to download and the tester
|
||||
targetBlocks := 8 * blockCacheLimit
|
||||
hashes, headers, blocks, receipts := makeChain(targetBlocks, 0, genesis, nil)
|
||||
|
@ -757,8 +751,8 @@ func testThrottling(t *testing.T, protocol int, mode SyncMode) {
|
|||
for start := time.Now(); time.Since(start) < time.Second; {
|
||||
time.Sleep(25 * time.Millisecond)
|
||||
|
||||
tester.lock.RLock()
|
||||
tester.downloader.queue.lock.RLock()
|
||||
tester.lock.Lock()
|
||||
tester.downloader.queue.lock.Lock()
|
||||
cached = len(tester.downloader.queue.blockDonePool)
|
||||
if mode == FastSync {
|
||||
if receipts := len(tester.downloader.queue.receiptDonePool); receipts < cached {
|
||||
|
@ -769,8 +763,8 @@ func testThrottling(t *testing.T, protocol int, mode SyncMode) {
|
|||
}
|
||||
frozen = int(atomic.LoadUint32(&blocked))
|
||||
retrieved = len(tester.ownBlocks)
|
||||
tester.downloader.queue.lock.RUnlock()
|
||||
tester.lock.RUnlock()
|
||||
tester.downloader.queue.lock.Unlock()
|
||||
tester.lock.Unlock()
|
||||
|
||||
if cached == blockCacheLimit || retrieved+cached+frozen == targetBlocks+1 {
|
||||
break
|
||||
|
@ -810,6 +804,8 @@ func TestForkedSynchronisation64Fast(t *testing.T) { testForkedSynchronisation(
|
|||
func TestForkedSynchronisation64Light(t *testing.T) { testForkedSynchronisation(t, 64, LightSync) }
|
||||
|
||||
func testForkedSynchronisation(t *testing.T, protocol int, mode SyncMode) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a long enough forked chain
|
||||
common, fork := MaxHashFetch, 2*MaxHashFetch
|
||||
hashesA, hashesB, headersA, headersB, blocksA, blocksB, receiptsA, receiptsB := makeChainFork(common+fork, fork, genesis, nil)
|
||||
|
@ -833,6 +829,7 @@ func testForkedSynchronisation(t *testing.T, protocol int, mode SyncMode) {
|
|||
|
||||
// Tests that an inactive downloader will not accept incoming hashes and blocks.
|
||||
func TestInactiveDownloader61(t *testing.T) {
|
||||
t.Parallel()
|
||||
tester := newTester()
|
||||
|
||||
// Check that neither hashes nor blocks are accepted
|
||||
|
@ -847,6 +844,7 @@ func TestInactiveDownloader61(t *testing.T) {
|
|||
// Tests that an inactive downloader will not accept incoming block headers and
|
||||
// bodies.
|
||||
func TestInactiveDownloader62(t *testing.T) {
|
||||
t.Parallel()
|
||||
tester := newTester()
|
||||
|
||||
// Check that neither block headers nor bodies are accepted
|
||||
|
@ -861,6 +859,7 @@ func TestInactiveDownloader62(t *testing.T) {
|
|||
// Tests that an inactive downloader will not accept incoming block headers,
|
||||
// bodies and receipts.
|
||||
func TestInactiveDownloader63(t *testing.T) {
|
||||
t.Parallel()
|
||||
tester := newTester()
|
||||
|
||||
// Check that neither block headers nor bodies are accepted
|
||||
|
@ -885,6 +884,8 @@ func TestCancel64Fast(t *testing.T) { testCancel(t, 64, FastSync) }
|
|||
func TestCancel64Light(t *testing.T) { testCancel(t, 64, LightSync) }
|
||||
|
||||
func testCancel(t *testing.T, protocol int, mode SyncMode) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a small enough block chain to download and the tester
|
||||
targetBlocks := blockCacheLimit - 15
|
||||
if targetBlocks >= MaxHashFetch {
|
||||
|
@ -923,6 +924,8 @@ func TestMultiSynchronisation64Fast(t *testing.T) { testMultiSynchronisation(t,
|
|||
func TestMultiSynchronisation64Light(t *testing.T) { testMultiSynchronisation(t, 64, LightSync) }
|
||||
|
||||
func testMultiSynchronisation(t *testing.T, protocol int, mode SyncMode) {
|
||||
t.Parallel()
|
||||
|
||||
// Create various peers with various parts of the chain
|
||||
targetPeers := 8
|
||||
targetBlocks := targetPeers*blockCacheLimit - 15
|
||||
|
@ -950,6 +953,8 @@ func TestMultiProtoSynchronisation64Fast(t *testing.T) { testMultiProtoSync(t,
|
|||
func TestMultiProtoSynchronisation64Light(t *testing.T) { testMultiProtoSync(t, 64, LightSync) }
|
||||
|
||||
func testMultiProtoSync(t *testing.T, protocol int, mode SyncMode) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a small enough block chain to download
|
||||
targetBlocks := blockCacheLimit - 15
|
||||
hashes, headers, blocks, receipts := makeChain(targetBlocks, 0, genesis, nil)
|
||||
|
@ -986,6 +991,8 @@ func TestEmptyShortCircuit64Fast(t *testing.T) { testEmptyShortCircuit(t, 64, F
|
|||
func TestEmptyShortCircuit64Light(t *testing.T) { testEmptyShortCircuit(t, 64, LightSync) }
|
||||
|
||||
func testEmptyShortCircuit(t *testing.T, protocol int, mode SyncMode) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a block chain to download
|
||||
targetBlocks := 2*blockCacheLimit - 15
|
||||
hashes, headers, blocks, receipts := makeChain(targetBlocks, 0, genesis, nil)
|
||||
|
@ -1037,6 +1044,8 @@ func TestMissingHeaderAttack64Fast(t *testing.T) { testMissingHeaderAttack(t, 6
|
|||
func TestMissingHeaderAttack64Light(t *testing.T) { testMissingHeaderAttack(t, 64, LightSync) }
|
||||
|
||||
func testMissingHeaderAttack(t *testing.T, protocol int, mode SyncMode) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a small enough block chain to download
|
||||
targetBlocks := blockCacheLimit - 15
|
||||
hashes, headers, blocks, receipts := makeChain(targetBlocks, 0, genesis, nil)
|
||||
|
@ -1188,6 +1197,8 @@ func TestHighTDStarvationAttack64Fast(t *testing.T) { testHighTDStarvationAttac
|
|||
func TestHighTDStarvationAttack64Light(t *testing.T) { testHighTDStarvationAttack(t, 64, LightSync) }
|
||||
|
||||
func testHighTDStarvationAttack(t *testing.T, protocol int, mode SyncMode) {
|
||||
t.Parallel()
|
||||
|
||||
tester := newTester()
|
||||
hashes, headers, blocks, receipts := makeChain(0, 0, genesis, nil)
|
||||
|
||||
|
@ -1209,25 +1220,26 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol int) {
|
|||
result error
|
||||
drop bool
|
||||
}{
|
||||
{nil, false}, // Sync succeeded, all is well
|
||||
{errBusy, false}, // Sync is already in progress, no problem
|
||||
{errUnknownPeer, false}, // Peer is unknown, was already dropped, don't double drop
|
||||
{errBadPeer, true}, // Peer was deemed bad for some reason, drop it
|
||||
{errStallingPeer, true}, // Peer was detected to be stalling, drop it
|
||||
{errNoPeers, false}, // No peers to download from, soft race, no issue
|
||||
{errPendingQueue, false}, // There are blocks still cached, wait to exhaust, no issue
|
||||
{errTimeout, true}, // No hashes received in due time, drop the peer
|
||||
{errEmptyHashSet, true}, // No hashes were returned as a response, drop as it's a dead end
|
||||
{errEmptyHeaderSet, true}, // No headers were returned as a response, drop as it's a dead end
|
||||
{errPeersUnavailable, true}, // Nobody had the advertised blocks, drop the advertiser
|
||||
{errInvalidChain, true}, // Hash chain was detected as invalid, definitely drop
|
||||
{errInvalidBlock, false}, // A bad peer was detected, but not the sync origin
|
||||
{errInvalidBody, false}, // A bad peer was detected, but not the sync origin
|
||||
{errInvalidReceipt, false}, // A bad peer was detected, but not the sync origin
|
||||
{errCancelHashFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
|
||||
{errCancelBlockFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
|
||||
{errCancelHeaderFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
|
||||
{errCancelBodyFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
|
||||
{nil, false}, // Sync succeeded, all is well
|
||||
{errBusy, false}, // Sync is already in progress, no problem
|
||||
{errUnknownPeer, false}, // Peer is unknown, was already dropped, don't double drop
|
||||
{errBadPeer, true}, // Peer was deemed bad for some reason, drop it
|
||||
{errStallingPeer, true}, // Peer was detected to be stalling, drop it
|
||||
{errNoPeers, false}, // No peers to download from, soft race, no issue
|
||||
{errTimeout, true}, // No hashes received in due time, drop the peer
|
||||
{errEmptyHashSet, true}, // No hashes were returned as a response, drop as it's a dead end
|
||||
{errEmptyHeaderSet, true}, // No headers were returned as a response, drop as it's a dead end
|
||||
{errPeersUnavailable, true}, // Nobody had the advertised blocks, drop the advertiser
|
||||
{errInvalidChain, true}, // Hash chain was detected as invalid, definitely drop
|
||||
{errInvalidBlock, false}, // A bad peer was detected, but not the sync origin
|
||||
{errInvalidBody, false}, // A bad peer was detected, but not the sync origin
|
||||
{errInvalidReceipt, false}, // A bad peer was detected, but not the sync origin
|
||||
{errCancelHashFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
|
||||
{errCancelBlockFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
|
||||
{errCancelHeaderFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
|
||||
{errCancelBodyFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
|
||||
{errCancelReceiptFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
|
||||
{errCancelProcessing, false}, // Synchronisation was canceled, origin may be innocent, don't drop
|
||||
}
|
||||
// Run the tests and check disconnection status
|
||||
tester := newTester()
|
||||
|
@ -1261,6 +1273,8 @@ func TestSyncProgress64Fast(t *testing.T) { testSyncProgress(t, 64, FastSync) }
|
|||
func TestSyncProgress64Light(t *testing.T) { testSyncProgress(t, 64, LightSync) }
|
||||
|
||||
func testSyncProgress(t *testing.T, protocol int, mode SyncMode) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a small enough block chain to download
|
||||
targetBlocks := blockCacheLimit - 15
|
||||
hashes, headers, blocks, receipts := makeChain(targetBlocks, 0, genesis, nil)
|
||||
|
@ -1331,6 +1345,8 @@ func TestForkedSyncProgress64Fast(t *testing.T) { testForkedSyncProgress(t, 64,
|
|||
func TestForkedSyncProgress64Light(t *testing.T) { testForkedSyncProgress(t, 64, LightSync) }
|
||||
|
||||
func testForkedSyncProgress(t *testing.T, protocol int, mode SyncMode) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a forked chain to simulate origin revertal
|
||||
common, fork := MaxHashFetch, 2*MaxHashFetch
|
||||
hashesA, hashesB, headersA, headersB, blocksA, blocksB, receiptsA, receiptsB := makeChainFork(common+fork, fork, genesis, nil)
|
||||
|
@ -1404,6 +1420,8 @@ func TestFailedSyncProgress64Fast(t *testing.T) { testFailedSyncProgress(t, 64,
|
|||
func TestFailedSyncProgress64Light(t *testing.T) { testFailedSyncProgress(t, 64, LightSync) }
|
||||
|
||||
func testFailedSyncProgress(t *testing.T, protocol int, mode SyncMode) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a small enough block chain to download
|
||||
targetBlocks := blockCacheLimit - 15
|
||||
hashes, headers, blocks, receipts := makeChain(targetBlocks, 0, genesis, nil)
|
||||
|
@ -1478,6 +1496,8 @@ func TestFakedSyncProgress64Fast(t *testing.T) { testFakedSyncProgress(t, 64, F
|
|||
func TestFakedSyncProgress64Light(t *testing.T) { testFakedSyncProgress(t, 64, LightSync) }
|
||||
|
||||
func testFakedSyncProgress(t *testing.T, protocol int, mode SyncMode) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a small block chain
|
||||
targetBlocks := blockCacheLimit - 15
|
||||
hashes, headers, blocks, receipts := makeChain(targetBlocks+3, 0, genesis, nil)
|
||||
|
@ -1541,3 +1561,50 @@ func testFakedSyncProgress(t *testing.T, protocol int, mode SyncMode) {
|
|||
t.Fatalf("Final progress mismatch: have %v/%v/%v, want 0-%v/%v/%v", origin, current, latest, targetBlocks, targetBlocks, targetBlocks)
|
||||
}
|
||||
}
|
||||
|
||||
// This test reproduces an issue where unexpected deliveries would
|
||||
// block indefinitely if they arrived at the right time.
|
||||
func TestDeliverHeadersHang62(t *testing.T) { testDeliverHeadersHang(t, 62, FullSync) }
|
||||
func TestDeliverHeadersHang63Full(t *testing.T) { testDeliverHeadersHang(t, 63, FullSync) }
|
||||
func TestDeliverHeadersHang63Fast(t *testing.T) { testDeliverHeadersHang(t, 63, FastSync) }
|
||||
func TestDeliverHeadersHang64Full(t *testing.T) { testDeliverHeadersHang(t, 64, FullSync) }
|
||||
func TestDeliverHeadersHang64Fast(t *testing.T) { testDeliverHeadersHang(t, 64, FastSync) }
|
||||
func TestDeliverHeadersHang64Light(t *testing.T) { testDeliverHeadersHang(t, 64, LightSync) }
|
||||
|
||||
func testDeliverHeadersHang(t *testing.T, protocol int, mode SyncMode) {
|
||||
t.Parallel()
|
||||
hashes, headers, blocks, receipts := makeChain(5, 0, genesis, nil)
|
||||
fakeHeads := []*types.Header{{}, {}, {}, {}}
|
||||
for i := 0; i < 200; i++ {
|
||||
tester := newTester()
|
||||
tester.newPeer("peer", protocol, hashes, headers, blocks, receipts)
|
||||
// Whenever the downloader requests headers, flood it with
|
||||
// a lot of unrequested header deliveries.
|
||||
tester.downloader.peers.peers["peer"].getAbsHeaders = func(from uint64, count, skip int, reverse bool) error {
|
||||
deliveriesDone := make(chan struct{}, 500)
|
||||
for i := 0; i < cap(deliveriesDone); i++ {
|
||||
peer := fmt.Sprintf("fake-peer%d", i)
|
||||
go func() {
|
||||
tester.downloader.DeliverHeaders(peer, fakeHeads)
|
||||
deliveriesDone <- struct{}{}
|
||||
}()
|
||||
}
|
||||
// Deliver the actual requested headers.
|
||||
impl := tester.peerGetAbsHeadersFn("peer", 0)
|
||||
go impl(from, count, skip, reverse)
|
||||
// None of the extra deliveries should block.
|
||||
timeout := time.After(5 * time.Second)
|
||||
for i := 0; i < cap(deliveriesDone); i++ {
|
||||
select {
|
||||
case <-deliveriesDone:
|
||||
case <-timeout:
|
||||
panic("blocked")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := tester.sync("peer", nil, mode); err != nil {
|
||||
t.Errorf("sync failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,11 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"gopkg.in/fatih/set.v0"
|
||||
)
|
||||
|
||||
const (
|
||||
maxLackingHashes = 4096 // Maximum number of entries allowed on the list or lacking items
|
||||
throughputImpact = 0.1 // The impact a single measurement has on a peer's final throughput value.
|
||||
)
|
||||
|
||||
// Hash and block fetchers belonging to eth/61 and below
|
||||
|
@ -57,17 +61,16 @@ type peer struct {
|
|||
blockIdle int32 // Current block activity state of the peer (idle = 0, active = 1)
|
||||
receiptIdle int32 // Current receipt activity state of the peer (idle = 0, active = 1)
|
||||
stateIdle int32 // Current node data activity state of the peer (idle = 0, active = 1)
|
||||
rep int32 // Simple peer reputation
|
||||
|
||||
blockCapacity int32 // Number of blocks (bodies) allowed to fetch per request
|
||||
receiptCapacity int32 // Number of receipts allowed to fetch per request
|
||||
stateCapacity int32 // Number of node data pieces allowed to fetch per request
|
||||
blockThroughput float64 // Number of blocks (bodies) measured to be retrievable per second
|
||||
receiptThroughput float64 // Number of receipts measured to be retrievable per second
|
||||
stateThroughput float64 // Number of node data pieces measured to be retrievable per second
|
||||
|
||||
blockStarted time.Time // Time instance when the last block (body)fetch was started
|
||||
receiptStarted time.Time // Time instance when the last receipt fetch was started
|
||||
stateStarted time.Time // Time instance when the last node data fetch was started
|
||||
|
||||
ignored *set.Set // Set of hashes not to request (didn't have previously)
|
||||
lacking map[common.Hash]struct{} // Set of hashes not to request (didn't have previously)
|
||||
|
||||
getRelHashes relativeHashFetcherFn // [eth/61] Method to retrieve a batch of hashes from an origin hash
|
||||
getAbsHashes absoluteHashFetcherFn // [eth/61] Method to retrieve a batch of hashes from an absolute position
|
||||
|
@ -81,6 +84,7 @@ type peer struct {
|
|||
getNodeData stateFetcherFn // [eth/63] Method to retrieve a batch of state trie data
|
||||
|
||||
version int // Eth protocol version number to switch strategies
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// newPeer create a new downloader peer, with specific hash and block retrieval
|
||||
|
@ -90,12 +94,9 @@ func newPeer(id string, version int, head common.Hash,
|
|||
getRelHeaders relativeHeaderFetcherFn, getAbsHeaders absoluteHeaderFetcherFn, getBlockBodies blockBodyFetcherFn,
|
||||
getReceipts receiptFetcherFn, getNodeData stateFetcherFn) *peer {
|
||||
return &peer{
|
||||
id: id,
|
||||
head: head,
|
||||
blockCapacity: 1,
|
||||
receiptCapacity: 1,
|
||||
stateCapacity: 1,
|
||||
ignored: set.New(),
|
||||
id: id,
|
||||
head: head,
|
||||
lacking: make(map[common.Hash]struct{}),
|
||||
|
||||
getRelHashes: getRelHashes,
|
||||
getAbsHashes: getAbsHashes,
|
||||
|
@ -114,12 +115,18 @@ func newPeer(id string, version int, head common.Hash,
|
|||
|
||||
// Reset clears the internal state of a peer entity.
|
||||
func (p *peer) Reset() {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
atomic.StoreInt32(&p.blockIdle, 0)
|
||||
atomic.StoreInt32(&p.receiptIdle, 0)
|
||||
atomic.StoreInt32(&p.blockCapacity, 1)
|
||||
atomic.StoreInt32(&p.receiptCapacity, 1)
|
||||
atomic.StoreInt32(&p.stateCapacity, 1)
|
||||
p.ignored.Clear()
|
||||
atomic.StoreInt32(&p.stateIdle, 0)
|
||||
|
||||
p.blockThroughput = 0
|
||||
p.receiptThroughput = 0
|
||||
p.stateThroughput = 0
|
||||
|
||||
p.lacking = make(map[common.Hash]struct{})
|
||||
}
|
||||
|
||||
// Fetch61 sends a block retrieval request to the remote peer.
|
||||
|
@ -210,108 +217,116 @@ func (p *peer) FetchNodeData(request *fetchRequest) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// SetBlocksIdle sets the peer to idle, allowing it to execute new retrieval requests.
|
||||
// Its block retrieval allowance will also be updated either up- or downwards,
|
||||
// depending on whether the previous fetch completed in time.
|
||||
func (p *peer) SetBlocksIdle() {
|
||||
p.setIdle(p.blockStarted, blockSoftTTL, blockHardTTL, MaxBlockFetch, &p.blockCapacity, &p.blockIdle)
|
||||
// SetBlocksIdle sets the peer to idle, allowing it to execute new block retrieval
|
||||
// requests. Its estimated block retrieval throughput is updated with that measured
|
||||
// just now.
|
||||
func (p *peer) SetBlocksIdle(delivered int) {
|
||||
p.setIdle(p.blockStarted, delivered, &p.blockThroughput, &p.blockIdle)
|
||||
}
|
||||
|
||||
// SetBodiesIdle sets the peer to idle, allowing it to execute new retrieval requests.
|
||||
// Its block body retrieval allowance will also be updated either up- or downwards,
|
||||
// depending on whether the previous fetch completed in time.
|
||||
func (p *peer) SetBodiesIdle() {
|
||||
p.setIdle(p.blockStarted, bodySoftTTL, bodyHardTTL, MaxBodyFetch, &p.blockCapacity, &p.blockIdle)
|
||||
// SetBodiesIdle sets the peer to idle, allowing it to execute block body retrieval
|
||||
// requests. Its estimated body retrieval throughput is updated with that measured
|
||||
// just now.
|
||||
func (p *peer) SetBodiesIdle(delivered int) {
|
||||
p.setIdle(p.blockStarted, delivered, &p.blockThroughput, &p.blockIdle)
|
||||
}
|
||||
|
||||
// SetReceiptsIdle sets the peer to idle, allowing it to execute new retrieval requests.
|
||||
// Its receipt retrieval allowance will also be updated either up- or downwards,
|
||||
// depending on whether the previous fetch completed in time.
|
||||
func (p *peer) SetReceiptsIdle() {
|
||||
p.setIdle(p.receiptStarted, receiptSoftTTL, receiptHardTTL, MaxReceiptFetch, &p.receiptCapacity, &p.receiptIdle)
|
||||
// SetReceiptsIdle sets the peer to idle, allowing it to execute new receipt
|
||||
// retrieval requests. Its estimated receipt retrieval throughput is updated
|
||||
// with that measured just now.
|
||||
func (p *peer) SetReceiptsIdle(delivered int) {
|
||||
p.setIdle(p.receiptStarted, delivered, &p.receiptThroughput, &p.receiptIdle)
|
||||
}
|
||||
|
||||
// SetNodeDataIdle sets the peer to idle, allowing it to execute new retrieval
|
||||
// requests. Its node data retrieval allowance will also be updated either up- or
|
||||
// downwards, depending on whether the previous fetch completed in time.
|
||||
func (p *peer) SetNodeDataIdle() {
|
||||
p.setIdle(p.stateStarted, stateSoftTTL, stateSoftTTL, MaxStateFetch, &p.stateCapacity, &p.stateIdle)
|
||||
// SetNodeDataIdle sets the peer to idle, allowing it to execute new state trie
|
||||
// data retrieval requests. Its estimated state retrieval throughput is updated
|
||||
// with that measured just now.
|
||||
func (p *peer) SetNodeDataIdle(delivered int) {
|
||||
p.setIdle(p.stateStarted, delivered, &p.stateThroughput, &p.stateIdle)
|
||||
}
|
||||
|
||||
// setIdle sets the peer to idle, allowing it to execute new retrieval requests.
|
||||
// Its data retrieval allowance will also be updated either up- or downwards,
|
||||
// depending on whether the previous fetch completed in time.
|
||||
func (p *peer) setIdle(started time.Time, softTTL, hardTTL time.Duration, maxFetch int, capacity, idle *int32) {
|
||||
// Update the peer's download allowance based on previous performance
|
||||
scale := 2.0
|
||||
if time.Since(started) > softTTL {
|
||||
scale = 0.5
|
||||
if time.Since(started) > hardTTL {
|
||||
scale = 1 / float64(maxFetch) // reduces capacity to 1
|
||||
}
|
||||
}
|
||||
for {
|
||||
// Calculate the new download bandwidth allowance
|
||||
prev := atomic.LoadInt32(capacity)
|
||||
next := int32(math.Max(1, math.Min(float64(maxFetch), float64(prev)*scale)))
|
||||
// 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)
|
||||
|
||||
// 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
|
||||
}
|
||||
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()
|
||||
|
||||
return int(math.Max(1, math.Min(p.stateThroughput*float64(stateTargetRTT)/float64(time.Second), float64(MaxStateFetch))))
|
||||
}
|
||||
|
||||
// Promote increases the peer's reputation.
|
||||
func (p *peer) Promote() {
|
||||
atomic.AddInt32(&p.rep, 1)
|
||||
}
|
||||
// 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()
|
||||
|
||||
// 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
|
||||
|
||||
// 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()
|
||||
|
||||
// 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
|
||||
}
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
return q.resultCache[0]
|
||||
}
|
||||
|
||||
// TakeResults retrieves and permanently removes a batch of fetch results from
|
||||
// the cache.
|
||||
func (q *queue) TakeResults() []*fetchResult {
|
||||
// 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()
|
||||
|
||||
// Accumulate all available results
|
||||
results := []*fetchResult{}
|
||||
for i, result := range q.resultCache {
|
||||
// Stop if no more results are ready
|
||||
if result == nil || result.Pending > 0 {
|
||||
break
|
||||
nproc := q.countProcessableItems()
|
||||
for nproc == 0 && !q.closed {
|
||||
q.active.Wait()
|
||||
nproc = q.countProcessableItems()
|
||||
}
|
||||
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)
|
||||
}
|
||||
// The fast sync pivot block may only be processed after state fetch completes
|
||||
if q.mode == FastSync && result.Header.Number.Uint64() == q.fastSyncPivot {
|
||||
if len(q.stateTaskPool) > 0 {
|
||||
break
|
||||
}
|
||||
if q.PendingNodeData() > 0 {
|
||||
break
|
||||
}
|
||||
// Even is state fetch is done, ensure post-pivot headers passed verifications
|
||||
safe := true
|
||||
for j := 0; j < fsHeaderForceVerify; j++ {
|
||||
if i+j+1 >= len(q.resultCache) || q.resultCache[i+j+1] == nil {
|
||||
safe = false
|
||||
// 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 results
|
||||
}
|
||||
|
||||
// countProcessableItems counts the processable items.
|
||||
func (q *queue) countProcessableItems() int {
|
||||
for i, result := range q.resultCache {
|
||||
// Don't process incomplete or unavailable items.
|
||||
if result == nil || result.Pending > 0 {
|
||||
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
|
||||
}
|
||||
for j := 0; j < fsHeaderForceVerify; j++ {
|
||||
if i+j+1 >= len(q.resultCache) || q.resultCache[i+j+1] == nil {
|
||||
return i
|
||||
}
|
||||
}
|
||||
}
|
||||
if !safe {
|
||||
break
|
||||
// If we're just the fast sync pivot, stop as well
|
||||
// because the following batch needs different insertion.
|
||||
// This simplifies handling the switchover in d.process.
|
||||
if bnum == q.fastSyncPivot+1 && i > 0 {
|
||||
return i
|
||||
}
|
||||
}
|
||||
// If we've just inserted the fast sync pivot, stop as the following batch needs different insertion
|
||||
if q.mode == FastSync && result.Header.Number.Uint64() == q.fastSyncPivot+1 && len(results) > 0 {
|
||||
break
|
||||
}
|
||||
results = append(results, result)
|
||||
|
||||
hash := result.Header.Hash()
|
||||
delete(q.blockDonePool, hash)
|
||||
delete(q.receiptDonePool, hash)
|
||||
}
|
||||
// Delete the results from the slice and let them be garbage collected
|
||||
// without this slice trick the results would stay in memory until nil
|
||||
// would be assigned to them.
|
||||
copy(q.resultCache, q.resultCache[len(results):])
|
||||
for k, n := len(q.resultCache)-len(results), len(q.resultCache); k < n; k++ {
|
||||
q.resultCache[k] = nil
|
||||
}
|
||||
q.resultOffset += uint64(len(results))
|
||||
|
||||
return results
|
||||
return len(q.resultCache)
|
||||
}
|
||||
|
||||
// ReserveBlocks reserves a set of block hashes for the given peer, skipping any
|
||||
|
@ -501,7 +499,7 @@ func (q *queue) reserveHashes(p *peer, count int, taskQueue *prque.Prque, taskGe
|
|||
|
||||
for proc := 0; (allowance == 0 || proc < allowance) && len(send) < count && !taskQueue.Empty(); proc++ {
|
||||
hash, priority := taskQueue.Pop()
|
||||
if p.ignored.Has(hash) {
|
||||
if p.Lacks(hash.(common.Hash)) {
|
||||
skip[hash.(common.Hash)] = int(priority)
|
||||
} else {
|
||||
send[hash.(common.Hash)] = int(priority)
|
||||
|
@ -584,6 +582,7 @@ func (q *queue) reserveHeaders(p *peer, count int, taskPool map[common.Hash]*typ
|
|||
// If we're the first to request this task, initialise the result container
|
||||
index := int(header.Number.Int64() - int64(q.resultOffset))
|
||||
if index >= len(q.resultCache) || index < 0 {
|
||||
common.Report("index allocation went beyond available resultCache space")
|
||||
return nil, false, errInvalidChain
|
||||
}
|
||||
if q.resultCache[index] == nil {
|
||||
|
@ -607,7 +606,7 @@ func (q *queue) reserveHeaders(p *peer, count int, taskPool map[common.Hash]*typ
|
|||
continue
|
||||
}
|
||||
// Otherwise unless the peer is known not to have the data, add to the retrieve list
|
||||
if p.ignored.Has(header.Hash()) {
|
||||
if p.Lacks(header.Hash()) {
|
||||
skip = append(skip, header)
|
||||
} else {
|
||||
send = append(send, header)
|
||||
|
@ -617,6 +616,10 @@ func (q *queue) reserveHeaders(p *peer, count int, taskPool map[common.Hash]*typ
|
|||
for _, header := range skip {
|
||||
taskQueue.Push(header, -float32(header.Number.Uint64()))
|
||||
}
|
||||
if progress {
|
||||
// Wake WaitResults, resultCache was modified
|
||||
q.active.Signal()
|
||||
}
|
||||
// Assemble and return the block download request
|
||||
if len(send) == 0 {
|
||||
return nil, progress, nil
|
||||
|
@ -700,7 +703,7 @@ func (q *queue) Revoke(peerId string) {
|
|||
|
||||
// ExpireBlocks checks for in flight requests that exceeded a timeout allowance,
|
||||
// canceling them and returning the responsible peers for penalisation.
|
||||
func (q *queue) ExpireBlocks(timeout time.Duration) []string {
|
||||
func (q *queue) ExpireBlocks(timeout time.Duration) map[string]int {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
|
||||
|
@ -709,7 +712,7 @@ func (q *queue) ExpireBlocks(timeout time.Duration) []string {
|
|||
|
||||
// ExpireBodies checks for in flight block body requests that exceeded a timeout
|
||||
// allowance, canceling them and returning the responsible peers for penalisation.
|
||||
func (q *queue) ExpireBodies(timeout time.Duration) []string {
|
||||
func (q *queue) ExpireBodies(timeout time.Duration) map[string]int {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
|
||||
|
@ -718,7 +721,7 @@ func (q *queue) ExpireBodies(timeout time.Duration) []string {
|
|||
|
||||
// ExpireReceipts checks for in flight receipt requests that exceeded a timeout
|
||||
// allowance, canceling them and returning the responsible peers for penalisation.
|
||||
func (q *queue) ExpireReceipts(timeout time.Duration) []string {
|
||||
func (q *queue) ExpireReceipts(timeout time.Duration) map[string]int {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
|
||||
|
@ -727,7 +730,7 @@ func (q *queue) ExpireReceipts(timeout time.Duration) []string {
|
|||
|
||||
// ExpireNodeData checks for in flight node data requests that exceeded a timeout
|
||||
// allowance, canceling them and returning the responsible peers for penalisation.
|
||||
func (q *queue) ExpireNodeData(timeout time.Duration) []string {
|
||||
func (q *queue) ExpireNodeData(timeout time.Duration) map[string]int {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
|
||||
|
@ -737,12 +740,12 @@ func (q *queue) ExpireNodeData(timeout time.Duration) []string {
|
|||
// expire is the generic check that move expired tasks from a pending pool back
|
||||
// into a task pool, returning all entities caught with expired tasks.
|
||||
//
|
||||
// Note, this method expects the queue lock to be already held for writing. The
|
||||
// Note, this method expects the queue lock to be already held. The
|
||||
// reason the lock is not obtained in here is because the parameters already need
|
||||
// to access the queue, so they already need a lock anyway.
|
||||
func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest, taskQueue *prque.Prque, timeoutMeter metrics.Meter) []string {
|
||||
func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest, taskQueue *prque.Prque, timeoutMeter metrics.Meter) map[string]int {
|
||||
// Iterate over the expired requests and return each to the queue
|
||||
peers := []string{}
|
||||
expiries := make(map[string]int)
|
||||
for id, request := range pendPool {
|
||||
if time.Since(request.Time) > timeout {
|
||||
// Update the metrics with the timeout
|
||||
|
@ -755,25 +758,32 @@ func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest,
|
|||
for _, header := range request.Headers {
|
||||
taskQueue.Push(header, -float32(header.Number.Uint64()))
|
||||
}
|
||||
peers = append(peers, id)
|
||||
// Add the peer to the expiry report along the the number of failed requests
|
||||
expirations := len(request.Hashes)
|
||||
if expirations < len(request.Headers) {
|
||||
expirations = len(request.Headers)
|
||||
}
|
||||
expiries[id] = expirations
|
||||
}
|
||||
}
|
||||
// Remove the expired requests from the pending pool
|
||||
for _, id := range peers {
|
||||
for id, _ := range expiries {
|
||||
delete(pendPool, id)
|
||||
}
|
||||
return peers
|
||||
return expiries
|
||||
}
|
||||
|
||||
// DeliverBlocks injects a block retrieval response into the download queue.
|
||||
func (q *queue) DeliverBlocks(id string, blocks []*types.Block) error {
|
||||
// DeliverBlocks injects a block retrieval response into the download queue. The
|
||||
// method returns the number of blocks accepted from the delivery and also wakes
|
||||
// any threads waiting for data delivery.
|
||||
func (q *queue) DeliverBlocks(id string, blocks []*types.Block) (int, error) {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
|
||||
// Short circuit if the blocks were never requested
|
||||
request := q.blockPendPool[id]
|
||||
if request == nil {
|
||||
return errNoFetchesPending
|
||||
return 0, errNoFetchesPending
|
||||
}
|
||||
blockReqTimer.UpdateSince(request.Time)
|
||||
delete(q.blockPendPool, id)
|
||||
|
@ -781,11 +791,11 @@ func (q *queue) DeliverBlocks(id string, blocks []*types.Block) error {
|
|||
// If no blocks were retrieved, mark them as unavailable for the origin peer
|
||||
if len(blocks) == 0 {
|
||||
for hash, _ := range request.Hashes {
|
||||
request.Peer.ignored.Add(hash)
|
||||
request.Peer.MarkLacking(hash)
|
||||
}
|
||||
}
|
||||
// Iterate over the downloaded blocks and add each of them
|
||||
errs := make([]error, 0)
|
||||
accepted, errs := 0, make([]error, 0)
|
||||
for _, block := range blocks {
|
||||
// Skip any blocks that were not requested
|
||||
hash := block.Hash()
|
||||
|
@ -808,29 +818,33 @@ func (q *queue) DeliverBlocks(id string, blocks []*types.Block) error {
|
|||
|
||||
delete(request.Hashes, hash)
|
||||
delete(q.hashPool, hash)
|
||||
accepted++
|
||||
}
|
||||
// Return all failed or missing fetches to the queue
|
||||
for hash, index := range request.Hashes {
|
||||
q.hashQueue.Push(hash, float32(index))
|
||||
}
|
||||
// Wake up WaitResults
|
||||
if accepted > 0 {
|
||||
q.active.Signal()
|
||||
}
|
||||
// If none of the blocks were good, it's a stale delivery
|
||||
switch {
|
||||
case len(errs) == 0:
|
||||
return nil
|
||||
|
||||
return accepted, nil
|
||||
case len(errs) == 1 && (errs[0] == errInvalidChain || errs[0] == errInvalidBlock):
|
||||
return errs[0]
|
||||
|
||||
return accepted, errs[0]
|
||||
case len(errs) == len(blocks):
|
||||
return errStaleDelivery
|
||||
|
||||
return accepted, errStaleDelivery
|
||||
default:
|
||||
return fmt.Errorf("multiple failures: %v", errs)
|
||||
return accepted, fmt.Errorf("multiple failures: %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
// DeliverBodies injects a block body retrieval response into the results queue.
|
||||
func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, uncleLists [][]*types.Header) error {
|
||||
// The method returns the number of blocks bodies accepted from the delivery and
|
||||
// also wakes any threads waiting for data delivery.
|
||||
func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, uncleLists [][]*types.Header) (int, error) {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
|
||||
|
@ -846,7 +860,9 @@ func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, uncleLi
|
|||
}
|
||||
|
||||
// DeliverReceipts injects a receipt retrieval response into the results queue.
|
||||
func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) error {
|
||||
// The method returns the number of transaction receipts accepted from the delivery
|
||||
// and also wakes any threads waiting for data delivery.
|
||||
func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) (int, error) {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
|
||||
|
@ -865,26 +881,29 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) error
|
|||
// Note, this method expects the queue lock to be already held for writing. The
|
||||
// reason the lock is not obtained in here is because the parameters already need
|
||||
// to access the queue, so they already need a lock anyway.
|
||||
func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque, pendPool map[string]*fetchRequest,
|
||||
donePool map[common.Hash]struct{}, reqTimer metrics.Timer, results int, reconstruct func(header *types.Header, index int, result *fetchResult) error) error {
|
||||
func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque,
|
||||
pendPool map[string]*fetchRequest, donePool map[common.Hash]struct{}, reqTimer metrics.Timer,
|
||||
results int, reconstruct func(header *types.Header, index int, result *fetchResult) error) (int, error) {
|
||||
|
||||
// Short circuit if the data was never requested
|
||||
request := pendPool[id]
|
||||
if request == nil {
|
||||
return errNoFetchesPending
|
||||
return 0, errNoFetchesPending
|
||||
}
|
||||
reqTimer.UpdateSince(request.Time)
|
||||
delete(pendPool, id)
|
||||
|
||||
// If no data items were retrieved, mark them as unavailable for the origin peer
|
||||
if results == 0 {
|
||||
for hash, _ := range request.Headers {
|
||||
request.Peer.ignored.Add(hash)
|
||||
for _, header := range request.Headers {
|
||||
request.Peer.MarkLacking(header.Hash())
|
||||
}
|
||||
}
|
||||
// Assemble each of the results with their headers and retrieved data parts
|
||||
var (
|
||||
failure error
|
||||
useful bool
|
||||
accepted int
|
||||
failure error
|
||||
useful bool
|
||||
)
|
||||
for i, header := range request.Headers {
|
||||
// Short circuit assembly if no more fetch results are found
|
||||
|
@ -904,6 +923,7 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQ
|
|||
donePool[header.Hash()] = struct{}{}
|
||||
q.resultCache[index].Pending--
|
||||
useful = true
|
||||
accepted++
|
||||
|
||||
// Clean up a successful fetch
|
||||
request.Headers[i] = nil
|
||||
|
@ -915,28 +935,32 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQ
|
|||
taskQueue.Push(header, -float32(header.Number.Uint64()))
|
||||
}
|
||||
}
|
||||
// Wake up WaitResults
|
||||
if accepted > 0 {
|
||||
q.active.Signal()
|
||||
}
|
||||
// If none of the data was good, it's a stale delivery
|
||||
switch {
|
||||
case failure == nil || failure == errInvalidChain:
|
||||
return failure
|
||||
|
||||
return accepted, failure
|
||||
case useful:
|
||||
return fmt.Errorf("partial failure: %v", failure)
|
||||
|
||||
return accepted, fmt.Errorf("partial failure: %v", failure)
|
||||
default:
|
||||
return errStaleDelivery
|
||||
return accepted, errStaleDelivery
|
||||
}
|
||||
}
|
||||
|
||||
// DeliverNodeData injects a node state data retrieval response into the queue.
|
||||
func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(error, int)) error {
|
||||
// The method returns the number of node state entries originally requested, and
|
||||
// the number of them actually accepted from the delivery.
|
||||
func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(error, int)) (int, error) {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
|
||||
// Short circuit if the data was never requested
|
||||
request := q.statePendPool[id]
|
||||
if request == nil {
|
||||
return errNoFetchesPending
|
||||
return 0, errNoFetchesPending
|
||||
}
|
||||
stateReqTimer.UpdateSince(request.Time)
|
||||
delete(q.statePendPool, id)
|
||||
|
@ -944,14 +968,14 @@ func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(error, i
|
|||
// If no data was retrieved, mark their hashes as unavailable for the origin peer
|
||||
if len(data) == 0 {
|
||||
for hash, _ := range request.Hashes {
|
||||
request.Peer.ignored.Add(hash)
|
||||
request.Peer.MarkLacking(hash)
|
||||
}
|
||||
}
|
||||
// Iterate over the downloaded data and verify each of them
|
||||
errs := make([]error, 0)
|
||||
accepted, errs := 0, make([]error, 0)
|
||||
process := []trie.SyncResult{}
|
||||
for _, blob := range data {
|
||||
// Skip any blocks that were not requested
|
||||
// Skip any state trie entires that were not requested
|
||||
hash := common.BytesToHash(crypto.Sha3(blob))
|
||||
if _, ok := request.Hashes[hash]; !ok {
|
||||
errs = append(errs, fmt.Errorf("non-requested state data %x", hash))
|
||||
|
@ -959,6 +983,7 @@ func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(error, i
|
|||
}
|
||||
// Inject the next state trie item into the processing queue
|
||||
process = append(process, trie.SyncResult{hash, blob})
|
||||
accepted++
|
||||
|
||||
delete(request.Hashes, hash)
|
||||
delete(q.stateTaskPool, hash)
|
||||
|
@ -976,19 +1001,21 @@ func (q *queue) DeliverNodeData(id string, data [][]byte, callback func(error, i
|
|||
// If none of the data items were good, it's a stale delivery
|
||||
switch {
|
||||
case len(errs) == 0:
|
||||
return nil
|
||||
|
||||
return accepted, nil
|
||||
case len(errs) == len(request.Hashes):
|
||||
return errStaleDelivery
|
||||
|
||||
return accepted, errStaleDelivery
|
||||
default:
|
||||
return fmt.Errorf("multiple failures: %v", errs)
|
||||
return accepted, fmt.Errorf("multiple failures: %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
// deliverNodeData is the asynchronous node data processor that injects a batch
|
||||
// of sync results into the state scheduler.
|
||||
func (q *queue) deliverNodeData(results []trie.SyncResult, callback func(error, int)) {
|
||||
// Wake up WaitResults after the state has been written because it
|
||||
// might be waiting for the pivot block state to get completed.
|
||||
defer q.active.Signal()
|
||||
|
||||
// Process results one by one to permit task fetches in between
|
||||
for i, result := range results {
|
||||
q.stateSchedLock.Lock()
|
||||
|
|
|
@ -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)
|
||||
|
|
49
eth/peer.go
49
eth/peer.go
|
@ -44,38 +44,51 @@ const (
|
|||
handshakeTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
type peer struct {
|
||||
*p2p.Peer
|
||||
// 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 {
|
||||
id string
|
||||
|
||||
*p2p.Peer
|
||||
rw p2p.MsgReadWriter
|
||||
|
||||
version int // Protocol version negotiated
|
||||
network int // Network ID being on
|
||||
|
||||
id string
|
||||
|
||||
head common.Hash
|
||||
td *big.Int
|
||||
lock sync.RWMutex
|
||||
head common.Hash
|
||||
td *big.Int
|
||||
lock sync.RWMutex
|
||||
|
||||
knownTxs *set.Set // Set of transaction hashes known to be known by this peer
|
||||
knownBlocks *set.Set // Set of block hashes known to be known by this peer
|
||||
}
|
||||
|
||||
func newPeer(version, network int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
||||
func newPeer(version int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
|
||||
id := p.ID()
|
||||
|
||||
return &peer{
|
||||
Peer: p,
|
||||
rw: rw,
|
||||
version: version,
|
||||
network: network,
|
||||
id: fmt.Sprintf("%x", id[:8]),
|
||||
knownTxs: set.New(),
|
||||
knownBlocks: set.New(),
|
||||
}
|
||||
}
|
||||
|
||||
// Info gathers and returns a collection of metadata known about a peer.
|
||||
func (p *peer) Info() *PeerInfo {
|
||||
return &PeerInfo{
|
||||
Version: p.version,
|
||||
Difficulty: p.Td(),
|
||||
Head: fmt.Sprintf("%x", p.Head()),
|
||||
}
|
||||
}
|
||||
|
||||
// Head retrieves a copy of the current head (most recent) hash of the peer.
|
||||
func (p *peer) Head() (hash common.Hash) {
|
||||
p.lock.RLock()
|
||||
|
@ -268,20 +281,22 @@ func (p *peer) RequestReceipts(hashes []common.Hash) error {
|
|||
|
||||
// Handshake executes the eth protocol handshake, negotiating version number,
|
||||
// network IDs, difficulties, head and genesis blocks.
|
||||
func (p *peer) Handshake(td *big.Int, head common.Hash, genesis common.Hash) error {
|
||||
func (p *peer) Handshake(network int, td *big.Int, head common.Hash, genesis common.Hash) error {
|
||||
// Send out own handshake in a new thread
|
||||
errc := make(chan error, 2)
|
||||
var status statusData // safe to read after two values have been received from errc
|
||||
|
||||
go func() {
|
||||
errc <- p2p.Send(p.rw, StatusMsg, &statusData{
|
||||
ProtocolVersion: uint32(p.version),
|
||||
NetworkId: uint32(p.network),
|
||||
NetworkId: uint32(network),
|
||||
TD: td,
|
||||
CurrentBlock: head,
|
||||
GenesisBlock: genesis,
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
errc <- p.readStatus(&status, genesis)
|
||||
errc <- p.readStatus(network, &status, genesis)
|
||||
}()
|
||||
timeout := time.NewTimer(handshakeTimeout)
|
||||
defer timeout.Stop()
|
||||
|
@ -299,7 +314,7 @@ func (p *peer) Handshake(td *big.Int, head common.Hash, genesis common.Hash) err
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *peer) readStatus(status *statusData, genesis common.Hash) (err error) {
|
||||
func (p *peer) readStatus(network int, status *statusData, genesis common.Hash) (err error) {
|
||||
msg, err := p.rw.ReadMsg()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -317,8 +332,8 @@ func (p *peer) readStatus(status *statusData, genesis common.Hash) (err error) {
|
|||
if status.GenesisBlock != genesis {
|
||||
return errResp(ErrGenesisBlockMismatch, "%x (!= %x)", status.GenesisBlock, genesis)
|
||||
}
|
||||
if int(status.NetworkId) != p.network {
|
||||
return errResp(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, p.network)
|
||||
if int(status.NetworkId) != network {
|
||||
return errResp(ErrNetworkIdMismatch, "%d (!= %d)", status.NetworkId, network)
|
||||
}
|
||||
if int(status.ProtocolVersion) != p.version {
|
||||
return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", status.ProtocolVersion, p.version)
|
||||
|
|
|
@ -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
|
||||
|
|
46
p2p/peer.go
46
p2p/peer.go
|
@ -359,3 +359,49 @@ func (rw *protoRW) ReadMsg() (Msg, error) {
|
|||
return Msg{}, io.EOF
|
||||
}
|
||||
}
|
||||
|
||||
// PeerInfo represents a short summary of the information known about a connected
|
||||
// peer. Sub-protocol independent fields are contained and initialized here, with
|
||||
// protocol specifics delegated to all connected sub-protocols.
|
||||
type PeerInfo struct {
|
||||
ID string `json:"id"` // Unique node identifier (also the encryption key)
|
||||
Name string `json:"name"` // Name of the node, including client type, version, OS, custom data
|
||||
Caps []string `json:"caps"` // Sum-protocols advertised by this particular peer
|
||||
Network struct {
|
||||
LocalAddress string `json:"localAddress"` // Local endpoint of the TCP data connection
|
||||
RemoteAddress string `json:"remoteAddress"` // Remote endpoint of the TCP data connection
|
||||
} `json:"network"`
|
||||
Protocols map[string]interface{} `json:"protocols"` // Sub-protocol specific metadata fields
|
||||
}
|
||||
|
||||
// Info gathers and returns a collection of metadata known about a peer.
|
||||
func (p *Peer) Info() *PeerInfo {
|
||||
// Gather the protocol capabilities
|
||||
var caps []string
|
||||
for _, cap := range p.Caps() {
|
||||
caps = append(caps, cap.String())
|
||||
}
|
||||
// Assemble the generic peer metadata
|
||||
info := &PeerInfo{
|
||||
ID: p.ID().String(),
|
||||
Name: p.Name(),
|
||||
Caps: caps,
|
||||
Protocols: make(map[string]interface{}),
|
||||
}
|
||||
info.Network.LocalAddress = p.LocalAddr().String()
|
||||
info.Network.RemoteAddress = p.RemoteAddr().String()
|
||||
|
||||
// Gather all the running protocol infos
|
||||
for _, proto := range p.running {
|
||||
protoInfo := interface{}("unknown")
|
||||
if query := proto.Protocol.PeerInfo; query != nil {
|
||||
if metadata := query(p.ID()); metadata != nil {
|
||||
protoInfo = metadata
|
||||
} else {
|
||||
protoInfo = "handshake"
|
||||
}
|
||||
}
|
||||
info.Protocols[proto.Name] = protoInfo
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
|
|
@ -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,11 +167,30 @@ func (self *debugApi) ProcessBlock(req *shared.Request) (interface{}, error) {
|
|||
defer func() { vm.Debug = old }()
|
||||
vm.Debug = true
|
||||
|
||||
_, err := self.ethereum.BlockProcessor().RetryProcess(block)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
var (
|
||||
blockchain = self.ethereum.BlockChain()
|
||||
validator = blockchain.Validator()
|
||||
processor = blockchain.Processor()
|
||||
)
|
||||
|
||||
err := core.ValidateHeader(blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash()), true, false)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return false, err
|
||||
statedb, err := state.New(blockchain.GetBlock(block.ParentHash()).Root(), self.ethereum.ChainDb())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
receipts, _, usedGas, err := processor.Process(block, statedb)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
err = validator.ValidateState(block, blockchain.GetBlock(block.ParentHash()), statedb, receipts, usedGas)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (self *debugApi) SeedHash(req *shared.Request) (interface{}, error) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -36,11 +36,23 @@ web3._extend({
|
|||
params: 3,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter, web3._extend.utils.fromDecimal, web3._extend.utils.fromDecimal]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'getNatSpec',
|
||||
call: 'eth_getNatSpec',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
|
||||
new web3._extend.Method({
|
||||
name: 'getNatSpec',
|
||||
call: 'eth_getNatSpec',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'signTransaction',
|
||||
call: 'eth_signTransaction',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'submitTransaction',
|
||||
call: 'eth_submitTransaction',
|
||||
params: 1,
|
||||
inputFormatter: [web3._extend.formatters.inputTransactionFormatter]
|
||||
})
|
||||
],
|
||||
properties:
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
97
xeth/xeth.go
97
xeth/xeth.go
|
@ -322,44 +322,11 @@ func (self *XEth) EthBlockByHash(strHash string) *types.Block {
|
|||
return block
|
||||
}
|
||||
|
||||
func (self *XEth) EthTransactionByHash(hash string) (tx *types.Transaction, blhash common.Hash, blnum *big.Int, txi uint64) {
|
||||
// Due to increasing return params and need to determine if this is from transaction pool or
|
||||
// some chain, this probably needs to be refactored for more expressiveness
|
||||
data, _ := self.backend.ChainDb().Get(common.FromHex(hash))
|
||||
if len(data) != 0 {
|
||||
dtx := new(types.Transaction)
|
||||
if err := rlp.DecodeBytes(data, dtx); err != nil {
|
||||
glog.V(logger.Error).Infoln(err)
|
||||
return
|
||||
}
|
||||
tx = dtx
|
||||
} else { // check pending transactions
|
||||
tx = self.backend.TxPool().GetTransaction(common.HexToHash(hash))
|
||||
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
|
||||
}
|
||||
|
||||
// meta
|
||||
var txExtra struct {
|
||||
BlockHash common.Hash
|
||||
BlockIndex uint64
|
||||
Index uint64
|
||||
}
|
||||
|
||||
v, dberr := self.backend.ChainDb().Get(append(common.FromHex(hash), 0x0001))
|
||||
// TODO check specifically for ErrNotFound
|
||||
if dberr != nil {
|
||||
return
|
||||
}
|
||||
r := bytes.NewReader(v)
|
||||
err := rlp.Decode(r, &txExtra)
|
||||
if err == nil {
|
||||
blhash = txExtra.BlockHash
|
||||
blnum = big.NewInt(int64(txExtra.BlockIndex))
|
||||
txi = txExtra.Index
|
||||
} else {
|
||||
glog.V(logger.Error).Infoln(err)
|
||||
}
|
||||
|
||||
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…
Reference in New Issue