mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 14:46:42 +00:00
Merge pull request #16 from expanse-project/rebase-1.4.16
Rebase 1.4.16
This commit is contained in:
commit
8ee40d3770
865 changed files with 202040 additions and 68245 deletions
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -23,13 +23,10 @@ Godeps/_workspace/bin
|
||||||
.project
|
.project
|
||||||
.settings
|
.settings
|
||||||
|
|
||||||
deploy/osx/Mist.app
|
|
||||||
deploy/osx/Mist\ Installer.dmg
|
|
||||||
cmd/mist/assets/ext/expanse.js/
|
|
||||||
|
|
||||||
# used by the Makefile
|
# used by the Makefile
|
||||||
/build/_workspace/
|
/build/_workspace/
|
||||||
/build/bin/
|
/build/bin/
|
||||||
|
/geth*.zip
|
||||||
|
|
||||||
# travis
|
# travis
|
||||||
profile.tmp
|
profile.tmp
|
||||||
|
|
|
||||||
6
Godeps/Godeps.json
generated
6
Godeps/Godeps.json
generated
|
|
@ -20,8 +20,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/expanse-org/ethash",
|
"ImportPath": "github.com/expanse-org/ethash",
|
||||||
"Comment": "v23.1-245-g25b32de",
|
"Comment": "v23.1-247-g2e80de5",
|
||||||
"Rev": "25b32de0c0271065c28c3719c2bfe86959d72f0c"
|
"Rev": "2e80de5022370cfe632195b1720db52d07ff8a77"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/fatih/color",
|
"ImportPath": "github.com/fatih/color",
|
||||||
|
|
@ -117,7 +117,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/rjeczalik/notify",
|
"ImportPath": "github.com/rjeczalik/notify",
|
||||||
"Rev": "5dd6205716539662f8f14ab513552b41eab69d5d"
|
"Rev": "f627deca7a510d96f0ef9388f2d0e8b16d21f87f"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "github.com/robertkrimen/otto",
|
"ImportPath": "github.com/robertkrimen/otto",
|
||||||
|
|
|
||||||
0
Godeps/_workspace/src/github.com/expanse-project/ethash/setup.py
generated
vendored
Normal file → Executable file
0
Godeps/_workspace/src/github.com/expanse-project/ethash/setup.py
generated
vendored
Normal file → Executable file
29
Godeps/_workspace/src/github.com/expanse-project/ethash/src/libethash/endian.h
generated
vendored
29
Godeps/_workspace/src/github.com/expanse-project/ethash/src/libethash/endian.h
generated
vendored
|
|
@ -19,7 +19,7 @@
|
||||||
# define BYTE_ORDER LITTLE_ENDIAN
|
# define BYTE_ORDER LITTLE_ENDIAN
|
||||||
#elif defined( __QNXNTO__ ) && defined( __BIGENDIAN__ )
|
#elif defined( __QNXNTO__ ) && defined( __BIGENDIAN__ )
|
||||||
# define BIG_ENDIAN 1234
|
# define BIG_ENDIAN 1234
|
||||||
# define BYTE_ORDER BIG_ENDIAN
|
# define BYTE_ORDER BIG_ENDIAN
|
||||||
#else
|
#else
|
||||||
# include <endian.h>
|
# include <endian.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -59,21 +59,20 @@
|
||||||
|
|
||||||
#define fix_endian32(dst_, src_) dst_ = ethash_swap_u32(src_)
|
#define fix_endian32(dst_, src_) dst_ = ethash_swap_u32(src_)
|
||||||
#define fix_endian32_same(val_) val_ = ethash_swap_u32(val_)
|
#define fix_endian32_same(val_) val_ = ethash_swap_u32(val_)
|
||||||
#define fix_endian64(dst_, src_) dst_ = ethash_swap_u64(src_
|
#define fix_endian64(dst_, src_) dst_ = ethash_swap_u64(src_)
|
||||||
#define fix_endian64_same(val_) val_ = ethash_swap_u64(val_)
|
#define fix_endian64_same(val_) val_ = ethash_swap_u64(val_)
|
||||||
#define fix_endian_arr32(arr_, size_) \
|
#define fix_endian_arr32(arr_, size_) \
|
||||||
do { \
|
do { \
|
||||||
for (unsigned i_ = 0; i_ < (size_), ++i_) { \
|
for (unsigned i_ = 0; i_ < (size_); ++i_) { \
|
||||||
arr_[i_] = ethash_swap_u32(arr_[i_]); \
|
arr_[i_] = ethash_swap_u32(arr_[i_]); \
|
||||||
} \
|
} \
|
||||||
while (0)
|
} while (0)
|
||||||
#define fix_endian_arr64(arr_, size_) \
|
#define fix_endian_arr64(arr_, size_) \
|
||||||
do { \
|
do { \
|
||||||
for (unsigned i_ = 0; i_ < (size_), ++i_) { \
|
for (unsigned i_ = 0; i_ < (size_); ++i_) { \
|
||||||
arr_[i_] = ethash_swap_u64(arr_[i_]); \
|
arr_[i_] = ethash_swap_u64(arr_[i_]); \
|
||||||
} \
|
} \
|
||||||
while (0) \
|
} while (0)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
# error "endian not supported"
|
# error "endian not supported"
|
||||||
#endif // BYTE_ORDER
|
#endif // BYTE_ORDER
|
||||||
|
|
|
||||||
2
Godeps/_workspace/src/github.com/expanse-project/ethash/src/libethash/internal.c
generated
vendored
2
Godeps/_workspace/src/github.com/expanse-project/ethash/src/libethash/internal.c
generated
vendored
|
|
@ -257,7 +257,7 @@ static bool ethash_hash(
|
||||||
void ethash_quick_hash(
|
void ethash_quick_hash(
|
||||||
ethash_h256_t* return_hash,
|
ethash_h256_t* return_hash,
|
||||||
ethash_h256_t const* header_hash,
|
ethash_h256_t const* header_hash,
|
||||||
uint64_t const nonce,
|
uint64_t nonce,
|
||||||
ethash_h256_t const* mix_hash
|
ethash_h256_t const* mix_hash
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
3
Godeps/_workspace/src/github.com/rjeczalik/notify/.travis.yml
generated
vendored
3
Godeps/_workspace/src/github.com/rjeczalik/notify/.travis.yml
generated
vendored
|
|
@ -21,10 +21,9 @@ env:
|
||||||
- PATH=$HOME/bin:$PATH
|
- PATH=$HOME/bin:$PATH
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- go get golang.org/x/tools/cmd/vet
|
|
||||||
- go get -t -v ./...
|
- go get -t -v ./...
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- go tool vet -all .
|
- "(go version | grep -q 1.4) || go tool vet -all ."
|
||||||
- go install $GOFLAGS ./...
|
- go install $GOFLAGS ./...
|
||||||
- go test -v -race $GOFLAGS ./...
|
- go test -v -race $GOFLAGS ./...
|
||||||
|
|
|
||||||
1
Godeps/_workspace/src/github.com/rjeczalik/notify/appveyor.yml
generated
vendored
1
Godeps/_workspace/src/github.com/rjeczalik/notify/appveyor.yml
generated
vendored
|
|
@ -11,7 +11,6 @@ environment:
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- go version
|
- go version
|
||||||
- go get golang.org/x/tools/cmd/vet
|
|
||||||
- go get -v -t ./...
|
- go get -v -t ./...
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
|
|
|
||||||
2
Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_fsevents.go
generated
vendored
2
Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_fsevents.go
generated
vendored
|
|
@ -133,7 +133,7 @@ func (w *watch) Dispatch(ev []FSEvent) {
|
||||||
ev[i].Flags, ev[i].Path, i, ev[i].ID, len(ev))
|
ev[i].Flags, ev[i].Path, i, ev[i].ID, len(ev))
|
||||||
if ev[i].Flags&failure != 0 {
|
if ev[i].Flags&failure != 0 {
|
||||||
// TODO(rjeczalik): missing error handling
|
// TODO(rjeczalik): missing error handling
|
||||||
panic("unhandled error: " + Event(ev[i].Flags).String())
|
continue
|
||||||
}
|
}
|
||||||
if !strings.HasPrefix(ev[i].Path, w.path) {
|
if !strings.HasPrefix(ev[i].Path, w.path) {
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
105
Makefile
105
Makefile
|
|
@ -2,8 +2,8 @@
|
||||||
# with Go source code. If you know what GOPATH is then you probably
|
# with Go source code. If you know what GOPATH is then you probably
|
||||||
# don't need to bother with make.
|
# don't need to bother with make.
|
||||||
|
|
||||||
.PHONY: gexp gexp-cross evm all test travis-test-with-coverage xgo clean
|
.PHONY: gexp gexp-cross evm all test clean
|
||||||
.PHONY: gexp-linux gexp-linux-386 gexp-linux-amd64
|
.PHONY: gexp-linux gexp-linux-386 gexp-linux-amd64 gexp-linux-mips64 gexp-linux-mips64le
|
||||||
.PHONY: gexp-linux-arm gexp-linux-arm-5 gexp-linux-arm-6 gexp-linux-arm-7 gexp-linux-arm64
|
.PHONY: gexp-linux-arm gexp-linux-arm-5 gexp-linux-arm-6 gexp-linux-arm-7 gexp-linux-arm64
|
||||||
.PHONY: gexp-darwin gexp-darwin-386 gexp-darwin-amd64
|
.PHONY: gexp-darwin gexp-darwin-386 gexp-darwin-amd64
|
||||||
.PHONY: gexp-windows gexp-windows-386 gexp-windows-amd64
|
.PHONY: gexp-windows gexp-windows-386 gexp-windows-amd64
|
||||||
|
|
@ -13,25 +13,41 @@ GOBIN = build/bin
|
||||||
GO ?= latest
|
GO ?= latest
|
||||||
|
|
||||||
gexp:
|
gexp:
|
||||||
build/env.sh go build -i -v $(shell build/flags.sh) -o $(GOBIN)/gexp ./cmd/gexp
|
build/env.sh go run build/ci.go install ./cmd/gexp
|
||||||
@echo "Done building."
|
@echo "Done building."
|
||||||
@echo "Run \"$(GOBIN)/gexp\" to launch gexp."
|
@echo "Run \"$(GOBIN)/gexp\" to launch gexp."
|
||||||
|
|
||||||
|
evm:
|
||||||
|
build/env.sh go run build/ci.go install ./cmd/evm
|
||||||
|
@echo "Done building."
|
||||||
|
@echo "Run \"$(GOBIN)/evm to start the evm."
|
||||||
|
|
||||||
|
all:
|
||||||
|
build/env.sh go run build/ci.go install
|
||||||
|
|
||||||
|
test: all
|
||||||
|
build/env.sh go run build/ci.go test
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -fr build/_workspace/pkg/ Godeps/_workspace/pkg $(GOBIN)/*
|
||||||
|
|
||||||
|
# Cross Compilation Targets (xgo)
|
||||||
|
|
||||||
gexp-cross: gexp-linux gexp-darwin gexp-windows gexp-android gexp-ios
|
gexp-cross: gexp-linux gexp-darwin gexp-windows gexp-android gexp-ios
|
||||||
@echo "Full cross compilation done:"
|
@echo "Full cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/gexp-*
|
@ls -ld $(GOBIN)/gexp-*
|
||||||
|
|
||||||
gexp-linux: gexp-linux-386 gexp-linux-amd64 gexp-linux-arm
|
gexp-linux: gexp-linux-386 gexp-linux-amd64 gexp-linux-arm gexp-linux-mips64 gexp-linux-mips64le
|
||||||
@echo "Linux cross compilation done:"
|
@echo "Linux cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/gexp-linux-*
|
@ls -ld $(GOBIN)/gexp-linux-*
|
||||||
|
|
||||||
gexp-linux-386: xgo
|
gexp-linux-386:
|
||||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/386 -v $(shell build/flags.sh) ./cmd/gexp
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/386 -v ./cmd/gexp
|
||||||
@echo "Linux 386 cross compilation done:"
|
@echo "Linux 386 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/gexp-linux-* | grep 386
|
@ls -ld $(GOBIN)/gexp-linux-* | grep 386
|
||||||
|
|
||||||
gexp-linux-amd64: xgo
|
gexp-linux-amd64:
|
||||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/amd64 -v $(shell build/flags.sh) ./cmd/gexp
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/amd64 -v ./cmd/gexp
|
||||||
@echo "Linux amd64 cross compilation done:"
|
@echo "Linux amd64 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/gexp-linux-* | grep amd64
|
@ls -ld $(GOBIN)/gexp-linux-* | grep amd64
|
||||||
|
|
||||||
|
|
@ -39,37 +55,47 @@ gexp-linux-arm: gexp-linux-arm-5 gexp-linux-arm-6 gexp-linux-arm-7 gexp-linux-ar
|
||||||
@echo "Linux ARM cross compilation done:"
|
@echo "Linux ARM cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/gexp-linux-* | grep arm
|
@ls -ld $(GOBIN)/gexp-linux-* | grep arm
|
||||||
|
|
||||||
gexp-linux-arm-5: xgo
|
gexp-linux-arm-5:
|
||||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-5 -v $(shell build/flags.sh) ./cmd/gexp
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-5 -v ./cmd/gexp
|
||||||
@echo "Linux ARMv5 cross compilation done:"
|
@echo "Linux ARMv5 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/gexp-linux-* | grep arm-5
|
@ls -ld $(GOBIN)/gexp-linux-* | grep arm-5
|
||||||
|
|
||||||
gexp-linux-arm-6: xgo
|
gexp-linux-arm-6:
|
||||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-6 -v $(shell build/flags.sh) ./cmd/gexp
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-6 -v ./cmd/gexp
|
||||||
@echo "Linux ARMv6 cross compilation done:"
|
@echo "Linux ARMv6 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/gexp-linux-* | grep arm-6
|
@ls -ld $(GOBIN)/gexp-linux-* | grep arm-6
|
||||||
|
|
||||||
gexp-linux-arm-7: xgo
|
gexp-linux-arm-7:
|
||||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-7 -v $(shell build/flags.sh) ./cmd/gexp
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-7 -v ./cmd/gexp
|
||||||
@echo "Linux ARMv7 cross compilation done:"
|
@echo "Linux ARMv7 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/gexp-linux-* | grep arm-7
|
@ls -ld $(GOBIN)/gexp-linux-* | grep arm-7
|
||||||
|
|
||||||
gexp-linux-arm64: xgo
|
gexp-linux-arm64:
|
||||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm64 -v $(shell build/flags.sh) ./cmd/gexp
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm64 -v ./cmd/gexp
|
||||||
@echo "Linux ARM64 cross compilation done:"
|
@echo "Linux ARM64 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/gexp-linux-* | grep arm64
|
@ls -ld $(GOBIN)/gexp-linux-* | grep arm64
|
||||||
|
|
||||||
|
gexp-linux-mips64:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64 -v ./cmd/gexp
|
||||||
|
@echo "Linux MIPS64 cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/gexp-linux-* | grep mips64
|
||||||
|
|
||||||
|
gexp-linux-mips64le:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64le -v ./cmd/gexp
|
||||||
|
@echo "Linux MIPS64le cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/gexp-linux-* | grep mips64le
|
||||||
|
|
||||||
gexp-darwin: gexp-darwin-386 gexp-darwin-amd64
|
gexp-darwin: gexp-darwin-386 gexp-darwin-amd64
|
||||||
@echo "Darwin cross compilation done:"
|
@echo "Darwin cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/gexp-darwin-*
|
@ls -ld $(GOBIN)/gexp-darwin-*
|
||||||
|
|
||||||
gexp-darwin-386: xgo
|
gexp-darwin-386:
|
||||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=darwin/386 -v $(shell build/flags.sh) ./cmd/gexp
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=darwin/386 -v ./cmd/gexp
|
||||||
@echo "Darwin 386 cross compilation done:"
|
@echo "Darwin 386 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/gexp-darwin-* | grep 386
|
@ls -ld $(GOBIN)/gexp-darwin-* | grep 386
|
||||||
|
|
||||||
gexp-darwin-amd64: xgo
|
gexp-darwin-amd64:
|
||||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=darwin/amd64 -v $(shell build/flags.sh) ./cmd/gexp
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=darwin/amd64 -v ./cmd/gexp
|
||||||
@echo "Darwin amd64 cross compilation done:"
|
@echo "Darwin amd64 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/gexp-darwin-* | grep amd64
|
@ls -ld $(GOBIN)/gexp-darwin-* | grep amd64
|
||||||
|
|
||||||
|
|
@ -77,45 +103,22 @@ gexp-windows: gexp-windows-386 gexp-windows-amd64
|
||||||
@echo "Windows cross compilation done:"
|
@echo "Windows cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/gexp-windows-*
|
@ls -ld $(GOBIN)/gexp-windows-*
|
||||||
|
|
||||||
gexp-windows-386: xgo
|
gexp-windows-386:
|
||||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=windows/386 -v $(shell build/flags.sh) ./cmd/gexp
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=windows/386 -v ./cmd/gexp
|
||||||
@echo "Windows 386 cross compilation done:"
|
@echo "Windows 386 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/gexp-windows-* | grep 386
|
@ls -ld $(GOBIN)/gexp-windows-* | grep 386
|
||||||
|
|
||||||
gexp-windows-amd64: xgo
|
gexp-windows-amd64:
|
||||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=windows/amd64 -v $(shell build/flags.sh) ./cmd/gexp
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=windows/amd64 -v ./cmd/gexp
|
||||||
@echo "Windows amd64 cross compilation done:"
|
@echo "Windows amd64 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/gexp-windows-* | grep amd64
|
@ls -ld $(GOBIN)/gexp-windows-* | grep amd64
|
||||||
|
|
||||||
gexp-android: xgo
|
gexp-android:
|
||||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=android-21/aar -v $(shell build/flags.sh) ./cmd/gexp
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=android-21/aar -v ./cmd/gexp
|
||||||
@echo "Android cross compilation done:"
|
@echo "Android cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/gexp-android-*
|
@ls -ld $(GOBIN)/gexp-android-*
|
||||||
|
|
||||||
gexp-ios: xgo
|
gexp-ios:
|
||||||
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=ios-7.0/framework -v $(shell build/flags.sh) ./cmd/gexp
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=ios-7.0/framework -v ./cmd/gexp
|
||||||
@echo "iOS framework cross compilation done:"
|
@echo "iOS framework cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/gexp-ios-*
|
@ls -ld $(GOBIN)/gexp-ios-*
|
||||||
|
|
||||||
evm:
|
|
||||||
build/env.sh $(GOROOT)/bin/go install -v $(shell build/flags.sh) ./cmd/evm
|
|
||||||
@echo "Done building."
|
|
||||||
@echo "Run \"$(GOBIN)/evm to start the evm."
|
|
||||||
|
|
||||||
all:
|
|
||||||
for cmd in `ls ./cmd/`; do \
|
|
||||||
build/env.sh go build -i -v $(shell build/flags.sh) -o $(GOBIN)/$$cmd ./cmd/$$cmd; \
|
|
||||||
done
|
|
||||||
|
|
||||||
test: all
|
|
||||||
build/env.sh go test ./...
|
|
||||||
|
|
||||||
travis-test-with-coverage: all
|
|
||||||
build/env.sh go vet ./...
|
|
||||||
build/env.sh build/test-global-coverage.sh
|
|
||||||
|
|
||||||
xgo:
|
|
||||||
build/env.sh go get github.com/karalabe/xgo
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -fr build/_workspace/pkg/ Godeps/_workspace/pkg $(GOBIN)/*
|
|
||||||
|
|
|
||||||
193
README.md
193
README.md
|
|
@ -53,13 +53,204 @@ Go Expanse comes with several wrappers/executables found in
|
||||||
* `disasm` disassembles EVM code: `echo "6001" | disasm`
|
* `disasm` disassembles EVM code: `echo "6001" | disasm`
|
||||||
* `rlpdump` prints RLP structures
|
* `rlpdump` prints RLP structures
|
||||||
|
|
||||||
|
## Running geth
|
||||||
|
|
||||||
|
Going through all the possible command line flags is out of scope here (please consult our
|
||||||
|
[CLI Wiki page](https://github.com/expanse-project/go-expanse/wiki/Command-Line-Options)), but we've
|
||||||
|
enumerated a few common parameter combos to get you up to speed quickly on how you can run your
|
||||||
|
own instance.
|
||||||
|
|
||||||
|
### Full node on the main Ethereum network
|
||||||
|
|
||||||
|
By far the most common scenario is people wanting to simply interact with the Ethereum network:
|
||||||
|
create accounts; transfer funds; deploy and interact with contracts. For this particular use-case
|
||||||
|
the user doesn't care about years-old historical data, so we can fast-sync quickly to the current
|
||||||
|
state of the network. To do so:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ geth --fast --cache=512 console
|
||||||
|
```
|
||||||
|
|
||||||
|
This command will:
|
||||||
|
|
||||||
|
* Start geth in fast sync mode (`--fast`), causing it to download more data in exchange for avoiding
|
||||||
|
processing the entire history of the Ethereum network, which is very CPU intensive.
|
||||||
|
* Bump the memory allowance of the database to 512MB (`--cache=512`), which can help significantly in
|
||||||
|
sync times especially for HDD users. This flag is optional and you can set it as high or as low as
|
||||||
|
you'd like, though we'd recommend the 512MB - 2GB range.
|
||||||
|
* Start up 's built-in interactive [JavaScript console](https://github.com/expanse-project/go-expanse/wiki/JavaScript-Console),
|
||||||
|
(via the trailing `console` subcommand) through which you can invoke all official [`web3` methods](https://github.com/ethereum/wiki/wiki/JavaScript-API)
|
||||||
|
as well as 's own [management APIs](https://github.com/expanse-project/go-expanse/wiki/Management-APIs).
|
||||||
|
This too is optional and if you leave it out you can always attach to an already running instance
|
||||||
|
with `geth --attach`.
|
||||||
|
|
||||||
|
### Full node on the Ethereum test network
|
||||||
|
|
||||||
|
Transitioning towards developers, if you'd like to play around with creating Ethereum contracts, you
|
||||||
|
almost certainly would like to do that without any real money involved until you get the hang of the
|
||||||
|
entire system. In other words, instead of attaching to the main network, you want to join the **test**
|
||||||
|
network with your node, which is fully equivalent to the main network, but with play-Ether only.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ geth --testnet --fast --cache=512 console
|
||||||
|
```
|
||||||
|
|
||||||
|
The `--fast`, `--cache` flags and `console` subcommand have the exact same meaning as above and they
|
||||||
|
are equially useful on the testnet too. Please see above for their explanations if you've skipped to
|
||||||
|
here.
|
||||||
|
|
||||||
|
Specifying the `--testnet` flag however will reconfigure your instance a bit:
|
||||||
|
|
||||||
|
* Instead of using the default data directory (`~/.ethereum` on Linux for example), will nest
|
||||||
|
itself one level deeper into a `testnet` subfolder (`~/.ethereum/testnet` on Linux).
|
||||||
|
* Instead of connecting the main Ethereum network, the client will connect to the test network,
|
||||||
|
which uses different P2P bootnodes, different network IDs and genesis states.
|
||||||
|
|
||||||
|
*Note: Although there are some internal protective measures to prevent transactions from crossing
|
||||||
|
over between the main network and test network (different starting nonces), you should make sure to
|
||||||
|
always use separate accounts for play-money and real-money. Unless you manually move accounts,
|
||||||
|
will by default correctly separate the two networks and will not make any accounts available between
|
||||||
|
them.*
|
||||||
|
|
||||||
|
### Programatically interfacing nodes
|
||||||
|
|
||||||
|
As a developer, sooner rather than later you'll want to start interacting with and the Ethereum
|
||||||
|
network via your own programs and not manually through the console. To aid this, has built in
|
||||||
|
support for a JSON-RPC based APIs ([standard APIs](https://github.com/ethereum/wiki/wiki/JSON-RPC) and
|
||||||
|
[ specific APIs](https://github.com/expanse-project/go-expanse/wiki/Management-APIs)). These can be
|
||||||
|
exposed via HTTP, WebSockets and IPC (unix sockets on unix based platroms, and named pipes on Windows).
|
||||||
|
|
||||||
|
The IPC interface is enabled by default and exposes all the APIs supported by , whereas the HTTP
|
||||||
|
and WS interfaces need to manually be enabled and only expose a subset of APIs due to security reasons.
|
||||||
|
These can be turned on/off and configured as you'd expect.
|
||||||
|
|
||||||
|
HTTP based JSON-RPC API options:
|
||||||
|
|
||||||
|
* `--rpc` Enable the HTTP-RPC server
|
||||||
|
* `--rpcaddr` HTTP-RPC server listening interface (default: "localhost")
|
||||||
|
* `--rpcport` HTTP-RPC server listening port (default: 8545)
|
||||||
|
* `--rpcapi` API's offered over the HTTP-RPC interface (default: "eth,net,web3")
|
||||||
|
* `--rpccorsdomain` Comma separated list of domains from which to accept cross origin requests (browser enforced)
|
||||||
|
* `--ws` Enable the WS-RPC server
|
||||||
|
* `--wsaddr` WS-RPC server listening interface (default: "localhost")
|
||||||
|
* `--wsport` WS-RPC server listening port (default: 8546)
|
||||||
|
* `--wsapi` API's offered over the WS-RPC interface (default: "eth,net,web3")
|
||||||
|
* `--wsorigins` Origins from which to accept websockets requests
|
||||||
|
* `--ipcdisable` Disable the IPC-RPC server
|
||||||
|
* `--ipcapi` API's offered over the IPC-RPC interface (default: "admin,debug,eth,miner,net,personal,shh,txpool,web3")
|
||||||
|
* `--ipcpath` Filename for IPC socket/pipe within the datadir (explicit paths escape it)
|
||||||
|
|
||||||
|
You'll need to use your own programming environments' capabilities (libraries, tools, etc) to connect
|
||||||
|
via HTTP, WS or IPC to a node configured with the above flags and you'll need to speak [JSON-RPC](http://www.jsonrpc.org/specification)
|
||||||
|
on all transports. You can reuse the same connection for multiple requests!
|
||||||
|
|
||||||
|
**Note: Please understand the security implications of opening up an HTTP/WS based transport before
|
||||||
|
doing so! Hackers on the internet are actively trying to subvert Ethereum nodes with exposed APIs!
|
||||||
|
Further, all browser tabs can access locally running webservers, so malicious webpages could try to
|
||||||
|
subvert locally available APIs!**
|
||||||
|
|
||||||
|
### Operating a private network
|
||||||
|
|
||||||
|
Maintaining your own private network is more involved as a lot of configurations taken for granted in
|
||||||
|
the official networks need to be manually set up.
|
||||||
|
|
||||||
|
#### Defining the private genesis state
|
||||||
|
|
||||||
|
First, you'll need to create the genesis state of your networks, which all nodes need to be aware of
|
||||||
|
and agree upon. This consists of a small JSON file (e.g. call it `genesis.json`):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"alloc" : {},
|
||||||
|
"coinbase" : "0x0000000000000000000000000000000000000000",
|
||||||
|
"difficulty" : "0x20000",
|
||||||
|
"extraData" : "",
|
||||||
|
"gasLimit" : "0x2fefd8",
|
||||||
|
"nonce" : "0x0000000000000042",
|
||||||
|
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"timestamp" : "0x00"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The above fields should be fine for most purposes, although we'd recommend changing the `nonce` to
|
||||||
|
some random value so you prevent unknown remote nodes from being able to connect to you. If you'd
|
||||||
|
like to pre-fund some accounts for easier testing, you can populate the `alloc` field with account
|
||||||
|
configs:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"alloc": {
|
||||||
|
"0x0000000000000000000000000000000000000001": {"balance": "111111111"},
|
||||||
|
"0x0000000000000000000000000000000000000002": {"balance": "222222222"}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
With the genesis state defined in the above JSON file, you'll need to initialize **every** node
|
||||||
|
with it prior to starting it up to ensure all blockchain parameters are correctly set:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ geth init path/to/genesis.json
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Creating the rendezvous point
|
||||||
|
|
||||||
|
With all nodes that you want to run initialized to the desired genesis state, you'll need to start a
|
||||||
|
bootstrap node that others can use to find each other in your network and/or over the internet. The
|
||||||
|
clean way is to configure and run a dedicated bootnode:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ bootnode --genkey=boot.key
|
||||||
|
$ bootnode --nodekey=boot.key
|
||||||
|
```
|
||||||
|
|
||||||
|
With the bootnode online, it will display an [`enode` URL](https://github.com/ethereum/wiki/wiki/enode-url-format)
|
||||||
|
that other nodes can use to connect to it and exchange peer information. Make sure to replace the
|
||||||
|
displayed IP address information (most probably `[::]`) with your externally accessible IP to get the
|
||||||
|
actual `enode` URL.
|
||||||
|
|
||||||
|
*Note: You could also use a full fledged node as a bootnode, but it's the less recommended way.*
|
||||||
|
|
||||||
|
#### Starting up your member nodes
|
||||||
|
|
||||||
|
With the bootnode operational and externally reachable (you can try `telnet <ip> <port>` to ensure
|
||||||
|
it's indeed reachable), start every subsequent node pointed to the bootnode for peer discovery
|
||||||
|
via the `--bootnodes` flag. It will probably also be desirable to keep the data directory of your
|
||||||
|
private network separated, so do also specify a custom `--datadir` flag.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ geth --datadir=path/to/custom/data/folder --bootnodes=<bootnode-enode-url-from-above>
|
||||||
|
```
|
||||||
|
|
||||||
|
*Note: Since your network will be completely cut off from the main and test networks, you'll also
|
||||||
|
need to configure a miner to process transactions and create new blocks for you.*
|
||||||
|
|
||||||
|
#### Running a private miner
|
||||||
|
|
||||||
|
Mining on the public Ethereum network is a complex task as it's only feasible using GPUs, requiring
|
||||||
|
an OpenCL or CUDA enabled `ethminer` instance. For information on such a setup, please consult the
|
||||||
|
[EtherMining subreddit](https://www.reddit.com/r/EtherMining/) and the [Genoil miner](https://github.com/Genoil/cpp-ethereum)
|
||||||
|
repository.
|
||||||
|
|
||||||
|
In a private network setting however, a single CPU miner instance is more than enough for practical
|
||||||
|
purposes as it can produce a stable stream of blocks at the correct intervals without needing heavy
|
||||||
|
resources (consider running on a single thread, no need for multiple ones either). To start a
|
||||||
|
instance for mining, run it with all your usual flags, extended by:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ geth <usual-flags> --mine --minerthreads=1 --etherbase=0x0000000000000000000000000000000000000000
|
||||||
|
```
|
||||||
|
|
||||||
|
Which will start mining bocks and transactions on a single CPU thread, crediting all proceedings to
|
||||||
|
the account specified by `--etherbase`. You can further tune the mining by changing the default gas
|
||||||
|
limit blocks converge to (`--targetgaslimit`) and the price transactions are accepted at (`--gasprice`).
|
||||||
|
|
||||||
## Contribution
|
## Contribution
|
||||||
|
|
||||||
`gexp` can be configured via command line options, environment variables and config files.
|
`gexp` can be configured via command line options, environment variables and config files.
|
||||||
|
|
||||||
If you'd like to contribute to go-expanse, please fork, fix, commit and send a pull request
|
If you'd like to contribute to go-expanse, please fork, fix, commit and send a pull request
|
||||||
for the maintainers to review and merge into the main code base. If you wish to submit more
|
for the maintainers to review and merge into the main code base. If you wish to submit more
|
||||||
complex changes though, please check up with the core devs first on [our gitter channel](https://gitter.im/ethereum/go-ethereum)
|
complex changes though, please check up with the core devs first on [our gitter channel](https://gitter.im/expanse-project/go-expanse)
|
||||||
to ensure those changes are in line with the general philosophy of the project and/or get some
|
to ensure those changes are in line with the general philosophy of the project and/or get some
|
||||||
early feedback which can make both your efforts much lighter as well as our review and merge
|
early feedback which can make both your efforts much lighter as well as our review and merge
|
||||||
procedures quick and simple.
|
procedures quick and simple.
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
1.4.7
|
1.4.16
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ func (b *SimulatedBackend) Commit() {
|
||||||
|
|
||||||
// Rollback aborts all pending transactions, reverting to the last committed state.
|
// Rollback aborts all pending transactions, reverting to the last committed state.
|
||||||
func (b *SimulatedBackend) Rollback() {
|
func (b *SimulatedBackend) Rollback() {
|
||||||
blocks, _ := core.GenerateChain(b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {})
|
blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {})
|
||||||
|
|
||||||
b.pendingBlock = blocks[0]
|
b.pendingBlock = blocks[0]
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
|
||||||
|
|
@ -97,7 +97,8 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe
|
||||||
statedb *state.StateDB
|
statedb *state.StateDB
|
||||||
)
|
)
|
||||||
if pending {
|
if pending {
|
||||||
block, statedb = b.pendingBlock, b.pendingState.Copy()
|
block, statedb = b.pendingBlock, b.pendingState
|
||||||
|
defer statedb.RevertToSnapshot(statedb.Snapshot())
|
||||||
} else {
|
} else {
|
||||||
block = b.blockchain.CurrentBlock()
|
block = b.blockchain.CurrentBlock()
|
||||||
statedb, _ = b.blockchain.State()
|
statedb, _ = b.blockchain.State()
|
||||||
|
|
@ -119,6 +120,7 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe
|
||||||
value: new(big.Int),
|
value: new(big.Int),
|
||||||
data: data,
|
data: data,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute the call and return
|
// Execute the call and return
|
||||||
vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
|
vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
|
||||||
gaspool := new(core.GasPool).AddGas(common.MaxBig)
|
gaspool := new(core.GasPool).AddGas(common.MaxBig)
|
||||||
|
|
@ -146,8 +148,10 @@ func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *com
|
||||||
// Create a copy of the currently pending state db to screw around with
|
// Create a copy of the currently pending state db to screw around with
|
||||||
var (
|
var (
|
||||||
block = b.pendingBlock
|
block = b.pendingBlock
|
||||||
statedb = b.pendingState.Copy()
|
statedb = b.pendingState
|
||||||
)
|
)
|
||||||
|
defer statedb.RevertToSnapshot(statedb.Snapshot())
|
||||||
|
|
||||||
// If there's no code to interact with, respond with an appropriate error
|
// If there's no code to interact with, respond with an appropriate error
|
||||||
if contract != nil {
|
if contract != nil {
|
||||||
if code := statedb.GetCode(*contract); len(code) == 0 {
|
if code := statedb.GetCode(*contract); len(code) == 0 {
|
||||||
|
|
@ -178,7 +182,7 @@ func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *com
|
||||||
// SendTransaction implements ContractTransactor.SendTransaction, delegating the raw
|
// SendTransaction implements ContractTransactor.SendTransaction, delegating the raw
|
||||||
// transaction injection to the remote node.
|
// transaction injection to the remote node.
|
||||||
func (b *SimulatedBackend) SendTransaction(tx *types.Transaction) error {
|
func (b *SimulatedBackend) SendTransaction(tx *types.Transaction) error {
|
||||||
blocks, _ := core.GenerateChain(b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) {
|
blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||||
for _, tx := range b.pendingBlock.Transactions() {
|
for _, tx := range b.pendingBlock.Transactions() {
|
||||||
block.AddTx(tx)
|
block.AddTx(tx)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
33
appveyor.yml
Normal file
33
appveyor.yml
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
os: Visual Studio 2015
|
||||||
|
|
||||||
|
# Clone directly into GOPATH.
|
||||||
|
clone_folder: c:\gopath\src\github.com\ethereum\go-ethereum
|
||||||
|
clone_depth: 5
|
||||||
|
version: "{branch}.{build}"
|
||||||
|
environment:
|
||||||
|
global:
|
||||||
|
# Go stuff
|
||||||
|
GOPATH: c:\gopath
|
||||||
|
GO: c:\go\bin\go
|
||||||
|
GOROOT: c:\go
|
||||||
|
CC: C:\msys64\mingw64\bin\gcc.exe
|
||||||
|
# MSYS2 stuff
|
||||||
|
MSYS2_ARCH: x86_64
|
||||||
|
MSYSTEM: MINGW64
|
||||||
|
PATH: C:\msys64\mingw64\bin\;%PATH%
|
||||||
|
|
||||||
|
install:
|
||||||
|
- "%GO% version"
|
||||||
|
- "%CC% --version"
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- "%GO% run build\\ci.go install"
|
||||||
|
|
||||||
|
test_script:
|
||||||
|
- "%GO% run build\\ci.go test -vet -coverage"
|
||||||
|
|
||||||
|
after_build:
|
||||||
|
- "%GO% run build\\ci.go archive -type zip"
|
||||||
|
|
||||||
|
artifacts:
|
||||||
|
- path: geth-*.zip
|
||||||
49
build/ci-notes.md
Normal file
49
build/ci-notes.md
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
# Debian Packaging
|
||||||
|
|
||||||
|
Tagged releases and develop branch commits are available as installable Debian packages
|
||||||
|
for Ubuntu. Packages are built for the all Ubuntu versions which are supported by
|
||||||
|
Canonical:
|
||||||
|
|
||||||
|
- Trusty Tahr (14.04 LTS)
|
||||||
|
- Wily Werewolf (15.10)
|
||||||
|
- Xenial Xerus (16.04 LTS)
|
||||||
|
- Yakkety Yak (16.10)
|
||||||
|
|
||||||
|
Packages of develop branch commits have suffix -unstable and cannot be installed alongside
|
||||||
|
the stable version. Switching between release streams requires user intervention.
|
||||||
|
|
||||||
|
The packages are built and served by launchpad.net. We generate a Debian source package
|
||||||
|
for each distribution and upload it. Their builder picks up the source package, builds it
|
||||||
|
and installs the new version into the PPA repository. Launchpad requires a valid signature
|
||||||
|
by a team member for source package uploads. The signing key is stored in an environment
|
||||||
|
variable which Travis CI makes available to certain builds.
|
||||||
|
|
||||||
|
We want to build go-ethereum with the most recent version of Go, irrespective of the Go
|
||||||
|
version that is available in the main Ubuntu repository. In order to make this possible,
|
||||||
|
our PPA depends on the ~gophers/ubuntu/archive PPA. Our source package build-depends on
|
||||||
|
golang-1.7, which is co-installable alongside the regular golang package. PPA dependencies
|
||||||
|
can be edited at https://launchpad.net/%7Elp-fjl/+archive/ubuntu/geth-ci-testing/+edit-dependencies
|
||||||
|
|
||||||
|
## Building Packages Locally (for testing)
|
||||||
|
|
||||||
|
You need to run Ubuntu to do test packaging.
|
||||||
|
|
||||||
|
Add the gophers PPA and install Go 1.7 and Debian packaging tools:
|
||||||
|
|
||||||
|
$ sudo apt-add-repository ppa:gophers/ubuntu/archive
|
||||||
|
$ sudo apt-get update
|
||||||
|
$ sudo apt-get install build-essential golang-1.7 devscripts debhelper
|
||||||
|
|
||||||
|
Create the source packages:
|
||||||
|
|
||||||
|
$ go run build/ci.go debsrc -workdir dist
|
||||||
|
|
||||||
|
Then go into the source package directory for your running distribution and build the package:
|
||||||
|
|
||||||
|
$ cd dist/ethereum-unstable-1.5.0+xenial
|
||||||
|
$ dpkg-buildpackage
|
||||||
|
|
||||||
|
Built packages are placed in the dist/ directory.
|
||||||
|
|
||||||
|
$ cd ..
|
||||||
|
$ dpkg-deb -c geth-unstable_1.5.0+xenial_amd64.deb
|
||||||
483
build/ci.go
Normal file
483
build/ci.go
Normal file
|
|
@ -0,0 +1,483 @@
|
||||||
|
// Copyright 2016 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// +build none
|
||||||
|
|
||||||
|
/*
|
||||||
|
The ci command is called from Continuous Integration scripts.
|
||||||
|
|
||||||
|
Usage: go run ci.go <command> <command flags/arguments>
|
||||||
|
|
||||||
|
Available commands are:
|
||||||
|
|
||||||
|
install [ packages... ] -- builds packages and executables
|
||||||
|
test [ -coverage ] [ -vet ] [ packages... ] -- runs the tests
|
||||||
|
archive [ -type zip|tar ] -- archives build artefacts
|
||||||
|
importkeys -- imports signing keys from env
|
||||||
|
debsrc [ -sign key-id ] [ -upload dest ] -- creates a debian source package
|
||||||
|
xgo [ options ] -- cross builds according to options
|
||||||
|
|
||||||
|
For all commands, -n prevents execution of external programs (dry run mode).
|
||||||
|
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"../internal/build"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Files that end up in the geth*.zip archive.
|
||||||
|
gethArchiveFiles = []string{
|
||||||
|
"COPYING",
|
||||||
|
executablePath("geth"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Files that end up in the geth-alltools*.zip archive.
|
||||||
|
allToolsArchiveFiles = []string{
|
||||||
|
"COPYING",
|
||||||
|
executablePath("abigen"),
|
||||||
|
executablePath("evm"),
|
||||||
|
executablePath("geth"),
|
||||||
|
executablePath("rlpdump"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// A debian package is created for all executables listed here.
|
||||||
|
debExecutables = []debExecutable{
|
||||||
|
{
|
||||||
|
Name: "geth",
|
||||||
|
Description: "Ethereum CLI client.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "rlpdump",
|
||||||
|
Description: "Developer utility tool that prints RLP structures.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "evm",
|
||||||
|
Description: "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "abigen",
|
||||||
|
Description: "Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages.",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Distros for which packages are created.
|
||||||
|
// Note: vivid is unsupported because there is no golang-1.6 package for it.
|
||||||
|
debDistros = []string{"trusty", "wily", "xenial", "yakkety"}
|
||||||
|
)
|
||||||
|
|
||||||
|
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
|
||||||
|
|
||||||
|
func executablePath(name string) string {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
name += ".exe"
|
||||||
|
}
|
||||||
|
return filepath.Join(GOBIN, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.SetFlags(log.Lshortfile)
|
||||||
|
|
||||||
|
if _, err := os.Stat(filepath.Join("build", "ci.go")); os.IsNotExist(err) {
|
||||||
|
log.Fatal("this script must be run from the root of the repository")
|
||||||
|
}
|
||||||
|
if len(os.Args) < 2 {
|
||||||
|
log.Fatal("need subcommand as first argument")
|
||||||
|
}
|
||||||
|
switch os.Args[1] {
|
||||||
|
case "install":
|
||||||
|
doInstall(os.Args[2:])
|
||||||
|
case "test":
|
||||||
|
doTest(os.Args[2:])
|
||||||
|
case "archive":
|
||||||
|
doArchive(os.Args[2:])
|
||||||
|
case "debsrc":
|
||||||
|
doDebianSource(os.Args[2:])
|
||||||
|
case "xgo":
|
||||||
|
doXgo(os.Args[2:])
|
||||||
|
default:
|
||||||
|
log.Fatal("unknown command ", os.Args[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compiling
|
||||||
|
|
||||||
|
func doInstall(cmdline []string) {
|
||||||
|
flag.CommandLine.Parse(cmdline)
|
||||||
|
env := build.Env()
|
||||||
|
|
||||||
|
// Check Go version. People regularly open issues about compilation
|
||||||
|
// failure with outdated Go. This should save them the trouble.
|
||||||
|
if runtime.Version() < "go1.4" && !strings.HasPrefix(runtime.Version(), "devel") {
|
||||||
|
log.Println("You have Go version", runtime.Version())
|
||||||
|
log.Println("go-ethereum requires at least Go version 1.4 and cannot")
|
||||||
|
log.Println("be compiled with an earlier version. Please upgrade your Go installation.")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile packages given as arguments, or everything if there are no arguments.
|
||||||
|
packages := []string{"./..."}
|
||||||
|
if flag.NArg() > 0 {
|
||||||
|
packages = flag.Args()
|
||||||
|
}
|
||||||
|
|
||||||
|
goinstall := goTool("install", buildFlags(env)...)
|
||||||
|
goinstall.Args = append(goinstall.Args, "-v")
|
||||||
|
goinstall.Args = append(goinstall.Args, packages...)
|
||||||
|
build.MustRun(goinstall)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildFlags(env build.Environment) (flags []string) {
|
||||||
|
if os.Getenv("GO_OPENCL") != "" {
|
||||||
|
flags = append(flags, "-tags", "opencl")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since Go 1.5, the separator char for link time assignments
|
||||||
|
// is '=' and using ' ' prints a warning. However, Go < 1.5 does
|
||||||
|
// not support using '='.
|
||||||
|
sep := " "
|
||||||
|
if runtime.Version() > "go1.5" || strings.Contains(runtime.Version(), "devel") {
|
||||||
|
sep = "="
|
||||||
|
}
|
||||||
|
// Set gitCommit constant via link-time assignment.
|
||||||
|
if env.Commit != "" {
|
||||||
|
flags = append(flags, "-ldflags", "-X main.gitCommit"+sep+env.Commit)
|
||||||
|
}
|
||||||
|
return flags
|
||||||
|
}
|
||||||
|
|
||||||
|
func goTool(subcmd string, args ...string) *exec.Cmd {
|
||||||
|
gocmd := filepath.Join(runtime.GOROOT(), "bin", "go")
|
||||||
|
cmd := exec.Command(gocmd, subcmd)
|
||||||
|
cmd.Args = append(cmd.Args, args...)
|
||||||
|
cmd.Env = []string{
|
||||||
|
"GOPATH=" + build.GOPATH(),
|
||||||
|
"GOBIN=" + GOBIN,
|
||||||
|
}
|
||||||
|
for _, e := range os.Environ() {
|
||||||
|
if strings.HasPrefix(e, "GOPATH=") || strings.HasPrefix(e, "GOBIN=") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cmd.Env = append(cmd.Env, e)
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Running The Tests
|
||||||
|
//
|
||||||
|
// "tests" also includes static analysis tools such as vet.
|
||||||
|
|
||||||
|
func doTest(cmdline []string) {
|
||||||
|
var (
|
||||||
|
vet = flag.Bool("vet", false, "Whether to run go vet")
|
||||||
|
coverage = flag.Bool("coverage", false, "Whether to record code coverage")
|
||||||
|
)
|
||||||
|
flag.CommandLine.Parse(cmdline)
|
||||||
|
packages := []string{"./..."}
|
||||||
|
if len(flag.CommandLine.Args()) > 0 {
|
||||||
|
packages = flag.CommandLine.Args()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run analysis tools before the tests.
|
||||||
|
if *vet {
|
||||||
|
build.MustRun(goTool("vet", packages...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the actual tests.
|
||||||
|
gotest := goTool("test")
|
||||||
|
// Test a single package at a time. CI builders are slow
|
||||||
|
// and some tests run into timeouts under load.
|
||||||
|
gotest.Args = append(gotest.Args, "-p", "1")
|
||||||
|
if *coverage {
|
||||||
|
gotest.Args = append(gotest.Args, "-covermode=atomic", "-cover")
|
||||||
|
}
|
||||||
|
gotest.Args = append(gotest.Args, packages...)
|
||||||
|
build.MustRun(gotest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release Packaging
|
||||||
|
|
||||||
|
func doArchive(cmdline []string) {
|
||||||
|
var (
|
||||||
|
atype = flag.String("type", "zip", "Type of archive to write (zip|tar)")
|
||||||
|
ext string
|
||||||
|
)
|
||||||
|
flag.CommandLine.Parse(cmdline)
|
||||||
|
switch *atype {
|
||||||
|
case "zip":
|
||||||
|
ext = ".zip"
|
||||||
|
case "tar":
|
||||||
|
ext = ".tar.gz"
|
||||||
|
default:
|
||||||
|
log.Fatal("unknown archive type: ", atype)
|
||||||
|
}
|
||||||
|
|
||||||
|
env := build.Env()
|
||||||
|
maybeSkipArchive(env)
|
||||||
|
|
||||||
|
base := archiveBasename(env)
|
||||||
|
if err := build.WriteArchive("geth-"+base, ext, gethArchiveFiles); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := build.WriteArchive("geth-alltools-"+base, ext, allToolsArchiveFiles); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func archiveBasename(env build.Environment) string {
|
||||||
|
// date := time.Now().UTC().Format("200601021504")
|
||||||
|
platform := runtime.GOOS + "-" + runtime.GOARCH
|
||||||
|
archive := platform + "-" + build.VERSION()
|
||||||
|
if env.Commit != "" {
|
||||||
|
archive += "-" + env.Commit[:8]
|
||||||
|
}
|
||||||
|
return archive
|
||||||
|
}
|
||||||
|
|
||||||
|
// skips archiving for some build configurations.
|
||||||
|
func maybeSkipArchive(env build.Environment) {
|
||||||
|
if env.IsPullRequest {
|
||||||
|
log.Printf("skipping because this is a PR build")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
if env.Branch != "develop" && !strings.HasPrefix(env.Tag, "v1.") {
|
||||||
|
log.Printf("skipping because branch %q, tag %q is not on the whitelist", env.Branch, env.Tag)
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debian Packaging
|
||||||
|
|
||||||
|
func doDebianSource(cmdline []string) {
|
||||||
|
var (
|
||||||
|
signer = flag.String("signer", "", `Signing key name, also used as package author`)
|
||||||
|
upload = flag.String("upload", "", `Where to upload the source package (usually "ppa:ethereum/ethereum")`)
|
||||||
|
workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`)
|
||||||
|
now = time.Now()
|
||||||
|
)
|
||||||
|
flag.CommandLine.Parse(cmdline)
|
||||||
|
*workdir = makeWorkdir(*workdir)
|
||||||
|
env := build.Env()
|
||||||
|
maybeSkipArchive(env)
|
||||||
|
|
||||||
|
// Import the signing key.
|
||||||
|
if b64key := os.Getenv("PPA_SIGNING_KEY"); b64key != "" {
|
||||||
|
key, err := base64.StdEncoding.DecodeString(b64key)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("invalid base64 PPA_SIGNING_KEY")
|
||||||
|
}
|
||||||
|
gpg := exec.Command("gpg", "--import")
|
||||||
|
gpg.Stdin = bytes.NewReader(key)
|
||||||
|
build.MustRun(gpg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the packages.
|
||||||
|
for _, distro := range debDistros {
|
||||||
|
meta := newDebMetadata(distro, *signer, env, now)
|
||||||
|
pkgdir := stageDebianSource(*workdir, meta)
|
||||||
|
debuild := exec.Command("debuild", "-S", "-sa", "-us", "-uc")
|
||||||
|
debuild.Dir = pkgdir
|
||||||
|
build.MustRun(debuild)
|
||||||
|
|
||||||
|
changes := fmt.Sprintf("%s_%s_source.changes", meta.Name(), meta.VersionString())
|
||||||
|
changes = filepath.Join(*workdir, changes)
|
||||||
|
if *signer != "" {
|
||||||
|
build.MustRunCommand("debsign", changes)
|
||||||
|
}
|
||||||
|
if *upload != "" {
|
||||||
|
build.MustRunCommand("dput", *upload, changes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeWorkdir(wdflag string) string {
|
||||||
|
var err error
|
||||||
|
if wdflag != "" {
|
||||||
|
err = os.MkdirAll(wdflag, 0744)
|
||||||
|
} else {
|
||||||
|
wdflag, err = ioutil.TempDir("", "eth-deb-build-")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return wdflag
|
||||||
|
}
|
||||||
|
|
||||||
|
func isUnstableBuild(env build.Environment) bool {
|
||||||
|
if env.Branch != "develop" && env.Tag != "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type debMetadata struct {
|
||||||
|
Env build.Environment
|
||||||
|
|
||||||
|
// go-ethereum version being built. Note that this
|
||||||
|
// is not the debian package version. The package version
|
||||||
|
// is constructed by VersionString.
|
||||||
|
Version string
|
||||||
|
|
||||||
|
Author string // "name <email>", also selects signing key
|
||||||
|
Distro, Time string
|
||||||
|
Executables []debExecutable
|
||||||
|
}
|
||||||
|
|
||||||
|
type debExecutable struct {
|
||||||
|
Name, Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDebMetadata(distro, author string, env build.Environment, t time.Time) debMetadata {
|
||||||
|
if author == "" {
|
||||||
|
// No signing key, use default author.
|
||||||
|
author = "Ethereum Builds <fjl@ethereum.org>"
|
||||||
|
}
|
||||||
|
return debMetadata{
|
||||||
|
Env: env,
|
||||||
|
Author: author,
|
||||||
|
Distro: distro,
|
||||||
|
Version: build.VERSION(),
|
||||||
|
Time: t.Format(time.RFC1123Z),
|
||||||
|
Executables: debExecutables,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the name of the metapackage that depends
|
||||||
|
// on all executable packages.
|
||||||
|
func (meta debMetadata) Name() string {
|
||||||
|
if isUnstableBuild(meta.Env) {
|
||||||
|
return "ethereum-unstable"
|
||||||
|
}
|
||||||
|
return "ethereum"
|
||||||
|
}
|
||||||
|
|
||||||
|
// VersionString returns the debian version of the packages.
|
||||||
|
func (meta debMetadata) VersionString() string {
|
||||||
|
vsn := meta.Version
|
||||||
|
if meta.Env.Buildnum != "" {
|
||||||
|
vsn += "+build" + meta.Env.Buildnum
|
||||||
|
}
|
||||||
|
if meta.Distro != "" {
|
||||||
|
vsn += "+" + meta.Distro
|
||||||
|
}
|
||||||
|
return vsn
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExeList returns the list of all executable packages.
|
||||||
|
func (meta debMetadata) ExeList() string {
|
||||||
|
names := make([]string, len(meta.Executables))
|
||||||
|
for i, e := range meta.Executables {
|
||||||
|
names[i] = meta.ExeName(e)
|
||||||
|
}
|
||||||
|
return strings.Join(names, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExeName returns the package name of an executable package.
|
||||||
|
func (meta debMetadata) ExeName(exe debExecutable) string {
|
||||||
|
if isUnstableBuild(meta.Env) {
|
||||||
|
return exe.Name + "-unstable"
|
||||||
|
}
|
||||||
|
return exe.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExeConflicts returns the content of the Conflicts field
|
||||||
|
// for executable packages.
|
||||||
|
func (meta debMetadata) ExeConflicts(exe debExecutable) string {
|
||||||
|
if isUnstableBuild(meta.Env) {
|
||||||
|
// Set up the conflicts list so that the *-unstable packages
|
||||||
|
// cannot be installed alongside the regular version.
|
||||||
|
//
|
||||||
|
// https://www.debian.org/doc/debian-policy/ch-relationships.html
|
||||||
|
// is very explicit about Conflicts: and says that Breaks: should
|
||||||
|
// be preferred and the conflicting files should be handled via
|
||||||
|
// alternates. We might do this eventually but using a conflict is
|
||||||
|
// easier now.
|
||||||
|
return "ethereum, " + exe.Name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func stageDebianSource(tmpdir string, meta debMetadata) (pkgdir string) {
|
||||||
|
pkg := meta.Name() + "-" + meta.VersionString()
|
||||||
|
pkgdir = filepath.Join(tmpdir, pkg)
|
||||||
|
if err := os.Mkdir(pkgdir, 0755); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the source code.
|
||||||
|
build.MustRunCommand("git", "checkout-index", "-a", "--prefix", pkgdir+string(filepath.Separator))
|
||||||
|
|
||||||
|
// Put the debian build files in place.
|
||||||
|
debian := filepath.Join(pkgdir, "debian")
|
||||||
|
build.Render("build/deb.rules", filepath.Join(debian, "rules"), 0755, meta)
|
||||||
|
build.Render("build/deb.changelog", filepath.Join(debian, "changelog"), 0644, meta)
|
||||||
|
build.Render("build/deb.control", filepath.Join(debian, "control"), 0644, meta)
|
||||||
|
build.Render("build/deb.copyright", filepath.Join(debian, "copyright"), 0644, meta)
|
||||||
|
build.RenderString("8\n", filepath.Join(debian, "compat"), 0644, meta)
|
||||||
|
build.RenderString("3.0 (native)\n", filepath.Join(debian, "source/format"), 0644, meta)
|
||||||
|
for _, exe := range meta.Executables {
|
||||||
|
install := filepath.Join(debian, meta.ExeName(exe)+".install")
|
||||||
|
docs := filepath.Join(debian, meta.ExeName(exe)+".docs")
|
||||||
|
build.Render("build/deb.install", install, 0644, exe)
|
||||||
|
build.Render("build/deb.docs", docs, 0644, exe)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pkgdir
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cross compilation
|
||||||
|
|
||||||
|
func doXgo(cmdline []string) {
|
||||||
|
flag.CommandLine.Parse(cmdline)
|
||||||
|
env := build.Env()
|
||||||
|
|
||||||
|
// Make sure xgo is available for cross compilation
|
||||||
|
gogetxgo := goTool("get", "github.com/karalabe/xgo")
|
||||||
|
build.MustRun(gogetxgo)
|
||||||
|
|
||||||
|
// Execute the actual cross compilation
|
||||||
|
xgo := xgoTool(append(buildFlags(env), flag.Args()...))
|
||||||
|
build.MustRun(xgo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func xgoTool(args []string) *exec.Cmd {
|
||||||
|
cmd := exec.Command(filepath.Join(GOBIN, "xgo"), args...)
|
||||||
|
cmd.Env = []string{
|
||||||
|
"GOPATH=" + build.GOPATH(),
|
||||||
|
"GOBIN=" + GOBIN,
|
||||||
|
}
|
||||||
|
for _, e := range os.Environ() {
|
||||||
|
if strings.HasPrefix(e, "GOPATH=") || strings.HasPrefix(e, "GOBIN=") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cmd.Env = append(cmd.Env, e)
|
||||||
|
}
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
5
build/deb.changelog
Normal file
5
build/deb.changelog
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{{.Name}} ({{.VersionString}}) {{.Distro}}; urgency=low
|
||||||
|
|
||||||
|
* git build of {{.Env.Commit}}
|
||||||
|
|
||||||
|
-- {{.Author}} {{.Time}}
|
||||||
25
build/deb.control
Normal file
25
build/deb.control
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
Source: {{.Name}}
|
||||||
|
Section: science
|
||||||
|
Priority: extra
|
||||||
|
Maintainer: {{.Author}}
|
||||||
|
Build-Depends: debhelper (>= 8.0.0), golang-1.7
|
||||||
|
Standards-Version: 3.9.5
|
||||||
|
Homepage: https://ethereum.org
|
||||||
|
Vcs-Git: git://github.com/expanse-project/go-expanse.git
|
||||||
|
Vcs-Browser: https://github.com/expanse-project/go-expanse
|
||||||
|
|
||||||
|
Package: {{.Name}}
|
||||||
|
Architecture: any
|
||||||
|
Depends: ${misc:Depends}, {{.ExeList}}
|
||||||
|
Description: Meta-package to install geth and other tools
|
||||||
|
Meta-package to install geth and other tools
|
||||||
|
|
||||||
|
{{range .Executables}}
|
||||||
|
Package: {{$.ExeName .}}
|
||||||
|
Conflicts: {{$.ExeConflicts .}}
|
||||||
|
Architecture: any
|
||||||
|
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||||
|
Built-Using: ${misc:Built-Using}
|
||||||
|
Description: {{.Description}}
|
||||||
|
{{.Description}}
|
||||||
|
{{end}}
|
||||||
14
build/deb.copyright
Normal file
14
build/deb.copyright
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
Copyright 2016 The go-ethereum Authors
|
||||||
|
|
||||||
|
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/>.
|
||||||
1
build/deb.docs
Normal file
1
build/deb.docs
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
AUTHORS
|
||||||
1
build/deb.install
Normal file
1
build/deb.install
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
build/bin/{{.Name}} usr/bin
|
||||||
13
build/deb.rules
Normal file
13
build/deb.rules
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/make -f
|
||||||
|
# -*- makefile -*-
|
||||||
|
|
||||||
|
# Uncomment this to turn on verbose mode.
|
||||||
|
#export DH_VERBOSE=1
|
||||||
|
|
||||||
|
override_dh_auto_build:
|
||||||
|
build/env.sh /usr/lib/go-1.7/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}}
|
||||||
|
|
||||||
|
override_dh_auto_test:
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
|
|
@ -20,9 +20,8 @@ fi
|
||||||
|
|
||||||
# Set up the environment to use the workspace.
|
# Set up the environment to use the workspace.
|
||||||
# Also add Godeps workspace so we build using canned dependencies.
|
# Also add Godeps workspace so we build using canned dependencies.
|
||||||
GOPATH="$ethdir/go-expanse/Godeps/_workspace:$workspace"
|
GOPATH="$workspace"
|
||||||
GOBIN="$PWD/build/bin"
|
export GOPATH
|
||||||
export GOPATH GOBIN
|
|
||||||
|
|
||||||
# Run the command inside the workspace.
|
# Run the command inside the workspace.
|
||||||
cd "$ethdir/go-expanse"
|
cd "$ethdir/go-expanse"
|
||||||
|
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
if [ ! -f "build/env.sh" ]; then
|
|
||||||
echo "$0 must be run from the root of the repository."
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Since Go 1.5, the separator char for link time assignments
|
|
||||||
# is '=' and using ' ' prints a warning. However, Go < 1.5 does
|
|
||||||
# not support using '='.
|
|
||||||
sep=$(go version | awk '{ if ($3 >= "go1.5" || index($3, "devel")) print "="; else print " "; }' -)
|
|
||||||
|
|
||||||
# set gitCommit when running from a Git checkout.
|
|
||||||
if [ -f ".git/HEAD" ]; then
|
|
||||||
echo "-ldflags '-X main.gitCommit$sep$(git rev-parse HEAD)'"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -z "$GO_OPENCL" ]; then
|
|
||||||
echo "-tags opencl"
|
|
||||||
fi
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
echo "" > coverage.txt
|
|
||||||
|
|
||||||
for d in $(find ./* -maxdepth 10 -type d -not -path "./build" -not -path "./Godeps/*" ); do
|
|
||||||
if ls $d/*.go &> /dev/null; then
|
|
||||||
go test -coverprofile=profile.out -covermode=atomic $d
|
|
||||||
if [ -f profile.out ]; then
|
|
||||||
cat profile.out >> coverage.txt
|
|
||||||
echo '<<<<<< EOF' >> coverage.txt
|
|
||||||
rm profile.out
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
@ -49,7 +49,6 @@ var (
|
||||||
// don't relicense vendored sources
|
// don't relicense vendored sources
|
||||||
"crypto/sha3/", "crypto/ecies/", "logger/glog/",
|
"crypto/sha3/", "crypto/ecies/", "logger/glog/",
|
||||||
"crypto/secp256k1/curve.go",
|
"crypto/secp256k1/curve.go",
|
||||||
"trie/arc.go",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// paths with this prefix are licensed as GPL. all other files are LGPL.
|
// paths with this prefix are licensed as GPL. all other files are LGPL.
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
@echo off
|
|
||||||
if not exist .\build\win-ci-compile.bat (
|
|
||||||
echo This script must be run from the root of the repository.
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
if not defined GOPATH (
|
|
||||||
echo GOPATH is not set.
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
|
|
||||||
set GOPATH=%GOPATH%;%cd%\Godeps\_workspace
|
|
||||||
set GOBIN=%cd%\build\bin
|
|
||||||
|
|
||||||
rem set gitCommit when running from a Git checkout.
|
|
||||||
set goLinkFlags=""
|
|
||||||
if exist ".git\HEAD" (
|
|
||||||
where /q git
|
|
||||||
if not errorlevel 1 (
|
|
||||||
for /f %%h in ('git rev-parse HEAD') do (
|
|
||||||
set goLinkFlags="-X main.gitCommit=%%h"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
@echo on
|
|
||||||
go install -v -ldflags %goLinkFlags% ./...
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
@echo off
|
|
||||||
if not exist .\build\win-ci-test.bat (
|
|
||||||
echo This script must be run from the root of the repository.
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
if not defined GOPATH (
|
|
||||||
echo GOPATH is not set.
|
|
||||||
exit /b
|
|
||||||
)
|
|
||||||
|
|
||||||
set GOPATH=%GOPATH%;%cd%\Godeps\_workspace
|
|
||||||
set GOBIN=%cd%\build\bin
|
|
||||||
|
|
||||||
@echo on
|
|
||||||
go test ./...
|
|
||||||
32
circle.yml
Normal file
32
circle.yml
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
machine:
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
cache_directories:
|
||||||
|
- "~/.ethash" # Cache the ethash DAG generated by hive for consecutive builds
|
||||||
|
- "~/.docker" # Cache all docker images manually to avoid lengthy rebuilds
|
||||||
|
override:
|
||||||
|
# Restore all previously cached docker images
|
||||||
|
- mkdir -p ~/.docker
|
||||||
|
- for img in `ls ~/.docker`; do docker load -i ~/.docker/$img; done
|
||||||
|
|
||||||
|
# Pull in and hive, restore cached ethash DAGs and do a dry run
|
||||||
|
- go get -u github.com/karalabe/hive
|
||||||
|
- (cd ~/.go_workspace/src/github.com/karalabe/hive && mkdir -p workspace/ethash/ ~/.ethash)
|
||||||
|
- (cd ~/.go_workspace/src/github.com/karalabe/hive && cp -r ~/.ethash/. workspace/ethash/)
|
||||||
|
- (cd ~/.go_workspace/src/github.com/karalabe/hive && hive --docker-noshell --client=NONE --test=. --sim=. --loglevel=6)
|
||||||
|
|
||||||
|
# Cache all the docker images and the ethash DAGs
|
||||||
|
- for img in `docker images | grep -v "^<none>" | tail -n +2 | awk '{print $1}'`; do docker save $img > ~/.docker/`echo $img | tr '/' ':'`.tar; done
|
||||||
|
- cp -r ~/.go_workspace/src/github.com/karalabe/hive/workspace/ethash/. ~/.ethash
|
||||||
|
|
||||||
|
test:
|
||||||
|
override:
|
||||||
|
# Build and move into a known folder
|
||||||
|
- make geth
|
||||||
|
- cp ./build/bin/geth $HOME/geth
|
||||||
|
|
||||||
|
# Run hive and move all generated logs into the public artifacts folder
|
||||||
|
- (cd ~/.go_workspace/src/github.com/karalabe/hive && hive --docker-noshell --client=go-ethereum:local --override=$HOME/geth --test=. --sim=.)
|
||||||
|
- cp -r ~/.go_workspace/src/github.com/karalabe/hive/workspace/logs/* $CIRCLE_ARTIFACTS
|
||||||
|
|
@ -75,9 +75,9 @@ func runTestWithReader(test string, r io.Reader) error {
|
||||||
var err error
|
var err error
|
||||||
switch strings.ToLower(test) {
|
switch strings.ToLower(test) {
|
||||||
case "bk", "block", "blocktest", "blockchaintest", "blocktests", "blockchaintests":
|
case "bk", "block", "blocktest", "blockchaintest", "blocktests", "blockchaintests":
|
||||||
err = tests.RunBlockTestWithReader(params.MainNetHomesteadBlock, r, skipTests)
|
err = tests.RunBlockTestWithReader(params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, r, skipTests)
|
||||||
case "st", "state", "statetest", "statetests":
|
case "st", "state", "statetest", "statetests":
|
||||||
rs := tests.RuleSet{HomesteadBlock: params.MainNetHomesteadBlock}
|
rs := tests.RuleSet{HomesteadBlock: params.MainNetHomesteadBlock, DAOForkBlock: params.MainNetDAOForkBlock, DAOForkSupport: true}
|
||||||
err = tests.RunStateTestWithReader(rs, r, skipTests)
|
err = tests.RunStateTestWithReader(rs, r, skipTests)
|
||||||
case "tx", "transactiontest", "transactiontests":
|
case "tx", "transactiontest", "transactiontests":
|
||||||
err = tests.RunTransactionTestsWithReader(r, skipTests)
|
err = tests.RunTransactionTestsWithReader(r, skipTests)
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/expanse-project/go-expanse/core/state"
|
"github.com/expanse-project/go-expanse/core/state"
|
||||||
"github.com/expanse-project/go-expanse/core/types"
|
"github.com/expanse-project/go-expanse/core/types"
|
||||||
"github.com/expanse-project/go-expanse/core/vm"
|
"github.com/expanse-project/go-expanse/core/vm"
|
||||||
|
"github.com/expanse-project/go-expanse/crypto"
|
||||||
"github.com/expanse-project/go-expanse/ethdb"
|
"github.com/expanse-project/go-expanse/ethdb"
|
||||||
"github.com/expanse-project/go-expanse/logger/glog"
|
"github.com/expanse-project/go-expanse/logger/glog"
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
|
@ -141,7 +142,9 @@ func run(ctx *cli.Context) error {
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
|
receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
|
||||||
receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)))
|
|
||||||
|
code := common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name))
|
||||||
|
receiver.SetCode(crypto.Keccak256Hash(code), code)
|
||||||
ret, err = vmenv.Call(
|
ret, err = vmenv.Call(
|
||||||
sender,
|
sender,
|
||||||
receiver.Address(),
|
receiver.Address(),
|
||||||
|
|
@ -220,22 +223,22 @@ type ruleSet struct{}
|
||||||
|
|
||||||
func (ruleSet) IsHomestead(*big.Int) bool { return true }
|
func (ruleSet) IsHomestead(*big.Int) bool { return true }
|
||||||
|
|
||||||
func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} }
|
func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} }
|
||||||
func (self *VMEnv) Vm() vm.Vm { return self.evm }
|
func (self *VMEnv) Vm() vm.Vm { return self.evm }
|
||||||
func (self *VMEnv) Db() vm.Database { return self.state }
|
func (self *VMEnv) Db() vm.Database { return self.state }
|
||||||
func (self *VMEnv) MakeSnapshot() vm.Database { return self.state.Copy() }
|
func (self *VMEnv) SnapshotDatabase() int { return self.state.Snapshot() }
|
||||||
func (self *VMEnv) SetSnapshot(db vm.Database) { self.state.Set(db.(*state.StateDB)) }
|
func (self *VMEnv) RevertToSnapshot(snap int) { self.state.RevertToSnapshot(snap) }
|
||||||
func (self *VMEnv) Origin() common.Address { return *self.transactor }
|
func (self *VMEnv) Origin() common.Address { return *self.transactor }
|
||||||
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
|
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
|
||||||
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
|
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
|
||||||
func (self *VMEnv) Time() *big.Int { return self.time }
|
func (self *VMEnv) Time() *big.Int { return self.time }
|
||||||
func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
|
func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
|
||||||
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
|
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
|
||||||
func (self *VMEnv) Value() *big.Int { return self.value }
|
func (self *VMEnv) Value() *big.Int { return self.value }
|
||||||
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
|
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
|
||||||
func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy }
|
func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy }
|
||||||
func (self *VMEnv) Depth() int { return 0 }
|
func (self *VMEnv) Depth() int { return 0 }
|
||||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||||
func (self *VMEnv) GetHash(n uint64) common.Hash {
|
func (self *VMEnv) GetHash(n uint64) common.Hash {
|
||||||
if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 {
|
if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 {
|
||||||
return self.block.Hash()
|
return self.block.Hash()
|
||||||
|
|
|
||||||
232
cmd/geth/dao_test.go
Normal file
232
cmd/geth/dao_test.go
Normal file
|
|
@ -0,0 +1,232 @@
|
||||||
|
// Copyright 2016 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 main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/expanse-project/go-expanse/common"
|
||||||
|
"github.com/expanse-project/go-expanse/core"
|
||||||
|
"github.com/expanse-project/go-expanse/ethdb"
|
||||||
|
"github.com/expanse-project/go-expanse/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Genesis block for nodes which don't care about the DAO fork (i.e. not configured)
|
||||||
|
var daoOldGenesis = `{
|
||||||
|
"alloc" : {},
|
||||||
|
"coinbase" : "0x0000000000000000000000000000000000000000",
|
||||||
|
"difficulty" : "0x20000",
|
||||||
|
"extraData" : "",
|
||||||
|
"gasLimit" : "0x2fefd8",
|
||||||
|
"nonce" : "0x0000000000000042",
|
||||||
|
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"timestamp" : "0x00",
|
||||||
|
"config" : {}
|
||||||
|
}`
|
||||||
|
|
||||||
|
// Genesis block for nodes which actively oppose the DAO fork
|
||||||
|
var daoNoForkGenesis = `{
|
||||||
|
"alloc" : {},
|
||||||
|
"coinbase" : "0x0000000000000000000000000000000000000000",
|
||||||
|
"difficulty" : "0x20000",
|
||||||
|
"extraData" : "",
|
||||||
|
"gasLimit" : "0x2fefd8",
|
||||||
|
"nonce" : "0x0000000000000042",
|
||||||
|
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"timestamp" : "0x00",
|
||||||
|
"config" : {
|
||||||
|
"daoForkBlock" : 314,
|
||||||
|
"daoForkSupport" : false
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
// Genesis block for nodes which actively support the DAO fork
|
||||||
|
var daoProForkGenesis = `{
|
||||||
|
"alloc" : {},
|
||||||
|
"coinbase" : "0x0000000000000000000000000000000000000000",
|
||||||
|
"difficulty" : "0x20000",
|
||||||
|
"extraData" : "",
|
||||||
|
"gasLimit" : "0x2fefd8",
|
||||||
|
"nonce" : "0x0000000000000042",
|
||||||
|
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"timestamp" : "0x00",
|
||||||
|
"config" : {
|
||||||
|
"daoForkBlock" : 314,
|
||||||
|
"daoForkSupport" : true
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
var daoGenesisHash = common.HexToHash("5e1fc79cb4ffa4739177b5408045cd5d51c6cf766133f23f7cd72ee1f8d790e0")
|
||||||
|
var daoGenesisForkBlock = big.NewInt(314)
|
||||||
|
|
||||||
|
// Tests that the DAO hard-fork number and the nodes support/opposition is correctly
|
||||||
|
// set in the database after various initialization procedures and invocations.
|
||||||
|
func TestDAODefaultMainnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, "", [][2]bool{{false, false}}, params.MainNetDAOForkBlock, true)
|
||||||
|
}
|
||||||
|
func TestDAOSupportMainnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, "", [][2]bool{{true, false}}, params.MainNetDAOForkBlock, true)
|
||||||
|
}
|
||||||
|
func TestDAOOpposeMainnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, "", [][2]bool{{false, true}}, params.MainNetDAOForkBlock, false)
|
||||||
|
}
|
||||||
|
func TestDAOSwitchToSupportMainnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, "", [][2]bool{{false, true}, {true, false}}, params.MainNetDAOForkBlock, true)
|
||||||
|
}
|
||||||
|
func TestDAOSwitchToOpposeMainnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, "", [][2]bool{{true, false}, {false, true}}, params.MainNetDAOForkBlock, false)
|
||||||
|
}
|
||||||
|
func TestDAODefaultTestnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, true, "", [][2]bool{{false, false}}, params.TestNetDAOForkBlock, true)
|
||||||
|
}
|
||||||
|
func TestDAOSupportTestnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, true, "", [][2]bool{{true, false}}, params.TestNetDAOForkBlock, true)
|
||||||
|
}
|
||||||
|
func TestDAOOpposeTestnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, true, "", [][2]bool{{false, true}}, params.TestNetDAOForkBlock, false)
|
||||||
|
}
|
||||||
|
func TestDAOSwitchToSupportTestnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, true, "", [][2]bool{{false, true}, {true, false}}, params.TestNetDAOForkBlock, true)
|
||||||
|
}
|
||||||
|
func TestDAOSwitchToOpposeTestnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, true, "", [][2]bool{{true, false}, {false, true}}, params.TestNetDAOForkBlock, false)
|
||||||
|
}
|
||||||
|
func TestDAOInitOldPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoOldGenesis, [][2]bool{}, nil, false)
|
||||||
|
}
|
||||||
|
func TestDAODefaultOldPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoOldGenesis, [][2]bool{{false, false}}, params.MainNetDAOForkBlock, true)
|
||||||
|
}
|
||||||
|
func TestDAOSupportOldPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoOldGenesis, [][2]bool{{true, false}}, params.MainNetDAOForkBlock, true)
|
||||||
|
}
|
||||||
|
func TestDAOOpposeOldPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoOldGenesis, [][2]bool{{false, true}}, params.MainNetDAOForkBlock, false)
|
||||||
|
}
|
||||||
|
func TestDAOSwitchToSupportOldPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoOldGenesis, [][2]bool{{false, true}, {true, false}}, params.MainNetDAOForkBlock, true)
|
||||||
|
}
|
||||||
|
func TestDAOSwitchToOpposeOldPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoOldGenesis, [][2]bool{{true, false}, {false, true}}, params.MainNetDAOForkBlock, false)
|
||||||
|
}
|
||||||
|
func TestDAOInitNoForkPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoNoForkGenesis, [][2]bool{}, daoGenesisForkBlock, false)
|
||||||
|
}
|
||||||
|
func TestDAODefaultNoForkPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoNoForkGenesis, [][2]bool{{false, false}}, daoGenesisForkBlock, false)
|
||||||
|
}
|
||||||
|
func TestDAOSupportNoForkPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoNoForkGenesis, [][2]bool{{true, false}}, daoGenesisForkBlock, true)
|
||||||
|
}
|
||||||
|
func TestDAOOpposeNoForkPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoNoForkGenesis, [][2]bool{{false, true}}, daoGenesisForkBlock, false)
|
||||||
|
}
|
||||||
|
func TestDAOSwitchToSupportNoForkPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoNoForkGenesis, [][2]bool{{false, true}, {true, false}}, daoGenesisForkBlock, true)
|
||||||
|
}
|
||||||
|
func TestDAOSwitchToOpposeNoForkPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoNoForkGenesis, [][2]bool{{true, false}, {false, true}}, daoGenesisForkBlock, false)
|
||||||
|
}
|
||||||
|
func TestDAOInitProForkPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoProForkGenesis, [][2]bool{}, daoGenesisForkBlock, true)
|
||||||
|
}
|
||||||
|
func TestDAODefaultProForkPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoProForkGenesis, [][2]bool{{false, false}}, daoGenesisForkBlock, true)
|
||||||
|
}
|
||||||
|
func TestDAOSupportProForkPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoProForkGenesis, [][2]bool{{true, false}}, daoGenesisForkBlock, true)
|
||||||
|
}
|
||||||
|
func TestDAOOpposeProForkPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoProForkGenesis, [][2]bool{{false, true}}, daoGenesisForkBlock, false)
|
||||||
|
}
|
||||||
|
func TestDAOSwitchToSupportProForkPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoProForkGenesis, [][2]bool{{false, true}, {true, false}}, daoGenesisForkBlock, true)
|
||||||
|
}
|
||||||
|
func TestDAOSwitchToOpposeProForkPrivnet(t *testing.T) {
|
||||||
|
testDAOForkBlockNewChain(t, false, daoProForkGenesis, [][2]bool{{true, false}, {false, true}}, daoGenesisForkBlock, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDAOForkBlockNewChain(t *testing.T, testnet bool, genesis string, votes [][2]bool, expectBlock *big.Int, expectVote bool) {
|
||||||
|
// Create a temporary data directory to use and inspect later
|
||||||
|
datadir := tmpdir(t)
|
||||||
|
defer os.RemoveAll(datadir)
|
||||||
|
|
||||||
|
// Start a instance with the requested flags set and immediately terminate
|
||||||
|
if genesis != "" {
|
||||||
|
json := filepath.Join(datadir, "genesis.json")
|
||||||
|
if err := ioutil.WriteFile(json, []byte(genesis), 0600); err != nil {
|
||||||
|
t.Fatalf("failed to write genesis file: %v", err)
|
||||||
|
}
|
||||||
|
runGeth(t, "--datadir", datadir, "init", json).cmd.Wait()
|
||||||
|
}
|
||||||
|
for _, vote := range votes {
|
||||||
|
args := []string{"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--datadir", datadir}
|
||||||
|
if testnet {
|
||||||
|
args = append(args, "--testnet")
|
||||||
|
}
|
||||||
|
if vote[0] {
|
||||||
|
args = append(args, "--support-dao-fork")
|
||||||
|
}
|
||||||
|
if vote[1] {
|
||||||
|
args = append(args, "--oppose-dao-fork")
|
||||||
|
}
|
||||||
|
geth := runGeth(t, append(args, []string{"--exec", "2+2", "console"}...)...)
|
||||||
|
geth.cmd.Wait()
|
||||||
|
}
|
||||||
|
// Retrieve the DAO config flag from the database
|
||||||
|
path := filepath.Join(datadir, "chaindata")
|
||||||
|
if testnet && genesis == "" {
|
||||||
|
path = filepath.Join(datadir, "testnet", "chaindata")
|
||||||
|
}
|
||||||
|
db, err := ethdb.NewLDBDatabase(path, 0, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to open test database: %v", err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
genesisHash := common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
|
||||||
|
if testnet {
|
||||||
|
genesisHash = common.HexToHash("0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303")
|
||||||
|
}
|
||||||
|
if genesis != "" {
|
||||||
|
genesisHash = daoGenesisHash
|
||||||
|
}
|
||||||
|
config, err := core.GetChainConfig(db, genesisHash)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to retrieve chain config: %v", err)
|
||||||
|
}
|
||||||
|
// Validate the DAO hard-fork block number against the expected value
|
||||||
|
if config.DAOForkBlock == nil {
|
||||||
|
if expectBlock != nil {
|
||||||
|
t.Errorf("dao hard-fork block mismatch: have nil, want %v", expectBlock)
|
||||||
|
}
|
||||||
|
} else if expectBlock == nil {
|
||||||
|
t.Errorf("dao hard-fork block mismatch: have %v, want nil", config.DAOForkBlock)
|
||||||
|
} else if config.DAOForkBlock.Cmp(expectBlock) != 0 {
|
||||||
|
t.Errorf("dao hard-fork block mismatch: have %v, want %v", config.DAOForkBlock, expectBlock)
|
||||||
|
}
|
||||||
|
if config.DAOForkSupport != expectVote {
|
||||||
|
t.Errorf("dao hard-fork support mismatch: have %v, want %v", config.DAOForkSupport, expectVote)
|
||||||
|
}
|
||||||
|
}
|
||||||
107
cmd/geth/genesis_test.go
Normal file
107
cmd/geth/genesis_test.go
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
// Copyright 2016 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 main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var customGenesisTests = []struct {
|
||||||
|
genesis string
|
||||||
|
query string
|
||||||
|
result string
|
||||||
|
}{
|
||||||
|
// Plain genesis file without anything extra
|
||||||
|
{
|
||||||
|
genesis: `{
|
||||||
|
"alloc" : {},
|
||||||
|
"coinbase" : "0x0000000000000000000000000000000000000000",
|
||||||
|
"difficulty" : "0x20000",
|
||||||
|
"extraData" : "",
|
||||||
|
"gasLimit" : "0x2fefd8",
|
||||||
|
"nonce" : "0x0000000000000042",
|
||||||
|
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"timestamp" : "0x00"
|
||||||
|
}`,
|
||||||
|
query: "eth.getBlock(0).nonce",
|
||||||
|
result: "0x0000000000000042",
|
||||||
|
},
|
||||||
|
// Genesis file with an empty chain configuration (ensure missing fields work)
|
||||||
|
{
|
||||||
|
genesis: `{
|
||||||
|
"alloc" : {},
|
||||||
|
"coinbase" : "0x0000000000000000000000000000000000000000",
|
||||||
|
"difficulty" : "0x20000",
|
||||||
|
"extraData" : "",
|
||||||
|
"gasLimit" : "0x2fefd8",
|
||||||
|
"nonce" : "0x0000000000000042",
|
||||||
|
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"timestamp" : "0x00",
|
||||||
|
"config" : {}
|
||||||
|
}`,
|
||||||
|
query: "eth.getBlock(0).nonce",
|
||||||
|
result: "0x0000000000000042",
|
||||||
|
},
|
||||||
|
// Genesis file with specific chain configurations
|
||||||
|
{
|
||||||
|
genesis: `{
|
||||||
|
"alloc" : {},
|
||||||
|
"coinbase" : "0x0000000000000000000000000000000000000000",
|
||||||
|
"difficulty" : "0x20000",
|
||||||
|
"extraData" : "",
|
||||||
|
"gasLimit" : "0x2fefd8",
|
||||||
|
"nonce" : "0x0000000000000042",
|
||||||
|
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"timestamp" : "0x00",
|
||||||
|
"config" : {
|
||||||
|
"homesteadBlock" : 314,
|
||||||
|
"daoForkBlock" : 141,
|
||||||
|
"daoForkSupport" : true
|
||||||
|
},
|
||||||
|
}`,
|
||||||
|
query: "eth.getBlock(0).nonce",
|
||||||
|
result: "0x0000000000000042",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that initializing with a custom genesis block and chain definitions
|
||||||
|
// work properly.
|
||||||
|
func TestCustomGenesis(t *testing.T) {
|
||||||
|
for i, tt := range customGenesisTests {
|
||||||
|
// Create a temporary data directory to use and inspect later
|
||||||
|
datadir := tmpdir(t)
|
||||||
|
defer os.RemoveAll(datadir)
|
||||||
|
|
||||||
|
// Initialize the data directory with the custom genesis block
|
||||||
|
json := filepath.Join(datadir, "genesis.json")
|
||||||
|
if err := ioutil.WriteFile(json, []byte(tt.genesis), 0600); err != nil {
|
||||||
|
t.Fatalf("test %d: failed to write genesis file: %v", i, err)
|
||||||
|
}
|
||||||
|
runGeth(t, "--datadir", datadir, "init", json).cmd.Wait()
|
||||||
|
|
||||||
|
// Query the custom genesis block
|
||||||
|
geth := runGeth(t, "--datadir", datadir, "--maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--exec", tt.query, "console")
|
||||||
|
geth.expectRegexp(tt.result)
|
||||||
|
geth.expectExit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -50,7 +50,7 @@ const (
|
||||||
clientIdentifier = "Gexp" // Client identifier to advertise over the network
|
clientIdentifier = "Gexp" // Client identifier to advertise over the network
|
||||||
versionMajor = 1 // Major version component of the current release
|
versionMajor = 1 // Major version component of the current release
|
||||||
versionMinor = 4 // Minor version component of the current release
|
versionMinor = 4 // Minor version component of the current release
|
||||||
versionPatch = 7 // Patch version component of the current release
|
versionPatch = 16 // Patch version component of the current release
|
||||||
versionMeta = "stable" // Version metadata to append to the version string
|
versionMeta = "stable" // Version metadata to append to the version string
|
||||||
versionOracle = "0x926d69cc3bbf81d52cba6886d788df007a15a3cd" // Expanse address of the Gexp release oracle
|
versionOracle = "0x926d69cc3bbf81d52cba6886d788df007a15a3cd" // Expanse address of the Gexp release oracle
|
||||||
)
|
)
|
||||||
|
|
@ -148,7 +148,6 @@ participating.
|
||||||
utils.IdentityFlag,
|
utils.IdentityFlag,
|
||||||
utils.UnlockedAccountFlag,
|
utils.UnlockedAccountFlag,
|
||||||
utils.PasswordFileFlag,
|
utils.PasswordFileFlag,
|
||||||
utils.GenesisFileFlag,
|
|
||||||
utils.BootnodesFlag,
|
utils.BootnodesFlag,
|
||||||
utils.DataDirFlag,
|
utils.DataDirFlag,
|
||||||
utils.KeyStoreDirFlag,
|
utils.KeyStoreDirFlag,
|
||||||
|
|
@ -163,6 +162,8 @@ participating.
|
||||||
utils.MaxPendingPeersFlag,
|
utils.MaxPendingPeersFlag,
|
||||||
utils.EtherbaseFlag,
|
utils.EtherbaseFlag,
|
||||||
utils.GasPriceFlag,
|
utils.GasPriceFlag,
|
||||||
|
utils.SupportDAOFork,
|
||||||
|
utils.OpposeDAOFork,
|
||||||
utils.MinerThreadsFlag,
|
utils.MinerThreadsFlag,
|
||||||
utils.MiningEnabledFlag,
|
utils.MiningEnabledFlag,
|
||||||
utils.MiningGPUFlag,
|
utils.MiningGPUFlag,
|
||||||
|
|
@ -223,12 +224,6 @@ participating.
|
||||||
exp.EnableBadBlockReporting = true
|
exp.EnableBadBlockReporting = true
|
||||||
|
|
||||||
utils.SetupNetwork(ctx)
|
utils.SetupNetwork(ctx)
|
||||||
|
|
||||||
// Deprecation warning.
|
|
||||||
if ctx.GlobalIsSet(utils.GenesisFileFlag.Name) {
|
|
||||||
common.PrintDepricationWarning("--genesis is deprecated. Switch to use 'gexp init /path/to/file'")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,6 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
utils.OlympicFlag,
|
utils.OlympicFlag,
|
||||||
utils.TestNetFlag,
|
utils.TestNetFlag,
|
||||||
utils.DevModeFlag,
|
utils.DevModeFlag,
|
||||||
utils.GenesisFileFlag,
|
|
||||||
utils.IdentityFlag,
|
utils.IdentityFlag,
|
||||||
utils.FastSyncFlag,
|
utils.FastSyncFlag,
|
||||||
utils.LightKDFFlag,
|
utils.LightKDFFlag,
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
"github.com/expanse-project/go-expanse/common"
|
"github.com/expanse-project/go-expanse/common"
|
||||||
"github.com/expanse-project/go-expanse/core"
|
"github.com/expanse-project/go-expanse/core"
|
||||||
|
|
@ -53,10 +54,16 @@ func openLogFile(Datadir string, filename string) *os.File {
|
||||||
// is redirected to a different file.
|
// is redirected to a different file.
|
||||||
func Fatalf(format string, args ...interface{}) {
|
func Fatalf(format string, args ...interface{}) {
|
||||||
w := io.MultiWriter(os.Stdout, os.Stderr)
|
w := io.MultiWriter(os.Stdout, os.Stderr)
|
||||||
outf, _ := os.Stdout.Stat()
|
if runtime.GOOS == "windows" {
|
||||||
errf, _ := os.Stderr.Stat()
|
// The SameFile check below doesn't work on Windows.
|
||||||
if outf != nil && errf != nil && os.SameFile(outf, errf) {
|
// stdout is unlikely to get redirected though, so just print there.
|
||||||
w = os.Stderr
|
w = os.Stdout
|
||||||
|
} else {
|
||||||
|
outf, _ := os.Stdout.Stat()
|
||||||
|
errf, _ := os.Stderr.Stat()
|
||||||
|
if outf != nil && errf != nil && os.SameFile(outf, errf) {
|
||||||
|
w = os.Stderr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "Fatal: "+format+"\n", args...)
|
fmt.Fprintf(w, "Fatal: "+format+"\n", args...)
|
||||||
logger.Flush()
|
logger.Flush()
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,11 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/expanse-project/ethash"
|
"github.com/expanse-project/ethash"
|
||||||
"github.com/expanse-project/go-expanse/accounts"
|
"github.com/expanse-project/go-expanse/accounts"
|
||||||
|
|
@ -126,10 +124,6 @@ var (
|
||||||
Name: "dev",
|
Name: "dev",
|
||||||
Usage: "Developer mode: pre-configured private network with several debugging flags",
|
Usage: "Developer mode: pre-configured private network with several debugging flags",
|
||||||
}
|
}
|
||||||
GenesisFileFlag = cli.StringFlag{
|
|
||||||
Name: "genesis",
|
|
||||||
Usage: "Insert/overwrite the genesis block (JSON format)",
|
|
||||||
}
|
|
||||||
IdentityFlag = cli.StringFlag{
|
IdentityFlag = cli.StringFlag{
|
||||||
Name: "identity",
|
Name: "identity",
|
||||||
Usage: "Custom node name",
|
Usage: "Custom node name",
|
||||||
|
|
@ -161,6 +155,15 @@ var (
|
||||||
Name: "lightkdf",
|
Name: "lightkdf",
|
||||||
Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
|
Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
|
||||||
}
|
}
|
||||||
|
// Fork settings
|
||||||
|
SupportDAOFork = cli.BoolFlag{
|
||||||
|
Name: "support-dao-fork",
|
||||||
|
Usage: "Updates the chain rules to support the DAO hard-fork",
|
||||||
|
}
|
||||||
|
OpposeDAOFork = cli.BoolFlag{
|
||||||
|
Name: "oppose-dao-fork",
|
||||||
|
Usage: "Updates the chain rules to oppose the DAO hard-fork",
|
||||||
|
}
|
||||||
// Miner settings
|
// Miner settings
|
||||||
// TODO: refactor CPU vs GPU mining flags
|
// TODO: refactor CPU vs GPU mining flags
|
||||||
MiningEnabledFlag = cli.BoolFlag{
|
MiningEnabledFlag = cli.BoolFlag{
|
||||||
|
|
@ -534,20 +537,6 @@ func MakeWSRpcHost(ctx *cli.Context) string {
|
||||||
return ctx.GlobalString(WSListenAddrFlag.Name)
|
return ctx.GlobalString(WSListenAddrFlag.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeGenesisBlock loads up a genesis block from an input file specified in the
|
|
||||||
// command line, or returns the empty string if none set.
|
|
||||||
func MakeGenesisBlock(ctx *cli.Context) string {
|
|
||||||
genesis := ctx.GlobalString(GenesisFileFlag.Name)
|
|
||||||
if genesis == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
data, err := ioutil.ReadFile(genesis)
|
|
||||||
if err != nil {
|
|
||||||
Fatalf("Failed to load custom genesis file: %v", err)
|
|
||||||
}
|
|
||||||
return string(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeDatabaseHandles raises out the number of allowed file handles per process
|
// MakeDatabaseHandles raises out the number of allowed file handles per process
|
||||||
// for Gexp and returns half of the allowance to assign to the database.
|
// for Gexp and returns half of the allowance to assign to the database.
|
||||||
func MakeDatabaseHandles() int {
|
func MakeDatabaseHandles() int {
|
||||||
|
|
@ -676,20 +665,9 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
|
||||||
}
|
}
|
||||||
// Configure the Expanse service
|
// Configure the Expanse service
|
||||||
accman := MakeAccountManager(ctx)
|
accman := MakeAccountManager(ctx)
|
||||||
|
|
||||||
// initialise new random number generator
|
|
||||||
rand := rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
||||||
// get enabled jit flag
|
|
||||||
jitEnabled := ctx.GlobalBool(VMEnableJitFlag.Name)
|
jitEnabled := ctx.GlobalBool(VMEnableJitFlag.Name)
|
||||||
// if the jit is not enabled enable it for 10 pct of the people
|
|
||||||
if !jitEnabled && rand.Float64() < 0.1 {
|
|
||||||
jitEnabled = true
|
|
||||||
glog.V(logger.Info).Infoln("You're one of the lucky few that will try out the JIT VM (random). If you get a consensus failure please be so kind to report this incident with the block hash that failed. You can switch to the regular VM by setting --jitvm=false")
|
|
||||||
}
|
|
||||||
|
|
||||||
ethConf := &exp.Config{
|
ethConf := &exp.Config{
|
||||||
ChainConfig: MustMakeChainConfig(ctx),
|
ChainConfig: MustMakeChainConfig(ctx),
|
||||||
Genesis: MakeGenesisBlock(ctx),
|
|
||||||
FastSync: ctx.GlobalBool(FastSyncFlag.Name),
|
FastSync: ctx.GlobalBool(FastSyncFlag.Name),
|
||||||
BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.Name),
|
BlockChainVersion: ctx.GlobalInt(BlockchainVersionFlag.Name),
|
||||||
DatabaseCache: ctx.GlobalInt(CacheFlag.Name),
|
DatabaseCache: ctx.GlobalInt(CacheFlag.Name),
|
||||||
|
|
@ -722,17 +700,13 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
|
||||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||||
ethConf.NetworkId = 1
|
ethConf.NetworkId = 1
|
||||||
}
|
}
|
||||||
if !ctx.GlobalIsSet(GenesisFileFlag.Name) {
|
ethConf.Genesis = core.OlympicGenesisBlock()
|
||||||
ethConf.Genesis = core.OlympicGenesisBlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
case ctx.GlobalBool(TestNetFlag.Name):
|
case ctx.GlobalBool(TestNetFlag.Name):
|
||||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||||
ethConf.NetworkId = 2
|
ethConf.NetworkId = 2
|
||||||
}
|
}
|
||||||
if !ctx.GlobalIsSet(GenesisFileFlag.Name) {
|
ethConf.Genesis = core.TestNetGenesisBlock()
|
||||||
ethConf.Genesis = core.TestNetGenesisBlock()
|
|
||||||
}
|
|
||||||
state.StartingNonce = 1048576 // (2**20)
|
state.StartingNonce = 1048576 // (2**20)
|
||||||
|
|
||||||
case ctx.GlobalBool(DevModeFlag.Name):
|
case ctx.GlobalBool(DevModeFlag.Name):
|
||||||
|
|
@ -746,10 +720,8 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
|
||||||
if !ctx.GlobalIsSet(ListenPortFlag.Name) {
|
if !ctx.GlobalIsSet(ListenPortFlag.Name) {
|
||||||
stackConf.ListenAddr = ":0"
|
stackConf.ListenAddr = ":0"
|
||||||
}
|
}
|
||||||
// Override the Expanse protocol configs
|
// Override the Ethereum protocol configs
|
||||||
if !ctx.GlobalIsSet(GenesisFileFlag.Name) {
|
ethConf.Genesis = core.OlympicGenesisBlock()
|
||||||
ethConf.Genesis = core.OlympicGenesisBlock()
|
|
||||||
}
|
|
||||||
if !ctx.GlobalIsSet(GasPriceFlag.Name) {
|
if !ctx.GlobalIsSet(GasPriceFlag.Name) {
|
||||||
ethConf.GasPrice = new(big.Int)
|
ethConf.GasPrice = new(big.Int)
|
||||||
}
|
}
|
||||||
|
|
@ -806,24 +778,62 @@ func MustMakeChainConfig(ctx *cli.Context) *core.ChainConfig {
|
||||||
|
|
||||||
// MustMakeChainConfigFromDb reads the chain configuration from the given database.
|
// MustMakeChainConfigFromDb reads the chain configuration from the given database.
|
||||||
func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainConfig {
|
func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainConfig {
|
||||||
genesis := core.GetBlock(db, core.GetCanonicalHash(db, 0))
|
// If the chain is already initialized, use any existing chain configs
|
||||||
|
config := new(core.ChainConfig)
|
||||||
|
|
||||||
|
genesis := core.GetBlock(db, core.GetCanonicalHash(db, 0))
|
||||||
if genesis != nil {
|
if genesis != nil {
|
||||||
// Existing genesis block, use stored config if available.
|
|
||||||
storedConfig, err := core.GetChainConfig(db, genesis.Hash())
|
storedConfig, err := core.GetChainConfig(db, genesis.Hash())
|
||||||
if err == nil {
|
switch err {
|
||||||
return storedConfig
|
case nil:
|
||||||
} else if err != core.ChainConfigNotFoundErr {
|
config = storedConfig
|
||||||
|
case core.ChainConfigNotFoundErr:
|
||||||
|
// No configs found, use empty, will populate below
|
||||||
|
default:
|
||||||
Fatalf("Could not make chain configuration: %v", err)
|
Fatalf("Could not make chain configuration: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var homesteadBlockNo *big.Int
|
// Set any missing fields due to them being unset or system upgrade
|
||||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
if config.HomesteadBlock == nil {
|
||||||
homesteadBlockNo = params.TestNetHomesteadBlock
|
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||||
} else {
|
config.HomesteadBlock = params.TestNetHomesteadBlock
|
||||||
homesteadBlockNo = params.MainNetHomesteadBlock
|
} else {
|
||||||
|
config.HomesteadBlock = params.MainNetHomesteadBlock
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return &core.ChainConfig{HomesteadBlock: homesteadBlockNo}
|
if config.DAOForkBlock == nil {
|
||||||
|
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||||
|
config.DAOForkBlock = params.TestNetDAOForkBlock
|
||||||
|
} else {
|
||||||
|
config.DAOForkBlock = params.MainNetDAOForkBlock
|
||||||
|
}
|
||||||
|
config.DAOForkSupport = true
|
||||||
|
}
|
||||||
|
// Force override any existing configs if explicitly requested
|
||||||
|
switch {
|
||||||
|
case ctx.GlobalBool(SupportDAOFork.Name):
|
||||||
|
config.DAOForkSupport = true
|
||||||
|
case ctx.GlobalBool(OpposeDAOFork.Name):
|
||||||
|
config.DAOForkSupport = false
|
||||||
|
}
|
||||||
|
// Temporarilly display a proper message so the user knows which fork its on
|
||||||
|
if !ctx.GlobalBool(TestNetFlag.Name) && (genesis == nil || genesis.Hash() == common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")) {
|
||||||
|
choice := "SUPPORT"
|
||||||
|
if !config.DAOForkSupport {
|
||||||
|
choice = "OPPOSE"
|
||||||
|
}
|
||||||
|
current := fmt.Sprintf(" is currently configured to %s the DAO hard-fork!", choice)
|
||||||
|
howtoswap := fmt.Sprintf("You can change your choice prior to block #%v with --support-dao-fork or --oppose-dao-fork.", config.DAOForkBlock)
|
||||||
|
howtosync := fmt.Sprintf("After the hard-fork block #%v passed, changing chains requires a resync from scratch!", config.DAOForkBlock)
|
||||||
|
separator := strings.Repeat("-", len(howtoswap))
|
||||||
|
|
||||||
|
glog.V(logger.Warn).Info(separator)
|
||||||
|
glog.V(logger.Warn).Info(current)
|
||||||
|
glog.V(logger.Warn).Info(howtoswap)
|
||||||
|
glog.V(logger.Warn).Info(howtosync)
|
||||||
|
glog.V(logger.Warn).Info(separator)
|
||||||
|
}
|
||||||
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails.
|
// MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails.
|
||||||
|
|
|
||||||
|
|
@ -241,11 +241,11 @@ func (c *Console) AutoCompleteInput(line string, pos int) (string, []string, str
|
||||||
return line[:start], c.jsre.CompleteKeywords(line[start:pos]), line[pos:]
|
return line[:start], c.jsre.CompleteKeywords(line[start:pos]), line[pos:]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Welcome show summary of current Geth instance and some metadata about the
|
// Welcome show summary of current instance and some metadata about the
|
||||||
// console's available modules.
|
// console's available modules.
|
||||||
func (c *Console) Welcome() {
|
func (c *Console) Welcome() {
|
||||||
// Print some generic Geth metadata
|
// Print some generic metadata
|
||||||
fmt.Fprintf(c.printer, "Welcome to the Geth JavaScript console!\n\n")
|
fmt.Fprintf(c.printer, "Welcome to the JavaScript console!\n\n")
|
||||||
c.jsre.Run(`
|
c.jsre.Run(`
|
||||||
console.log("instance: " + web3.version.node);
|
console.log("instance: " + web3.version.node);
|
||||||
console.log("coinbase: " + eth.coinbase);
|
console.log("coinbase: " + eth.coinbase);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ FROM alpine:3.3
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
apk add --update go git make gcc musl-dev && \
|
apk add --update go git make gcc musl-dev && \
|
||||||
git clone https://github.com/ethereum/go-ethereum && \
|
git clone https://github.com/expanse-project/go-expanse && \
|
||||||
(cd go-ethereum && git checkout develop) && \
|
(cd go-ethereum && git checkout develop) && \
|
||||||
(cd go-ethereum && make gexp) && \
|
(cd go-ethereum && make gexp) && \
|
||||||
cp go-ethereum/build/bin/gexp /gexp && \
|
cp go-ethereum/build/bin/gexp /gexp && \
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
FROM alpine:3.3
|
FROM alpine:3.3
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
apk add --update go git make gcc musl-dev gmp-dev gmp && \
|
apk add --update go git make gcc musl-dev && \
|
||||||
git clone https://github.com/ethereum/go-ethereum && \
|
git clone https://github.com/expanse-project/go-expanse && \
|
||||||
(cd go-ethereum && make gexp) && \
|
(cd go-expanse && make geth) && \
|
||||||
cp go-ethereum/build/bin/gexp /gexp && \
|
cp go-expanse/build/bin/gexp /gexp && \
|
||||||
apk del go git make gcc musl-dev gmp-dev && \
|
apk del go git make gcc musl-dev && \
|
||||||
rm -rf /go-ethereum && rm -rf /var/cache/apk/*
|
rm -rf /go-expanse && rm -rf /var/cache/apk/*
|
||||||
|
|
||||||
EXPOSE 8545
|
EXPOSE 9656
|
||||||
EXPOSE 30303
|
EXPOSE 42786
|
||||||
|
|
||||||
ENTRYPOINT ["/gexp"]
|
ENTRYPOINT ["/gexp"]
|
||||||
|
|
|
||||||
2
containers/vagrant/Vagrantfile
vendored
2
containers/vagrant/Vagrantfile
vendored
|
|
@ -8,7 +8,7 @@ Vagrant.configure(2) do |config|
|
||||||
vb.memory = "2048"
|
vb.memory = "2048"
|
||||||
end
|
end
|
||||||
|
|
||||||
config.vm.synced_folder "../../", "/home/vagrant/go/src/github.com/ethereum/go-ethereum"
|
config.vm.synced_folder "../../", "/home/vagrant/go/src/github.com/expanse-project/go-expanse"
|
||||||
config.vm.synced_folder ".", "/vagrant", disabled: true
|
config.vm.synced_folder ".", "/vagrant", disabled: true
|
||||||
|
|
||||||
config.vm.provision "shell", inline: <<-SHELL
|
config.vm.provision "shell", inline: <<-SHELL
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
||||||
// Generate a chain of b.N blocks using the supplied block
|
// Generate a chain of b.N blocks using the supplied block
|
||||||
// generator function.
|
// generator function.
|
||||||
genesis := WriteGenesisBlockForTesting(db, GenesisAccount{benchRootAddr, benchRootFunds})
|
genesis := WriteGenesisBlockForTesting(db, GenesisAccount{benchRootAddr, benchRootFunds})
|
||||||
chain, _ := GenerateChain(genesis, db, b.N, gen)
|
chain, _ := GenerateChain(nil, genesis, db, b.N, gen)
|
||||||
|
|
||||||
// Time the insertion of the new chain.
|
// Time the insertion of the new chain.
|
||||||
// State and blocks are stored in the same DB.
|
// State and blocks are stored in the same DB.
|
||||||
|
|
|
||||||
|
|
@ -247,7 +247,8 @@ func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, pare
|
||||||
return &BlockNonceErr{header.Number, header.Hash(), header.Nonce.Uint64()}
|
return &BlockNonceErr{header.Number, header.Hash(), header.Nonce.Uint64()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
// If all checks passed, validate the extra-data field for hard forks
|
||||||
|
return ValidateDAOHeaderExtraData(config, header)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CalcDifficulty is the difficulty adjustment algorithm. It returns
|
// CalcDifficulty is the difficulty adjustment algorithm. It returns
|
||||||
|
|
|
||||||
|
|
@ -95,10 +95,11 @@ type BlockChain struct {
|
||||||
currentBlock *types.Block // Current head of the block chain
|
currentBlock *types.Block // Current head of the block chain
|
||||||
currentFastBlock *types.Block // Current head of the fast-sync chain (may be above the block chain!)
|
currentFastBlock *types.Block // Current head of the fast-sync chain (may be above the block chain!)
|
||||||
|
|
||||||
bodyCache *lru.Cache // Cache for the most recent block bodies
|
stateCache *state.StateDB // State database to reuse between imports (contains state cache)
|
||||||
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
|
bodyCache *lru.Cache // Cache for the most recent block bodies
|
||||||
blockCache *lru.Cache // Cache for the most recent entire blocks
|
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
|
||||||
futureBlocks *lru.Cache // future blocks are blocks added for later processing
|
blockCache *lru.Cache // Cache for the most recent entire blocks
|
||||||
|
futureBlocks *lru.Cache // future blocks are blocks added for later processing
|
||||||
|
|
||||||
quit chan struct{} // blockchain quit channel
|
quit chan struct{} // blockchain quit channel
|
||||||
running int32 // running must be called atomically
|
running int32 // running must be called atomically
|
||||||
|
|
@ -198,11 +199,18 @@ func (self *BlockChain) loadLastState() error {
|
||||||
self.currentFastBlock = block
|
self.currentFastBlock = block
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Initialize a statedb cache to ensure singleton account bloom filter generation
|
||||||
|
statedb, err := state.New(self.currentBlock.Root(), self.chainDb)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
self.stateCache = statedb
|
||||||
|
self.stateCache.GetAccount(common.Address{})
|
||||||
|
|
||||||
// Issue a status log and return
|
// Issue a status log and return
|
||||||
headerTd := self.GetTd(self.hc.CurrentHeader().Hash())
|
headerTd := self.GetTd(self.hc.CurrentHeader().Hash())
|
||||||
blockTd := self.GetTd(self.currentBlock.Hash())
|
blockTd := self.GetTd(self.currentBlock.Hash())
|
||||||
fastTd := self.GetTd(self.currentFastBlock.Hash())
|
fastTd := self.GetTd(self.currentFastBlock.Hash())
|
||||||
|
|
||||||
glog.V(logger.Info).Infof("Last header: #%d [%x…] TD=%v", self.hc.CurrentHeader().Number, self.hc.CurrentHeader().Hash().Bytes()[:4], headerTd)
|
glog.V(logger.Info).Infof("Last header: #%d [%x…] TD=%v", self.hc.CurrentHeader().Number, self.hc.CurrentHeader().Hash().Bytes()[:4], headerTd)
|
||||||
glog.V(logger.Info).Infof("Last block: #%d [%x…] TD=%v", self.currentBlock.Number(), self.currentBlock.Hash().Bytes()[:4], blockTd)
|
glog.V(logger.Info).Infof("Last block: #%d [%x…] TD=%v", self.currentBlock.Number(), self.currentBlock.Hash().Bytes()[:4], blockTd)
|
||||||
glog.V(logger.Info).Infof("Fast block: #%d [%x…] TD=%v", self.currentFastBlock.Number(), self.currentFastBlock.Hash().Bytes()[:4], fastTd)
|
glog.V(logger.Info).Infof("Fast block: #%d [%x…] TD=%v", self.currentFastBlock.Number(), self.currentFastBlock.Hash().Bytes()[:4], fastTd)
|
||||||
|
|
@ -349,7 +357,12 @@ func (self *BlockChain) AuxValidator() pow.PoW { return self.pow }
|
||||||
|
|
||||||
// State returns a new mutable state based on the current HEAD block.
|
// State returns a new mutable state based on the current HEAD block.
|
||||||
func (self *BlockChain) State() (*state.StateDB, error) {
|
func (self *BlockChain) State() (*state.StateDB, error) {
|
||||||
return state.New(self.CurrentBlock().Root(), self.chainDb)
|
return self.StateAt(self.CurrentBlock().Root())
|
||||||
|
}
|
||||||
|
|
||||||
|
// StateAt returns a new mutable state based on a particular point in time.
|
||||||
|
func (self *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
|
||||||
|
return self.stateCache.New(root)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset purges the entire blockchain, restoring it to its genesis state.
|
// Reset purges the entire blockchain, restoring it to its genesis state.
|
||||||
|
|
@ -763,13 +776,20 @@ func (self *BlockChain) WriteBlock(block *types.Block) (status WriteStatus, err
|
||||||
if ptd == nil {
|
if ptd == nil {
|
||||||
return NonStatTy, ParentError(block.ParentHash())
|
return NonStatTy, ParentError(block.ParentHash())
|
||||||
}
|
}
|
||||||
|
// Make sure no inconsistent state is leaked during insertion
|
||||||
|
self.mu.Lock()
|
||||||
|
defer self.mu.Unlock()
|
||||||
|
|
||||||
localTd := self.GetTd(self.currentBlock.Hash())
|
localTd := self.GetTd(self.currentBlock.Hash())
|
||||||
externTd := new(big.Int).Add(block.Difficulty(), ptd)
|
externTd := new(big.Int).Add(block.Difficulty(), ptd)
|
||||||
|
|
||||||
// Make sure no inconsistent state is leaked during insertion
|
// Irrelevant of the canonical status, write the block itself to the database
|
||||||
self.mu.Lock()
|
if err := self.hc.WriteTd(block.Hash(), externTd); err != nil {
|
||||||
defer self.mu.Unlock()
|
glog.Fatalf("failed to write block total difficulty: %v", err)
|
||||||
|
}
|
||||||
|
if err := WriteBlock(self.chainDb, block); err != nil {
|
||||||
|
glog.Fatalf("failed to write block contents: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// If the total difficulty is higher than our known, add it to the canonical chain
|
// If the total difficulty is higher than our known, add it to the canonical chain
|
||||||
// Second clause in the if statement reduces the vulnerability to selfish mining.
|
// Second clause in the if statement reduces the vulnerability to selfish mining.
|
||||||
|
|
@ -782,20 +802,11 @@ func (self *BlockChain) WriteBlock(block *types.Block) (status WriteStatus, err
|
||||||
return NonStatTy, err
|
return NonStatTy, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Insert the block as the new head of the chain
|
self.insert(block) // Insert the block as the new head of the chain
|
||||||
self.insert(block)
|
|
||||||
status = CanonStatTy
|
status = CanonStatTy
|
||||||
} else {
|
} else {
|
||||||
status = SideStatTy
|
status = SideStatTy
|
||||||
}
|
}
|
||||||
// Irrelevant of the canonical status, write the block itself to the database
|
|
||||||
if err := self.hc.WriteTd(block.Hash(), externTd); err != nil {
|
|
||||||
glog.Fatalf("failed to write block total difficulty: %v", err)
|
|
||||||
}
|
|
||||||
if err := WriteBlock(self.chainDb, block); err != nil {
|
|
||||||
glog.Fatalf("failed to write block contents: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.futureBlocks.Remove(block.Hash())
|
self.futureBlocks.Remove(block.Hash())
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
@ -820,7 +831,6 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
tstart = time.Now()
|
tstart = time.Now()
|
||||||
|
|
||||||
nonceChecked = make([]bool, len(chain))
|
nonceChecked = make([]bool, len(chain))
|
||||||
statedb *state.StateDB
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Start the parallel nonce verifier.
|
// Start the parallel nonce verifier.
|
||||||
|
|
@ -887,29 +897,30 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
|
|
||||||
// Create a new statedb using the parent block and report an
|
// Create a new statedb using the parent block and report an
|
||||||
// error if it fails.
|
// error if it fails.
|
||||||
if statedb == nil {
|
switch {
|
||||||
statedb, err = state.New(self.GetBlock(block.ParentHash()).Root(), self.chainDb)
|
case i == 0:
|
||||||
} else {
|
err = self.stateCache.Reset(self.GetBlock(block.ParentHash()).Root())
|
||||||
err = statedb.Reset(chain[i-1].Root())
|
default:
|
||||||
|
err = self.stateCache.Reset(chain[i-1].Root())
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reportBlock(block, err)
|
reportBlock(block, err)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
// Process block using the parent state as reference point.
|
// Process block using the parent state as reference point.
|
||||||
receipts, logs, usedGas, err := self.processor.Process(block, statedb, self.config.VmConfig)
|
receipts, logs, usedGas, err := self.processor.Process(block, self.stateCache, self.config.VmConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reportBlock(block, err)
|
reportBlock(block, err)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
// Validate the state using the default validator
|
// Validate the state using the default validator
|
||||||
err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash()), statedb, receipts, usedGas)
|
err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash()), self.stateCache, receipts, usedGas)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reportBlock(block, err)
|
reportBlock(block, err)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
// Write state changes to database
|
// Write state changes to database
|
||||||
_, err = statedb.Commit()
|
_, err = self.stateCache.Commit()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -712,7 +712,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||||
funds = big.NewInt(1000000000)
|
funds = big.NewInt(1000000000)
|
||||||
genesis = GenesisBlockForTesting(gendb, address, funds)
|
genesis = GenesisBlockForTesting(gendb, address, funds)
|
||||||
)
|
)
|
||||||
blocks, receipts := GenerateChain(genesis, gendb, 1024, func(i int, block *BlockGen) {
|
blocks, receipts := GenerateChain(nil, genesis, gendb, 1024, func(i int, block *BlockGen) {
|
||||||
block.SetCoinbase(common.Address{0x00})
|
block.SetCoinbase(common.Address{0x00})
|
||||||
|
|
||||||
// If the block number is multiple of 3, send a few bonus transactions to the miner
|
// If the block number is multiple of 3, send a few bonus transactions to the miner
|
||||||
|
|
@ -795,7 +795,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||||
genesis = GenesisBlockForTesting(gendb, address, funds)
|
genesis = GenesisBlockForTesting(gendb, address, funds)
|
||||||
)
|
)
|
||||||
height := uint64(1024)
|
height := uint64(1024)
|
||||||
blocks, receipts := GenerateChain(genesis, gendb, int(height), nil)
|
blocks, receipts := GenerateChain(nil, genesis, gendb, int(height), nil)
|
||||||
|
|
||||||
// Configure a subchain to roll back
|
// Configure a subchain to roll back
|
||||||
remove := []common.Hash{}
|
remove := []common.Hash{}
|
||||||
|
|
@ -895,7 +895,7 @@ func TestChainTxReorgs(t *testing.T) {
|
||||||
// - futureAdd: transaction added after the reorg has already finished
|
// - futureAdd: transaction added after the reorg has already finished
|
||||||
var pastAdd, freshAdd, futureAdd *types.Transaction
|
var pastAdd, freshAdd, futureAdd *types.Transaction
|
||||||
|
|
||||||
chain, _ := GenerateChain(genesis, db, 3, func(i int, gen *BlockGen) {
|
chain, _ := GenerateChain(nil, genesis, db, 3, func(i int, gen *BlockGen) {
|
||||||
switch i {
|
switch i {
|
||||||
case 0:
|
case 0:
|
||||||
pastDrop, _ = types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key2)
|
pastDrop, _ = types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key2)
|
||||||
|
|
@ -920,7 +920,7 @@ func TestChainTxReorgs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// overwrite the old chain
|
// overwrite the old chain
|
||||||
chain, _ = GenerateChain(genesis, db, 5, func(i int, gen *BlockGen) {
|
chain, _ = GenerateChain(nil, genesis, db, 5, func(i int, gen *BlockGen) {
|
||||||
switch i {
|
switch i {
|
||||||
case 0:
|
case 0:
|
||||||
pastAdd, _ = types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key3)
|
pastAdd, _ = types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key3)
|
||||||
|
|
@ -990,7 +990,7 @@ func TestLogReorgs(t *testing.T) {
|
||||||
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
|
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
|
||||||
|
|
||||||
subs := evmux.Subscribe(RemovedLogsEvent{})
|
subs := evmux.Subscribe(RemovedLogsEvent{})
|
||||||
chain, _ := GenerateChain(genesis, db, 2, func(i int, gen *BlockGen) {
|
chain, _ := GenerateChain(nil, genesis, db, 2, func(i int, gen *BlockGen) {
|
||||||
if i == 1 {
|
if i == 1 {
|
||||||
tx, err := types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), code).SignECDSA(key1)
|
tx, err := types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), code).SignECDSA(key1)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -1003,7 +1003,7 @@ func TestLogReorgs(t *testing.T) {
|
||||||
t.Fatalf("failed to insert chain: %v", err)
|
t.Fatalf("failed to insert chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
chain, _ = GenerateChain(genesis, db, 3, func(i int, gen *BlockGen) {})
|
chain, _ = GenerateChain(nil, genesis, db, 3, func(i int, gen *BlockGen) {})
|
||||||
if _, err := blockchain.InsertChain(chain); err != nil {
|
if _, err := blockchain.InsertChain(chain); err != nil {
|
||||||
t.Fatalf("failed to insert forked chain: %v", err)
|
t.Fatalf("failed to insert forked chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1025,12 +1025,12 @@ func TestReorgSideEvent(t *testing.T) {
|
||||||
evmux := &event.TypeMux{}
|
evmux := &event.TypeMux{}
|
||||||
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
|
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
|
||||||
|
|
||||||
chain, _ := GenerateChain(genesis, db, 3, func(i int, gen *BlockGen) {})
|
chain, _ := GenerateChain(nil, genesis, db, 3, func(i int, gen *BlockGen) {})
|
||||||
if _, err := blockchain.InsertChain(chain); err != nil {
|
if _, err := blockchain.InsertChain(chain); err != nil {
|
||||||
t.Fatalf("failed to insert chain: %v", err)
|
t.Fatalf("failed to insert chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
replacementBlocks, _ := GenerateChain(genesis, db, 4, func(i int, gen *BlockGen) {
|
replacementBlocks, _ := GenerateChain(nil, genesis, db, 4, func(i int, gen *BlockGen) {
|
||||||
tx, err := types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), nil).SignECDSA(key1)
|
tx, err := types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), nil).SignECDSA(key1)
|
||||||
if i == 2 {
|
if i == 2 {
|
||||||
gen.OffsetTime(-1)
|
gen.OffsetTime(-1)
|
||||||
|
|
@ -1090,3 +1090,41 @@ done:
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests if the canonical block can be fetched from the database during chain insertion.
|
||||||
|
func TestCanonicalBlockRetrieval(t *testing.T) {
|
||||||
|
var (
|
||||||
|
db, _ = ethdb.NewMemDatabase()
|
||||||
|
genesis = WriteGenesisBlockForTesting(db)
|
||||||
|
)
|
||||||
|
|
||||||
|
evmux := &event.TypeMux{}
|
||||||
|
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
|
||||||
|
|
||||||
|
chain, _ := GenerateChain(nil, genesis, db, 10, func(i int, gen *BlockGen) {})
|
||||||
|
|
||||||
|
for i, _ := range chain {
|
||||||
|
go func(block *types.Block) {
|
||||||
|
// try to retrieve a block by its canonical hash and see if the block data can be retrieved.
|
||||||
|
for {
|
||||||
|
ch := GetCanonicalHash(db, block.NumberU64())
|
||||||
|
if ch == (common.Hash{}) {
|
||||||
|
continue // busy wait for canonical hash to be written
|
||||||
|
}
|
||||||
|
if ch != block.Hash() {
|
||||||
|
t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex())
|
||||||
|
}
|
||||||
|
fb := GetBlock(db, ch)
|
||||||
|
if fb == nil {
|
||||||
|
t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
|
||||||
|
}
|
||||||
|
if fb.Hash() != block.Hash() {
|
||||||
|
t.Fatalf("invalid block hash for block %d, want %s, got %s", block.NumberU64(), block.Hash().Hex(), fb.Hash().Hex())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}(chain[i])
|
||||||
|
|
||||||
|
blockchain.InsertChain(types.Blocks{chain[i]})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,13 @@ package core
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/expanse-project/go-expanse/common"
|
"github.com/expanse-project/go-expanse/common"
|
||||||
"github.com/expanse-project/go-expanse/core/state"
|
"github.com/expanse-project/go-expanse/core/state"
|
||||||
"github.com/expanse-project/go-expanse/core/types"
|
"github.com/expanse-project/go-expanse/core/types"
|
||||||
"github.com/expanse-project/go-expanse/core/vm"
|
"github.com/expanse-project/go-expanse/core/vm"
|
||||||
"github.com/expanse-project/go-expanse/ethdb"
|
"github.com/expanse-project/go-expanse/ethdb"
|
||||||
"github.com/expanse-project/go-expanse/event"
|
"github.com/expanse-project/go-expanse/event"
|
||||||
|
"github.com/expanse-project/go-expanse/params"
|
||||||
"github.com/expanse-project/go-expanse/pow"
|
"github.com/expanse-project/go-expanse/pow"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -35,7 +35,11 @@ import (
|
||||||
|
|
||||||
// MakeChainConfig returns a new ChainConfig with the expanse default chain settings.
|
// MakeChainConfig returns a new ChainConfig with the expanse default chain settings.
|
||||||
func MakeChainConfig() *ChainConfig {
|
func MakeChainConfig() *ChainConfig {
|
||||||
return &ChainConfig{HomesteadBlock: big.NewInt(0)}
|
return &ChainConfig{
|
||||||
|
HomesteadBlock: big.NewInt(0),
|
||||||
|
DAOForkBlock: nil,
|
||||||
|
DAOForkSupport: true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FakePow is a non-validating proof of work implementation.
|
// FakePow is a non-validating proof of work implementation.
|
||||||
|
|
@ -126,7 +130,7 @@ func (b *BlockGen) AddUncheckedReceipt(receipt *types.Receipt) {
|
||||||
// TxNonce returns the next valid transaction nonce for the
|
// TxNonce returns the next valid transaction nonce for the
|
||||||
// account at addr. It panics if the account does not exist.
|
// account at addr. It panics if the account does not exist.
|
||||||
func (b *BlockGen) TxNonce(addr common.Address) uint64 {
|
func (b *BlockGen) TxNonce(addr common.Address) uint64 {
|
||||||
if !b.statedb.HasAccount(addr) {
|
if !b.statedb.Exist(addr) {
|
||||||
panic("account does not exist")
|
panic("account does not exist")
|
||||||
}
|
}
|
||||||
return b.statedb.GetNonce(addr)
|
return b.statedb.GetNonce(addr)
|
||||||
|
|
@ -173,10 +177,27 @@ func (b *BlockGen) OffsetTime(seconds int64) {
|
||||||
// Blocks created by GenerateChain do not contain valid proof of work
|
// Blocks created by GenerateChain do not contain valid proof of work
|
||||||
// values. Inserting them into BlockChain requires use of FakePow or
|
// values. Inserting them into BlockChain requires use of FakePow or
|
||||||
// a similar non-validating proof of work implementation.
|
// a similar non-validating proof of work implementation.
|
||||||
func GenerateChain(parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
|
func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
|
||||||
blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n)
|
blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n)
|
||||||
genblock := func(i int, h *types.Header, statedb *state.StateDB) (*types.Block, types.Receipts) {
|
genblock := func(i int, h *types.Header, statedb *state.StateDB) (*types.Block, types.Receipts) {
|
||||||
b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb}
|
b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb}
|
||||||
|
|
||||||
|
// Mutate the state and block according to any hard-fork specs
|
||||||
|
if config == nil {
|
||||||
|
config = MakeChainConfig()
|
||||||
|
}
|
||||||
|
if daoBlock := config.DAOForkBlock; daoBlock != nil {
|
||||||
|
limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange)
|
||||||
|
if h.Number.Cmp(daoBlock) >= 0 && h.Number.Cmp(limit) < 0 {
|
||||||
|
if config.DAOForkSupport {
|
||||||
|
h.Extra = common.CopyBytes(params.DAOForkBlockExtra)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(h.Number) == 0 {
|
||||||
|
ApplyDAOHardFork(statedb)
|
||||||
|
}
|
||||||
|
// Execute any user modifications to the block and finalize it
|
||||||
if gen != nil {
|
if gen != nil {
|
||||||
gen(i, b)
|
gen(i, b)
|
||||||
}
|
}
|
||||||
|
|
@ -261,7 +282,7 @@ func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) [
|
||||||
|
|
||||||
// makeBlockChain creates a deterministic chain of blocks rooted at parent.
|
// makeBlockChain creates a deterministic chain of blocks rooted at parent.
|
||||||
func makeBlockChain(parent *types.Block, n int, db ethdb.Database, seed int) []*types.Block {
|
func makeBlockChain(parent *types.Block, n int, db ethdb.Database, seed int) []*types.Block {
|
||||||
blocks, _ := GenerateChain(parent, db, n, func(i int, b *BlockGen) {
|
blocks, _ := GenerateChain(nil, parent, db, n, func(i int, b *BlockGen) {
|
||||||
b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)})
|
b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)})
|
||||||
})
|
})
|
||||||
return blocks
|
return blocks
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ func ExampleGenerateChain() {
|
||||||
// This call generates a chain of 5 blocks. The function runs for
|
// This call generates a chain of 5 blocks. The function runs for
|
||||||
// each block and adds different features to gen based on the
|
// each block and adds different features to gen based on the
|
||||||
// block index.
|
// block index.
|
||||||
chain, _ := GenerateChain(genesis, db, 5, func(i int, gen *BlockGen) {
|
chain, _ := GenerateChain(nil, genesis, db, 5, func(i int, gen *BlockGen) {
|
||||||
switch i {
|
switch i {
|
||||||
case 0:
|
case 0:
|
||||||
// In block 1, addr1 sends addr2 some expanse.
|
// In block 1, addr1 sends addr2 some expanse.
|
||||||
|
|
@ -79,7 +79,7 @@ func ExampleGenerateChain() {
|
||||||
evmux := &event.TypeMux{}
|
evmux := &event.TypeMux{}
|
||||||
blockchain, _ := NewBlockChain(db, MakeChainConfig(), FakePow{}, evmux)
|
blockchain, _ := NewBlockChain(db, MakeChainConfig(), FakePow{}, evmux)
|
||||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||||
fmt.Printf("insert error (block %d): %v\n", i, err)
|
fmt.Printf("insert error (block %d): %v\n", chain[i].NumberU64(), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ func TestPowVerification(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
testdb, _ = ethdb.NewMemDatabase()
|
testdb, _ = ethdb.NewMemDatabase()
|
||||||
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
|
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
|
||||||
blocks, _ = GenerateChain(genesis, testdb, 8, nil)
|
blocks, _ = GenerateChain(nil, genesis, testdb, 8, nil)
|
||||||
)
|
)
|
||||||
headers := make([]*types.Header, len(blocks))
|
headers := make([]*types.Header, len(blocks))
|
||||||
for i, block := range blocks {
|
for i, block := range blocks {
|
||||||
|
|
@ -115,7 +115,7 @@ func testPowConcurrentVerification(t *testing.T, threads int) {
|
||||||
var (
|
var (
|
||||||
testdb, _ = ethdb.NewMemDatabase()
|
testdb, _ = ethdb.NewMemDatabase()
|
||||||
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
|
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
|
||||||
blocks, _ = GenerateChain(genesis, testdb, 8, nil)
|
blocks, _ = GenerateChain(nil, genesis, testdb, 8, nil)
|
||||||
)
|
)
|
||||||
headers := make([]*types.Header, len(blocks))
|
headers := make([]*types.Header, len(blocks))
|
||||||
for i, block := range blocks {
|
for i, block := range blocks {
|
||||||
|
|
@ -186,7 +186,7 @@ func testPowConcurrentAbortion(t *testing.T, threads int) {
|
||||||
var (
|
var (
|
||||||
testdb, _ = ethdb.NewMemDatabase()
|
testdb, _ = ethdb.NewMemDatabase()
|
||||||
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
|
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
|
||||||
blocks, _ = GenerateChain(genesis, testdb, 1024, nil)
|
blocks, _ = GenerateChain(nil, genesis, testdb, 1024, nil)
|
||||||
)
|
)
|
||||||
headers := make([]*types.Header, len(blocks))
|
headers := make([]*types.Header, len(blocks))
|
||||||
for i, block := range blocks {
|
for i, block := range blocks {
|
||||||
|
|
|
||||||
|
|
@ -31,16 +31,17 @@ var ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general conf
|
||||||
// that any network, identified by its genesis block, can have its own
|
// that any network, identified by its genesis block, can have its own
|
||||||
// set of configuration options.
|
// set of configuration options.
|
||||||
type ChainConfig struct {
|
type ChainConfig struct {
|
||||||
HomesteadBlock *big.Int // homestead switch block
|
HomesteadBlock *big.Int `json:"homesteadBlock"` // Homestead switch block (nil = no fork, 0 = already homestead)
|
||||||
|
DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork switch block (nil = no fork)
|
||||||
|
DAOForkSupport bool `json:"daoForkSupport"` // Whether the nodes supports or opposes the DAO hard-fork
|
||||||
|
|
||||||
VmConfig vm.Config `json:"-"`
|
VmConfig vm.Config `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsHomestead returns whether num is either equal to the homestead block or greater.
|
// IsHomestead returns whether num is either equal to the homestead block or greater.
|
||||||
func (c *ChainConfig) IsHomestead(num *big.Int) bool {
|
func (c *ChainConfig) IsHomestead(num *big.Int) bool {
|
||||||
if num == nil {
|
if c.HomesteadBlock == nil || num == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return num.Cmp(c.HomesteadBlock) >= 0
|
return num.Cmp(c.HomesteadBlock) >= 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
74
core/dao.go
Normal file
74
core/dao.go
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright 2016 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/expanse-project/go-expanse/core/state"
|
||||||
|
"github.com/expanse-project/go-expanse/core/types"
|
||||||
|
"github.com/expanse-project/go-expanse/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidateDAOHeaderExtraData validates the extra-data field of a block header to
|
||||||
|
// ensure it conforms to DAO hard-fork rules.
|
||||||
|
//
|
||||||
|
// DAO hard-fork extension to the header validity:
|
||||||
|
// a) if the node is no-fork, do not accept blocks in the [fork, fork+10) range
|
||||||
|
// with the fork specific extra-data set
|
||||||
|
// b) if the node is pro-fork, require blocks in the specific range to have the
|
||||||
|
// unique extra-data set.
|
||||||
|
func ValidateDAOHeaderExtraData(config *ChainConfig, header *types.Header) error {
|
||||||
|
// Short circuit validation if the node doesn't care about the DAO fork
|
||||||
|
if config.DAOForkBlock == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Make sure the block is within the fork's modified extra-data range
|
||||||
|
limit := new(big.Int).Add(config.DAOForkBlock, params.DAOForkExtraRange)
|
||||||
|
if header.Number.Cmp(config.DAOForkBlock) < 0 || header.Number.Cmp(limit) >= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// Depending whether we support or oppose the fork, validate the extra-data contents
|
||||||
|
if config.DAOForkSupport {
|
||||||
|
if bytes.Compare(header.Extra, params.DAOForkBlockExtra) != 0 {
|
||||||
|
return ValidationError("DAO pro-fork bad block extra-data: 0x%x", header.Extra)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if bytes.Compare(header.Extra, params.DAOForkBlockExtra) == 0 {
|
||||||
|
return ValidationError("DAO no-fork bad block extra-data: 0x%x", header.Extra)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// All ok, header has the same extra-data we expect
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplyDAOHardFork modifies the state database according to the DAO hard-fork
|
||||||
|
// rules, transferring all balances of a set of DAO accounts to a single refund
|
||||||
|
// contract.
|
||||||
|
func ApplyDAOHardFork(statedb *state.StateDB) {
|
||||||
|
// Retrieve the contract to refund balances into
|
||||||
|
refund := statedb.GetOrNewStateObject(params.DAORefundContract)
|
||||||
|
|
||||||
|
// Move every DAO account and extra-balance account funds into the refund contract
|
||||||
|
for _, addr := range params.DAODrainList {
|
||||||
|
if account := statedb.GetStateObject(addr); account != nil {
|
||||||
|
refund.AddBalance(account.Balance())
|
||||||
|
account.SetBalance(new(big.Int))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
132
core/dao_test.go
Normal file
132
core/dao_test.go
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
// Copyright 2016 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/expanse-project/go-expanse/ethdb"
|
||||||
|
"github.com/expanse-project/go-expanse/event"
|
||||||
|
"github.com/expanse-project/go-expanse/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tests that DAO-fork enabled clients can properly filter out fork-commencing
|
||||||
|
// blocks based on their extradata fields.
|
||||||
|
func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
|
forkBlock := big.NewInt(32)
|
||||||
|
|
||||||
|
// Generate a common prefix for both pro-forkers and non-forkers
|
||||||
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
genesis := WriteGenesisBlockForTesting(db)
|
||||||
|
prefix, _ := GenerateChain(nil, genesis, db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
|
||||||
|
|
||||||
|
// Create the concurrent, conflicting two nodes
|
||||||
|
proDb, _ := ethdb.NewMemDatabase()
|
||||||
|
WriteGenesisBlockForTesting(proDb)
|
||||||
|
proConf := &ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: true}
|
||||||
|
proBc, _ := NewBlockChain(proDb, proConf, new(FakePow), new(event.TypeMux))
|
||||||
|
|
||||||
|
conDb, _ := ethdb.NewMemDatabase()
|
||||||
|
WriteGenesisBlockForTesting(conDb)
|
||||||
|
conConf := &ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: false}
|
||||||
|
conBc, _ := NewBlockChain(conDb, conConf, new(FakePow), new(event.TypeMux))
|
||||||
|
|
||||||
|
if _, err := proBc.InsertChain(prefix); err != nil {
|
||||||
|
t.Fatalf("pro-fork: failed to import chain prefix: %v", err)
|
||||||
|
}
|
||||||
|
if _, err := conBc.InsertChain(prefix); err != nil {
|
||||||
|
t.Fatalf("con-fork: failed to import chain prefix: %v", err)
|
||||||
|
}
|
||||||
|
// Try to expand both pro-fork and non-fork chains iteratively with other camp's blocks
|
||||||
|
for i := int64(0); i < params.DAOForkExtraRange.Int64(); i++ {
|
||||||
|
// Create a pro-fork block, and try to feed into the no-fork chain
|
||||||
|
db, _ = ethdb.NewMemDatabase()
|
||||||
|
WriteGenesisBlockForTesting(db)
|
||||||
|
bc, _ := NewBlockChain(db, conConf, new(FakePow), new(event.TypeMux))
|
||||||
|
|
||||||
|
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()+1))
|
||||||
|
for j := 0; j < len(blocks)/2; j++ {
|
||||||
|
blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j]
|
||||||
|
}
|
||||||
|
if _, err := bc.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
|
||||||
|
}
|
||||||
|
blocks, _ = GenerateChain(proConf, conBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
|
||||||
|
if _, err := conBc.InsertChain(blocks); err == nil {
|
||||||
|
t.Fatalf("contra-fork chain accepted pro-fork block: %v", blocks[0])
|
||||||
|
}
|
||||||
|
// Create a proper no-fork block for the contra-forker
|
||||||
|
blocks, _ = GenerateChain(conConf, conBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
|
||||||
|
if _, err := conBc.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("contra-fork chain didn't accepted no-fork block: %v", err)
|
||||||
|
}
|
||||||
|
// Create a no-fork block, and try to feed into the pro-fork chain
|
||||||
|
db, _ = ethdb.NewMemDatabase()
|
||||||
|
WriteGenesisBlockForTesting(db)
|
||||||
|
bc, _ = NewBlockChain(db, proConf, new(FakePow), new(event.TypeMux))
|
||||||
|
|
||||||
|
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()+1))
|
||||||
|
for j := 0; j < len(blocks)/2; j++ {
|
||||||
|
blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j]
|
||||||
|
}
|
||||||
|
if _, err := bc.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
|
||||||
|
}
|
||||||
|
blocks, _ = GenerateChain(conConf, proBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
|
||||||
|
if _, err := proBc.InsertChain(blocks); err == nil {
|
||||||
|
t.Fatalf("pro-fork chain accepted contra-fork block: %v", blocks[0])
|
||||||
|
}
|
||||||
|
// Create a proper pro-fork block for the pro-forker
|
||||||
|
blocks, _ = GenerateChain(proConf, proBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
|
||||||
|
if _, err := proBc.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("pro-fork chain didn't accepted pro-fork block: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes
|
||||||
|
db, _ = ethdb.NewMemDatabase()
|
||||||
|
WriteGenesisBlockForTesting(db)
|
||||||
|
bc, _ := NewBlockChain(db, conConf, new(FakePow), new(event.TypeMux))
|
||||||
|
|
||||||
|
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()+1))
|
||||||
|
for j := 0; j < len(blocks)/2; j++ {
|
||||||
|
blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j]
|
||||||
|
}
|
||||||
|
if _, err := bc.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
|
||||||
|
}
|
||||||
|
blocks, _ = GenerateChain(proConf, conBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
|
||||||
|
if _, err := conBc.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("contra-fork chain didn't accept pro-fork block post-fork: %v", err)
|
||||||
|
}
|
||||||
|
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes
|
||||||
|
db, _ = ethdb.NewMemDatabase()
|
||||||
|
WriteGenesisBlockForTesting(db)
|
||||||
|
bc, _ = NewBlockChain(db, proConf, new(FakePow), new(event.TypeMux))
|
||||||
|
|
||||||
|
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()+1))
|
||||||
|
for j := 0; j < len(blocks)/2; j++ {
|
||||||
|
blocks[j], blocks[len(blocks)-1-j] = blocks[len(blocks)-1-j], blocks[j]
|
||||||
|
}
|
||||||
|
if _, err := bc.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
|
||||||
|
}
|
||||||
|
blocks, _ = GenerateChain(conConf, proBc.CurrentBlock(), db, 1, func(i int, gen *BlockGen) {})
|
||||||
|
if _, err := proBc.InsertChain(blocks); err != nil {
|
||||||
|
t.Fatalf("pro-fork chain didn't accept contra-fork block post-fork: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -157,7 +157,11 @@ func GetTd(db ethdb.Database, hash common.Hash) *big.Int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBlock retrieves an entire block corresponding to the hash, assembling it
|
// GetBlock retrieves an entire block corresponding to the hash, assembling it
|
||||||
// back from the stored header and body.
|
// back from the stored header and body. If either the header or body could not
|
||||||
|
// be retrieved nil is returned.
|
||||||
|
//
|
||||||
|
// Note, due to concurrent download of header and block body the header and thus
|
||||||
|
// canonical hash can be stored in the database but the body data not (yet).
|
||||||
func GetBlock(db ethdb.Database, hash common.Hash) *types.Block {
|
func GetBlock(db ethdb.Database, hash common.Hash) *types.Block {
|
||||||
// Retrieve the block header and body contents
|
// Retrieve the block header and body contents
|
||||||
header := GetHeader(db, hash)
|
header := GetHeader(db, hash)
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ func (d *diffTest) UnmarshalJSON(b []byte) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDifficultyFrontier(t *testing.T) {
|
func TestCalcDifficulty(t *testing.T) {
|
||||||
file, err := os.Open("../tests/files/BasicTests/difficulty.json")
|
file, err := os.Open("../tests/files/BasicTests/difficulty.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -75,9 +75,10 @@ func TestDifficultyFrontier(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config := &ChainConfig{HomesteadBlock: big.NewInt(1150000)}
|
||||||
for name, test := range tests {
|
for name, test := range tests {
|
||||||
number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1))
|
number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1))
|
||||||
diff := calcDifficultyFrontier(test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty)
|
diff := CalcDifficulty(config, test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty)
|
||||||
if diff.Cmp(test.CurrentDifficulty) != 0 {
|
if diff.Cmp(test.CurrentDifficulty) != 0 {
|
||||||
t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff)
|
t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff)
|
||||||
}
|
}
|
||||||
|
|
@ -561,7 +562,7 @@ func TestMipmapChain(t *testing.T) {
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
genesis := WriteGenesisBlockForTesting(db, GenesisAccount{addr, big.NewInt(1000000)})
|
genesis := WriteGenesisBlockForTesting(db, GenesisAccount{addr, big.NewInt(1000000)})
|
||||||
chain, receipts := GenerateChain(genesis, db, 1010, func(i int, gen *BlockGen) {
|
chain, receipts := GenerateChain(nil, genesis, db, 1010, func(i int, gen *BlockGen) {
|
||||||
var receipts types.Receipts
|
var receipts types.Receipts
|
||||||
switch i {
|
switch i {
|
||||||
case 1:
|
case 1:
|
||||||
|
|
|
||||||
|
|
@ -28,14 +28,14 @@ import (
|
||||||
|
|
||||||
// Call executes within the given contract
|
// Call executes within the given contract
|
||||||
func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
|
func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
|
||||||
ret, _, err = exec(env, caller, &addr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value)
|
ret, _, err = exec(env, caller, &addr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value)
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// CallCode executes the given address' code as the given contract address
|
// CallCode executes the given address' code as the given contract address
|
||||||
func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
|
func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
|
||||||
callerAddr := caller.Address()
|
callerAddr := caller.Address()
|
||||||
ret, _, err = exec(env, caller, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value)
|
ret, _, err = exec(env, caller, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value)
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,13 +44,13 @@ func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address
|
||||||
callerAddr := caller.Address()
|
callerAddr := caller.Address()
|
||||||
originAddr := env.Origin()
|
originAddr := env.Origin()
|
||||||
callerValue := caller.Value()
|
callerValue := caller.Value()
|
||||||
ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, callerValue)
|
ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, callerValue)
|
||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates a new contract with the given code
|
// Create creates a new contract with the given code
|
||||||
func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) {
|
func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) {
|
||||||
ret, address, err = exec(env, caller, nil, nil, nil, code, gas, gasPrice, value)
|
ret, address, err = exec(env, caller, nil, nil, crypto.Keccak256Hash(code), nil, code, gas, gasPrice, value)
|
||||||
// Here we get an error if we run into maximum stack depth,
|
// Here we get an error if we run into maximum stack depth,
|
||||||
// See: https://github.com/expanse-project/yellowpaper/pull/131
|
// See: https://github.com/expanse-project/yellowpaper/pull/131
|
||||||
// and YP definitions for CREATE instruction
|
// and YP definitions for CREATE instruction
|
||||||
|
|
@ -60,7 +60,7 @@ func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPric
|
||||||
return ret, address, err
|
return ret, address, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
|
func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
|
||||||
evm := env.Vm()
|
evm := env.Vm()
|
||||||
// Depth check execution. Fail if we're trying to execute above the
|
// Depth check execution. Fail if we're trying to execute above the
|
||||||
// limit.
|
// limit.
|
||||||
|
|
@ -86,7 +86,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
|
||||||
createAccount = true
|
createAccount = true
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshotPreTransfer := env.MakeSnapshot()
|
snapshotPreTransfer := env.SnapshotDatabase()
|
||||||
var (
|
var (
|
||||||
from = env.Db().GetAccount(caller.Address())
|
from = env.Db().GetAccount(caller.Address())
|
||||||
to vm.Account
|
to vm.Account
|
||||||
|
|
@ -106,7 +106,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
|
||||||
// EVM. The contract is a scoped environment for this execution context
|
// EVM. The contract is a scoped environment for this execution context
|
||||||
// only.
|
// only.
|
||||||
contract := vm.NewContract(caller, to, value, gas, gasPrice)
|
contract := vm.NewContract(caller, to, value, gas, gasPrice)
|
||||||
contract.SetCallCode(codeAddr, code)
|
contract.SetCallCode(codeAddr, codeHash, code)
|
||||||
defer contract.Finalise()
|
defer contract.Finalise()
|
||||||
|
|
||||||
ret, err = evm.Run(contract, input)
|
ret, err = evm.Run(contract, input)
|
||||||
|
|
@ -130,13 +130,13 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
|
||||||
if err != nil && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
|
if err != nil && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
|
||||||
contract.UseGas(contract.Gas)
|
contract.UseGas(contract.Gas)
|
||||||
|
|
||||||
env.SetSnapshot(snapshotPreTransfer)
|
env.RevertToSnapshot(snapshotPreTransfer)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret, addr, err
|
return ret, addr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
|
func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
|
||||||
evm := env.Vm()
|
evm := env.Vm()
|
||||||
// Depth check execution. Fail if we're trying to execute above the
|
// Depth check execution. Fail if we're trying to execute above the
|
||||||
// limit.
|
// limit.
|
||||||
|
|
@ -145,7 +145,7 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
|
||||||
return nil, common.Address{}, vm.DepthError
|
return nil, common.Address{}, vm.DepthError
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshot := env.MakeSnapshot()
|
snapshot := env.SnapshotDatabase()
|
||||||
|
|
||||||
var to vm.Account
|
var to vm.Account
|
||||||
if !env.Db().Exist(*toAddr) {
|
if !env.Db().Exist(*toAddr) {
|
||||||
|
|
@ -156,14 +156,14 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
|
||||||
|
|
||||||
// Iinitialise a new contract and make initialise the delegate values
|
// Iinitialise a new contract and make initialise the delegate values
|
||||||
contract := vm.NewContract(caller, to, value, gas, gasPrice).AsDelegate()
|
contract := vm.NewContract(caller, to, value, gas, gasPrice).AsDelegate()
|
||||||
contract.SetCallCode(codeAddr, code)
|
contract.SetCallCode(codeAddr, codeHash, code)
|
||||||
defer contract.Finalise()
|
defer contract.Finalise()
|
||||||
|
|
||||||
ret, err = evm.Run(contract, input)
|
ret, err = evm.Run(contract, input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
contract.UseGas(contract.Gas)
|
contract.UseGas(contract.Gas)
|
||||||
|
|
||||||
env.SetSnapshot(snapshot)
|
env.RevertToSnapshot(snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret, addr, err
|
return ret, addr, err
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,14 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
|
||||||
localTd := hc.GetTd(hc.currentHeaderHash)
|
localTd := hc.GetTd(hc.currentHeaderHash)
|
||||||
externTd := new(big.Int).Add(header.Difficulty, ptd)
|
externTd := new(big.Int).Add(header.Difficulty, ptd)
|
||||||
|
|
||||||
|
// Irrelevant of the canonical status, write the td and header to the database
|
||||||
|
if err := hc.WriteTd(hash, externTd); err != nil {
|
||||||
|
glog.Fatalf("failed to write header total difficulty: %v", err)
|
||||||
|
}
|
||||||
|
if err := WriteHeader(hc.chainDb, header); err != nil {
|
||||||
|
glog.Fatalf("failed to write header contents: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
// If the total difficulty is higher than our known, add it to the canonical chain
|
// If the total difficulty is higher than our known, add it to the canonical chain
|
||||||
// Second clause in the if statement reduces the vulnerability to selfish mining.
|
// Second clause in the if statement reduces the vulnerability to selfish mining.
|
||||||
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
|
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
|
||||||
|
|
@ -150,6 +158,7 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
|
||||||
headHeader = hc.GetHeader(headHash)
|
headHeader = hc.GetHeader(headHash)
|
||||||
headNumber = headHeader.Number.Uint64()
|
headNumber = headHeader.Number.Uint64()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extend the canonical chain with the new header
|
// Extend the canonical chain with the new header
|
||||||
if err := WriteCanonicalHash(hc.chainDb, hash, number); err != nil {
|
if err := WriteCanonicalHash(hc.chainDb, hash, number); err != nil {
|
||||||
glog.Fatalf("failed to insert header number: %v", err)
|
glog.Fatalf("failed to insert header number: %v", err)
|
||||||
|
|
@ -157,19 +166,13 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
|
||||||
if err := WriteHeadHeaderHash(hc.chainDb, hash); err != nil {
|
if err := WriteHeadHeaderHash(hc.chainDb, hash); err != nil {
|
||||||
glog.Fatalf("failed to insert head header hash: %v", err)
|
glog.Fatalf("failed to insert head header hash: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
hc.currentHeaderHash, hc.currentHeader = hash, types.CopyHeader(header)
|
hc.currentHeaderHash, hc.currentHeader = hash, types.CopyHeader(header)
|
||||||
|
|
||||||
status = CanonStatTy
|
status = CanonStatTy
|
||||||
} else {
|
} else {
|
||||||
status = SideStatTy
|
status = SideStatTy
|
||||||
}
|
}
|
||||||
// Irrelevant of the canonical status, write the header itself to the database
|
|
||||||
if err := hc.WriteTd(hash, externTd); err != nil {
|
|
||||||
glog.Fatalf("failed to write header total difficulty: %v", err)
|
|
||||||
}
|
|
||||||
if err := WriteHeader(hc.chainDb, header); err != nil {
|
|
||||||
glog.Fatalf("failed to write header contents: %v", err)
|
|
||||||
}
|
|
||||||
hc.headerCache.Add(hash, header)
|
hc.headerCache.Add(hash, header)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/expanse-project/go-expanse/common"
|
"github.com/expanse-project/go-expanse/common"
|
||||||
|
"github.com/expanse-project/go-expanse/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Account struct {
|
type DumpAccount struct {
|
||||||
Balance string `json:"balance"`
|
Balance string `json:"balance"`
|
||||||
Nonce uint64 `json:"nonce"`
|
Nonce uint64 `json:"nonce"`
|
||||||
Root string `json:"root"`
|
Root string `json:"root"`
|
||||||
|
|
@ -32,40 +33,41 @@ type Account struct {
|
||||||
Storage map[string]string `json:"storage"`
|
Storage map[string]string `json:"storage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type World struct {
|
type Dump struct {
|
||||||
Root string `json:"root"`
|
Root string `json:"root"`
|
||||||
Accounts map[string]Account `json:"accounts"`
|
Accounts map[string]DumpAccount `json:"accounts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) RawDump() World {
|
func (self *StateDB) RawDump() Dump {
|
||||||
world := World{
|
dump := Dump{
|
||||||
Root: common.Bytes2Hex(self.trie.Root()),
|
Root: common.Bytes2Hex(self.trie.Root()),
|
||||||
Accounts: make(map[string]Account),
|
Accounts: make(map[string]DumpAccount),
|
||||||
}
|
}
|
||||||
|
|
||||||
it := self.trie.Iterator()
|
it := self.trie.Iterator()
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
addr := self.trie.GetKey(it.Key)
|
addr := self.trie.GetKey(it.Key)
|
||||||
stateObject, err := DecodeObject(common.BytesToAddress(addr), self.db, it.Value)
|
var data Account
|
||||||
if err != nil {
|
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
account := Account{
|
obj := newObject(nil, common.BytesToAddress(addr), data, nil)
|
||||||
Balance: stateObject.balance.String(),
|
account := DumpAccount{
|
||||||
Nonce: stateObject.nonce,
|
Balance: data.Balance.String(),
|
||||||
Root: common.Bytes2Hex(stateObject.Root()),
|
Nonce: data.Nonce,
|
||||||
CodeHash: common.Bytes2Hex(stateObject.codeHash),
|
Root: common.Bytes2Hex(data.Root[:]),
|
||||||
Code: common.Bytes2Hex(stateObject.Code()),
|
CodeHash: common.Bytes2Hex(data.CodeHash),
|
||||||
|
Code: common.Bytes2Hex(obj.Code(self.db)),
|
||||||
Storage: make(map[string]string),
|
Storage: make(map[string]string),
|
||||||
}
|
}
|
||||||
storageIt := stateObject.trie.Iterator()
|
storageIt := obj.getTrie(self.db).Iterator()
|
||||||
for storageIt.Next() {
|
for storageIt.Next() {
|
||||||
account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value)
|
account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value)
|
||||||
}
|
}
|
||||||
world.Accounts[common.Bytes2Hex(addr)] = account
|
dump.Accounts[common.Bytes2Hex(addr)] = account
|
||||||
}
|
}
|
||||||
return world
|
return dump
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) Dump() []byte {
|
func (self *StateDB) Dump() []byte {
|
||||||
|
|
@ -76,12 +78,3 @@ func (self *StateDB) Dump() []byte {
|
||||||
|
|
||||||
return json
|
return json
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug stuff
|
|
||||||
func (self *StateObject) CreateOutputForDiff() {
|
|
||||||
fmt.Printf("%x %x %x %x\n", self.Address(), self.Root(), self.balance.Bytes(), self.nonce)
|
|
||||||
it := self.trie.Iterator()
|
|
||||||
for it.Next() {
|
|
||||||
fmt.Printf("%x %x\n", it.Key, it.Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ func (it *NodeIterator) step() error {
|
||||||
}
|
}
|
||||||
// Initialize the iterator if we've just started
|
// Initialize the iterator if we've just started
|
||||||
if it.stateIt == nil {
|
if it.stateIt == nil {
|
||||||
it.stateIt = trie.NewNodeIterator(it.state.trie.Trie)
|
it.stateIt = it.state.trie.NodeIterator()
|
||||||
}
|
}
|
||||||
// If we had data nodes previously, we surely have at least state nodes
|
// If we had data nodes previously, we surely have at least state nodes
|
||||||
if it.dataIt != nil {
|
if it.dataIt != nil {
|
||||||
|
|
|
||||||
117
core/state/journal.go
Normal file
117
core/state/journal.go
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
// Copyright 2016 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/expanse-project/go-expanse/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
type journalEntry interface {
|
||||||
|
undo(*StateDB)
|
||||||
|
}
|
||||||
|
|
||||||
|
type journal []journalEntry
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Changes to the account trie.
|
||||||
|
createObjectChange struct {
|
||||||
|
account *common.Address
|
||||||
|
}
|
||||||
|
resetObjectChange struct {
|
||||||
|
prev *StateObject
|
||||||
|
}
|
||||||
|
suicideChange struct {
|
||||||
|
account *common.Address
|
||||||
|
prev bool // whether account had already suicided
|
||||||
|
prevbalance *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changes to individual accounts.
|
||||||
|
balanceChange struct {
|
||||||
|
account *common.Address
|
||||||
|
prev *big.Int
|
||||||
|
}
|
||||||
|
nonceChange struct {
|
||||||
|
account *common.Address
|
||||||
|
prev uint64
|
||||||
|
}
|
||||||
|
storageChange struct {
|
||||||
|
account *common.Address
|
||||||
|
key, prevalue common.Hash
|
||||||
|
}
|
||||||
|
codeChange struct {
|
||||||
|
account *common.Address
|
||||||
|
prevcode, prevhash []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Changes to other state values.
|
||||||
|
refundChange struct {
|
||||||
|
prev *big.Int
|
||||||
|
}
|
||||||
|
addLogChange struct {
|
||||||
|
txhash common.Hash
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (ch createObjectChange) undo(s *StateDB) {
|
||||||
|
s.GetStateObject(*ch.account).deleted = true
|
||||||
|
delete(s.stateObjects, *ch.account)
|
||||||
|
delete(s.stateObjectsDirty, *ch.account)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch resetObjectChange) undo(s *StateDB) {
|
||||||
|
s.setStateObject(ch.prev)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch suicideChange) undo(s *StateDB) {
|
||||||
|
obj := s.GetStateObject(*ch.account)
|
||||||
|
if obj != nil {
|
||||||
|
obj.suicided = ch.prev
|
||||||
|
obj.setBalance(ch.prevbalance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch balanceChange) undo(s *StateDB) {
|
||||||
|
s.GetStateObject(*ch.account).setBalance(ch.prev)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch nonceChange) undo(s *StateDB) {
|
||||||
|
s.GetStateObject(*ch.account).setNonce(ch.prev)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch codeChange) undo(s *StateDB) {
|
||||||
|
s.GetStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch storageChange) undo(s *StateDB) {
|
||||||
|
s.GetStateObject(*ch.account).setState(ch.key, ch.prevalue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch refundChange) undo(s *StateDB) {
|
||||||
|
s.refund = ch.prev
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ch addLogChange) undo(s *StateDB) {
|
||||||
|
logs := s.logs[ch.txhash]
|
||||||
|
if len(logs) == 1 {
|
||||||
|
delete(s.logs, ch.txhash)
|
||||||
|
} else {
|
||||||
|
s.logs[ch.txhash] = logs[:len(logs)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -33,14 +33,14 @@ type ManagedState struct {
|
||||||
|
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
|
||||||
accounts map[string]*account
|
accounts map[common.Address]*account
|
||||||
}
|
}
|
||||||
|
|
||||||
// ManagedState returns a new managed state with the statedb as it's backing layer
|
// ManagedState returns a new managed state with the statedb as it's backing layer
|
||||||
func ManageState(statedb *StateDB) *ManagedState {
|
func ManageState(statedb *StateDB) *ManagedState {
|
||||||
return &ManagedState{
|
return &ManagedState{
|
||||||
StateDB: statedb.Copy(),
|
StateDB: statedb.Copy(),
|
||||||
accounts: make(map[string]*account),
|
accounts: make(map[common.Address]*account),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,7 +103,7 @@ func (ms *ManagedState) SetNonce(addr common.Address, nonce uint64) {
|
||||||
so := ms.GetOrNewStateObject(addr)
|
so := ms.GetOrNewStateObject(addr)
|
||||||
so.SetNonce(nonce)
|
so.SetNonce(nonce)
|
||||||
|
|
||||||
ms.accounts[addr.Str()] = newAccount(so)
|
ms.accounts[addr] = newAccount(so)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasAccount returns whether the given address is managed or not
|
// HasAccount returns whether the given address is managed or not
|
||||||
|
|
@ -114,29 +114,28 @@ func (ms *ManagedState) HasAccount(addr common.Address) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ms *ManagedState) hasAccount(addr common.Address) bool {
|
func (ms *ManagedState) hasAccount(addr common.Address) bool {
|
||||||
_, ok := ms.accounts[addr.Str()]
|
_, ok := ms.accounts[addr]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// populate the managed state
|
// populate the managed state
|
||||||
func (ms *ManagedState) getAccount(addr common.Address) *account {
|
func (ms *ManagedState) getAccount(addr common.Address) *account {
|
||||||
straddr := addr.Str()
|
if account, ok := ms.accounts[addr]; !ok {
|
||||||
if account, ok := ms.accounts[straddr]; !ok {
|
|
||||||
so := ms.GetOrNewStateObject(addr)
|
so := ms.GetOrNewStateObject(addr)
|
||||||
ms.accounts[straddr] = newAccount(so)
|
ms.accounts[addr] = newAccount(so)
|
||||||
} else {
|
} else {
|
||||||
// Always make sure the state account nonce isn't actually higher
|
// Always make sure the state account nonce isn't actually higher
|
||||||
// than the tracked one.
|
// than the tracked one.
|
||||||
so := ms.StateDB.GetStateObject(addr)
|
so := ms.StateDB.GetStateObject(addr)
|
||||||
if so != nil && uint64(len(account.nonces))+account.nstart < so.nonce {
|
if so != nil && uint64(len(account.nonces))+account.nstart < so.Nonce() {
|
||||||
ms.accounts[straddr] = newAccount(so)
|
ms.accounts[addr] = newAccount(so)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ms.accounts[straddr]
|
return ms.accounts[addr]
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAccount(so *StateObject) *account {
|
func newAccount(so *StateObject) *account {
|
||||||
return &account{so, so.nonce, nil}
|
return &account{so, so.Nonce(), nil}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,9 @@ func create() (*ManagedState, *account) {
|
||||||
db, _ := ethdb.NewMemDatabase()
|
db, _ := ethdb.NewMemDatabase()
|
||||||
statedb, _ := New(common.Hash{}, db)
|
statedb, _ := New(common.Hash{}, db)
|
||||||
ms := ManageState(statedb)
|
ms := ManageState(statedb)
|
||||||
so := &StateObject{address: addr, nonce: 100}
|
ms.StateDB.SetNonce(addr, 100)
|
||||||
ms.StateDB.stateObjects[addr.Str()] = so
|
ms.accounts[addr] = newAccount(ms.StateDB.GetStateObject(addr))
|
||||||
ms.accounts[addr.Str()] = newAccount(so)
|
return ms, ms.accounts[addr]
|
||||||
|
|
||||||
return ms, ms.accounts[addr.Str()]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewNonce(t *testing.T) {
|
func TestNewNonce(t *testing.T) {
|
||||||
|
|
@ -92,7 +90,7 @@ func TestRemoteNonceChange(t *testing.T) {
|
||||||
account.nonces = append(account.nonces, nn...)
|
account.nonces = append(account.nonces, nn...)
|
||||||
nonce := ms.NewNonce(addr)
|
nonce := ms.NewNonce(addr)
|
||||||
|
|
||||||
ms.StateDB.stateObjects[addr.Str()].nonce = 200
|
ms.StateDB.stateObjects[addr].data.Nonce = 200
|
||||||
nonce = ms.NewNonce(addr)
|
nonce = ms.NewNonce(addr)
|
||||||
if nonce != 200 {
|
if nonce != 200 {
|
||||||
t.Error("expected nonce after remote update to be", 201, "got", nonce)
|
t.Error("expected nonce after remote update to be", 201, "got", nonce)
|
||||||
|
|
@ -100,7 +98,7 @@ func TestRemoteNonceChange(t *testing.T) {
|
||||||
ms.NewNonce(addr)
|
ms.NewNonce(addr)
|
||||||
ms.NewNonce(addr)
|
ms.NewNonce(addr)
|
||||||
ms.NewNonce(addr)
|
ms.NewNonce(addr)
|
||||||
ms.StateDB.stateObjects[addr.Str()].nonce = 200
|
ms.StateDB.stateObjects[addr].data.Nonce = 200
|
||||||
nonce = ms.NewNonce(addr)
|
nonce = ms.NewNonce(addr)
|
||||||
if nonce != 204 {
|
if nonce != 204 {
|
||||||
t.Error("expected nonce after remote update to be", 201, "got", nonce)
|
t.Error("expected nonce after remote update to be", 201, "got", nonce)
|
||||||
|
|
|
||||||
|
|
@ -57,143 +57,220 @@ func (self Storage) Copy() Storage {
|
||||||
return cpy
|
return cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StateObject represents an Ethereum account which is being modified.
|
||||||
|
//
|
||||||
|
// The usage pattern is as follows:
|
||||||
|
// First you need to obtain a state object.
|
||||||
|
// Account values can be accessed and modified through the object.
|
||||||
|
// Finally, call CommitTrie to write the modified storage trie into a database.
|
||||||
type StateObject struct {
|
type StateObject struct {
|
||||||
db trie.Database // State database for storing state changes
|
address common.Address // Ethereum address of this account
|
||||||
trie *trie.SecureTrie
|
data Account
|
||||||
|
db *StateDB
|
||||||
|
|
||||||
// Address belonging to this account
|
// DB error.
|
||||||
address common.Address
|
// State objects are used by the consensus core and VM which are
|
||||||
// The balance of the account
|
// unable to deal with database-level errors. Any error that occurs
|
||||||
balance *big.Int
|
// during a database read is memoized here and will eventually be returned
|
||||||
// The nonce of the account
|
// by StateDB.Commit.
|
||||||
nonce uint64
|
dbErr error
|
||||||
// The code hash if code is present (i.e. a contract)
|
|
||||||
codeHash []byte
|
|
||||||
// The code for this account
|
|
||||||
code Code
|
|
||||||
// Temporarily initialisation code
|
|
||||||
initCode Code
|
|
||||||
// Cached storage (flushed when updated)
|
|
||||||
storage Storage
|
|
||||||
|
|
||||||
// Mark for deletion
|
// Write caches.
|
||||||
// When an object is marked for deletion it will be delete from the trie
|
trie *trie.SecureTrie // storage trie, which becomes non-nil on first access
|
||||||
// during the "update" phase of the state transition
|
code Code // contract bytecode, which gets set when code is loaded
|
||||||
remove bool
|
|
||||||
deleted bool
|
cachedStorage Storage // Storage entry cache to avoid duplicate reads
|
||||||
dirty bool
|
dirtyStorage Storage // Storage entries that need to be flushed to disk
|
||||||
|
|
||||||
|
// Cache flags.
|
||||||
|
// When an object is marked suicided it will be delete from the trie
|
||||||
|
// during the "update" phase of the state transition.
|
||||||
|
dirtyCode bool // true if the code was updated
|
||||||
|
suicided bool
|
||||||
|
deleted bool
|
||||||
|
onDirty func(addr common.Address) // Callback method to mark a state object newly dirty
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStateObject(address common.Address, db trie.Database) *StateObject {
|
// Account is the Ethereum consensus representation of accounts.
|
||||||
object := &StateObject{
|
// These objects are stored in the main account trie.
|
||||||
db: db,
|
type Account struct {
|
||||||
address: address,
|
Nonce uint64
|
||||||
balance: new(big.Int),
|
Balance *big.Int
|
||||||
dirty: true,
|
Root common.Hash // merkle root of the storage trie
|
||||||
codeHash: emptyCodeHash,
|
CodeHash []byte
|
||||||
storage: make(Storage),
|
}
|
||||||
|
|
||||||
|
// newObject creates a state object.
|
||||||
|
func newObject(db *StateDB, address common.Address, data Account, onDirty func(addr common.Address)) *StateObject {
|
||||||
|
if data.Balance == nil {
|
||||||
|
data.Balance = new(big.Int)
|
||||||
}
|
}
|
||||||
object.trie, _ = trie.NewSecure(common.Hash{}, db)
|
if data.CodeHash == nil {
|
||||||
return object
|
data.CodeHash = emptyCodeHash
|
||||||
|
}
|
||||||
|
return &StateObject{db: db, address: address, data: data, cachedStorage: make(Storage), dirtyStorage: make(Storage), onDirty: onDirty}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateObject) MarkForDeletion() {
|
// EncodeRLP implements rlp.Encoder.
|
||||||
self.remove = true
|
func (c *StateObject) EncodeRLP(w io.Writer) error {
|
||||||
self.dirty = true
|
return rlp.Encode(w, c.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setError remembers the first non-nil error it is called with.
|
||||||
|
func (self *StateObject) setError(err error) {
|
||||||
|
if self.dbErr == nil {
|
||||||
|
self.dbErr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StateObject) markSuicided() {
|
||||||
|
self.suicided = true
|
||||||
|
if self.onDirty != nil {
|
||||||
|
self.onDirty(self.Address())
|
||||||
|
self.onDirty = nil
|
||||||
|
}
|
||||||
if glog.V(logger.Core) {
|
if glog.V(logger.Core) {
|
||||||
glog.Infof("%x: #%d %v X\n", self.Address(), self.nonce, self.balance)
|
glog.Infof("%x: #%d %v X\n", self.Address(), self.Nonce(), self.Balance())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *StateObject) getAddr(addr common.Hash) common.Hash {
|
func (c *StateObject) getTrie(db trie.Database) *trie.SecureTrie {
|
||||||
var ret []byte
|
if c.trie == nil {
|
||||||
rlp.DecodeBytes(c.trie.Get(addr[:]), &ret)
|
var err error
|
||||||
return common.BytesToHash(ret)
|
c.trie, err = trie.NewSecure(c.data.Root, db)
|
||||||
}
|
if err != nil {
|
||||||
|
c.trie, _ = trie.NewSecure(common.Hash{}, db)
|
||||||
func (c *StateObject) setAddr(addr, value common.Hash) {
|
c.setError(fmt.Errorf("can't create storage trie: %v", err))
|
||||||
v, err := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
|
|
||||||
if err != nil {
|
|
||||||
// if RLPing failed we better panic and not fail silently. This would be considered a consensus issue
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
c.trie.Update(addr[:], v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *StateObject) Storage() Storage {
|
|
||||||
return self.storage
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *StateObject) GetState(key common.Hash) common.Hash {
|
|
||||||
value, exists := self.storage[key]
|
|
||||||
if !exists {
|
|
||||||
value = self.getAddr(key)
|
|
||||||
if (value != common.Hash{}) {
|
|
||||||
self.storage[key] = value
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return c.trie
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetState returns a value in account storage.
|
||||||
|
func (self *StateObject) GetState(db trie.Database, key common.Hash) common.Hash {
|
||||||
|
value, exists := self.cachedStorage[key]
|
||||||
|
if exists {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
// Load from DB in case it is missing.
|
||||||
|
if enc := self.getTrie(db).Get(key[:]); len(enc) > 0 {
|
||||||
|
_, content, _, err := rlp.Split(enc)
|
||||||
|
if err != nil {
|
||||||
|
self.setError(err)
|
||||||
|
}
|
||||||
|
value.SetBytes(content)
|
||||||
|
}
|
||||||
|
if (value != common.Hash{}) {
|
||||||
|
self.cachedStorage[key] = value
|
||||||
|
}
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateObject) SetState(key, value common.Hash) {
|
// SetState updates a value in account storage.
|
||||||
self.storage[key] = value
|
func (self *StateObject) SetState(db trie.Database, key, value common.Hash) {
|
||||||
self.dirty = true
|
self.db.journal = append(self.db.journal, storageChange{
|
||||||
|
account: &self.address,
|
||||||
|
key: key,
|
||||||
|
prevalue: self.GetState(db, key),
|
||||||
|
})
|
||||||
|
self.setState(key, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update updates the current cached storage to the trie
|
func (self *StateObject) setState(key, value common.Hash) {
|
||||||
func (self *StateObject) Update() {
|
self.cachedStorage[key] = value
|
||||||
for key, value := range self.storage {
|
self.dirtyStorage[key] = value
|
||||||
if (value == common.Hash{}) {
|
|
||||||
self.trie.Delete(key[:])
|
if self.onDirty != nil {
|
||||||
continue
|
self.onDirty(self.Address())
|
||||||
}
|
self.onDirty = nil
|
||||||
self.setAddr(key, value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateTrie writes cached storage modifications into the object's storage trie.
|
||||||
|
func (self *StateObject) updateTrie(db trie.Database) {
|
||||||
|
tr := self.getTrie(db)
|
||||||
|
for key, value := range self.dirtyStorage {
|
||||||
|
delete(self.dirtyStorage, key)
|
||||||
|
if (value == common.Hash{}) {
|
||||||
|
tr.Delete(key[:])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Encoding []byte cannot fail, ok to ignore the error.
|
||||||
|
v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
|
||||||
|
tr.Update(key[:], v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateRoot sets the trie root to the current root hash of
|
||||||
|
func (self *StateObject) updateRoot(db trie.Database) {
|
||||||
|
self.updateTrie(db)
|
||||||
|
self.data.Root = self.trie.Hash()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommitTrie the storage trie of the object to dwb.
|
||||||
|
// This updates the trie root.
|
||||||
|
func (self *StateObject) CommitTrie(db trie.Database, dbw trie.DatabaseWriter) error {
|
||||||
|
self.updateTrie(db)
|
||||||
|
if self.dbErr != nil {
|
||||||
|
return self.dbErr
|
||||||
|
}
|
||||||
|
root, err := self.trie.CommitTo(dbw)
|
||||||
|
if err == nil {
|
||||||
|
self.data.Root = root
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (c *StateObject) AddBalance(amount *big.Int) {
|
func (c *StateObject) AddBalance(amount *big.Int) {
|
||||||
c.SetBalance(new(big.Int).Add(c.balance, amount))
|
if amount.Cmp(common.Big0) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.SetBalance(new(big.Int).Add(c.Balance(), amount))
|
||||||
|
|
||||||
if glog.V(logger.Core) {
|
if glog.V(logger.Core) {
|
||||||
glog.Infof("%x: #%d %v (+ %v)\n", c.Address(), c.nonce, c.balance, amount)
|
glog.Infof("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce(), c.Balance(), amount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *StateObject) SubBalance(amount *big.Int) {
|
func (c *StateObject) SubBalance(amount *big.Int) {
|
||||||
c.SetBalance(new(big.Int).Sub(c.balance, amount))
|
if amount.Cmp(common.Big0) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.SetBalance(new(big.Int).Sub(c.Balance(), amount))
|
||||||
|
|
||||||
if glog.V(logger.Core) {
|
if glog.V(logger.Core) {
|
||||||
glog.Infof("%x: #%d %v (- %v)\n", c.Address(), c.nonce, c.balance, amount)
|
glog.Infof("%x: #%d %v (- %v)\n", c.Address(), c.Nonce(), c.Balance(), amount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *StateObject) SetBalance(amount *big.Int) {
|
func (self *StateObject) SetBalance(amount *big.Int) {
|
||||||
c.balance = amount
|
self.db.journal = append(self.db.journal, balanceChange{
|
||||||
c.dirty = true
|
account: &self.address,
|
||||||
|
prev: new(big.Int).Set(self.data.Balance),
|
||||||
|
})
|
||||||
|
self.setBalance(amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *StateObject) St() Storage {
|
func (self *StateObject) setBalance(amount *big.Int) {
|
||||||
return c.storage
|
self.data.Balance = amount
|
||||||
|
if self.onDirty != nil {
|
||||||
|
self.onDirty(self.Address())
|
||||||
|
self.onDirty = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the gas back to the origin. Used by the Virtual machine or Closures
|
// Return the gas back to the origin. Used by the Virtual machine or Closures
|
||||||
func (c *StateObject) ReturnGas(gas, price *big.Int) {}
|
func (c *StateObject) ReturnGas(gas, price *big.Int) {}
|
||||||
|
|
||||||
func (self *StateObject) Copy() *StateObject {
|
func (self *StateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *StateObject {
|
||||||
stateObject := NewStateObject(self.Address(), self.db)
|
stateObject := newObject(db, self.address, self.data, onDirty)
|
||||||
stateObject.balance.Set(self.balance)
|
|
||||||
stateObject.codeHash = common.CopyBytes(self.codeHash)
|
|
||||||
stateObject.nonce = self.nonce
|
|
||||||
stateObject.trie = self.trie
|
stateObject.trie = self.trie
|
||||||
stateObject.code = common.CopyBytes(self.code)
|
stateObject.code = self.code
|
||||||
stateObject.initCode = common.CopyBytes(self.initCode)
|
stateObject.dirtyStorage = self.dirtyStorage.Copy()
|
||||||
stateObject.storage = self.storage.Copy()
|
stateObject.cachedStorage = self.dirtyStorage.Copy()
|
||||||
stateObject.remove = self.remove
|
stateObject.suicided = self.suicided
|
||||||
stateObject.dirty = self.dirty
|
stateObject.dirtyCode = self.dirtyCode
|
||||||
stateObject.deleted = self.deleted
|
stateObject.deleted = self.deleted
|
||||||
|
|
||||||
return stateObject
|
return stateObject
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,40 +278,73 @@ func (self *StateObject) Copy() *StateObject {
|
||||||
// Attribute accessors
|
// Attribute accessors
|
||||||
//
|
//
|
||||||
|
|
||||||
func (self *StateObject) Balance() *big.Int {
|
|
||||||
return self.balance
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the address of the contract/account
|
// Returns the address of the contract/account
|
||||||
func (c *StateObject) Address() common.Address {
|
func (c *StateObject) Address() common.Address {
|
||||||
return c.address
|
return c.address
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateObject) Trie() *trie.SecureTrie {
|
// Code returns the contract code associated with this object, if any.
|
||||||
return self.trie
|
func (self *StateObject) Code(db trie.Database) []byte {
|
||||||
}
|
if self.code != nil {
|
||||||
|
return self.code
|
||||||
func (self *StateObject) Root() []byte {
|
}
|
||||||
return self.trie.Root()
|
if bytes.Equal(self.CodeHash(), emptyCodeHash) {
|
||||||
}
|
return nil
|
||||||
|
}
|
||||||
func (self *StateObject) Code() []byte {
|
code, err := db.Get(self.CodeHash())
|
||||||
return self.code
|
if err != nil {
|
||||||
}
|
self.setError(fmt.Errorf("can't load code hash %x: %v", self.CodeHash(), err))
|
||||||
|
}
|
||||||
func (self *StateObject) SetCode(code []byte) {
|
|
||||||
self.code = code
|
self.code = code
|
||||||
self.codeHash = crypto.Keccak256(code)
|
return code
|
||||||
self.dirty = true
|
}
|
||||||
|
|
||||||
|
func (self *StateObject) SetCode(codeHash common.Hash, code []byte) {
|
||||||
|
prevcode := self.Code(self.db.db)
|
||||||
|
self.db.journal = append(self.db.journal, codeChange{
|
||||||
|
account: &self.address,
|
||||||
|
prevhash: self.CodeHash(),
|
||||||
|
prevcode: prevcode,
|
||||||
|
})
|
||||||
|
self.setCode(codeHash, code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StateObject) setCode(codeHash common.Hash, code []byte) {
|
||||||
|
self.code = code
|
||||||
|
self.data.CodeHash = codeHash[:]
|
||||||
|
self.dirtyCode = true
|
||||||
|
if self.onDirty != nil {
|
||||||
|
self.onDirty(self.Address())
|
||||||
|
self.onDirty = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateObject) SetNonce(nonce uint64) {
|
func (self *StateObject) SetNonce(nonce uint64) {
|
||||||
self.nonce = nonce
|
self.db.journal = append(self.db.journal, nonceChange{
|
||||||
self.dirty = true
|
account: &self.address,
|
||||||
|
prev: self.data.Nonce,
|
||||||
|
})
|
||||||
|
self.setNonce(nonce)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StateObject) setNonce(nonce uint64) {
|
||||||
|
self.data.Nonce = nonce
|
||||||
|
if self.onDirty != nil {
|
||||||
|
self.onDirty(self.Address())
|
||||||
|
self.onDirty = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StateObject) CodeHash() []byte {
|
||||||
|
return self.data.CodeHash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StateObject) Balance() *big.Int {
|
||||||
|
return self.data.Balance
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateObject) Nonce() uint64 {
|
func (self *StateObject) Nonce() uint64 {
|
||||||
return self.nonce
|
return self.data.Nonce
|
||||||
}
|
}
|
||||||
|
|
||||||
// Never called, but must be present to allow StateObject to be used
|
// Never called, but must be present to allow StateObject to be used
|
||||||
|
|
@ -246,52 +356,16 @@ func (self *StateObject) Value() *big.Int {
|
||||||
|
|
||||||
func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) {
|
func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) {
|
||||||
// When iterating over the storage check the cache first
|
// When iterating over the storage check the cache first
|
||||||
for h, value := range self.storage {
|
for h, value := range self.cachedStorage {
|
||||||
cb(h, value)
|
cb(h, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
it := self.trie.Iterator()
|
it := self.getTrie(self.db.db).Iterator()
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
// ignore cached values
|
// ignore cached values
|
||||||
key := common.BytesToHash(self.trie.GetKey(it.Key))
|
key := common.BytesToHash(self.trie.GetKey(it.Key))
|
||||||
if _, ok := self.storage[key]; !ok {
|
if _, ok := self.cachedStorage[key]; !ok {
|
||||||
cb(key, common.BytesToHash(it.Value))
|
cb(key, common.BytesToHash(it.Value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type extStateObject struct {
|
|
||||||
Nonce uint64
|
|
||||||
Balance *big.Int
|
|
||||||
Root common.Hash
|
|
||||||
CodeHash []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeRLP implements rlp.Encoder.
|
|
||||||
func (c *StateObject) EncodeRLP(w io.Writer) error {
|
|
||||||
return rlp.Encode(w, []interface{}{c.nonce, c.balance, c.Root(), c.codeHash})
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeObject decodes an RLP-encoded state object.
|
|
||||||
func DecodeObject(address common.Address, db trie.Database, data []byte) (*StateObject, error) {
|
|
||||||
var (
|
|
||||||
obj = &StateObject{address: address, db: db, storage: make(Storage)}
|
|
||||||
ext extStateObject
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
if err = rlp.DecodeBytes(data, &ext); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if obj.trie, err = trie.NewSecure(ext.Root, db); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !bytes.Equal(ext.CodeHash, emptyCodeHash) {
|
|
||||||
if obj.code, err = db.Get(ext.CodeHash); err != nil {
|
|
||||||
return nil, fmt.Errorf("can't get code for hash %x: %v", ext.CodeHash, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
obj.nonce = ext.Nonce
|
|
||||||
obj.balance = ext.Balance
|
|
||||||
obj.codeHash = ext.CodeHash
|
|
||||||
return obj, nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
checker "gopkg.in/check.v1"
|
checker "gopkg.in/check.v1"
|
||||||
|
|
||||||
"github.com/expanse-project/go-expanse/common"
|
"github.com/expanse-project/go-expanse/common"
|
||||||
|
"github.com/expanse-project/go-expanse/crypto"
|
||||||
"github.com/expanse-project/go-expanse/ethdb"
|
"github.com/expanse-project/go-expanse/ethdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -40,13 +41,13 @@ func (s *StateSuite) TestDump(c *checker.C) {
|
||||||
obj1 := s.state.GetOrNewStateObject(toAddr([]byte{0x01}))
|
obj1 := s.state.GetOrNewStateObject(toAddr([]byte{0x01}))
|
||||||
obj1.AddBalance(big.NewInt(22))
|
obj1.AddBalance(big.NewInt(22))
|
||||||
obj2 := s.state.GetOrNewStateObject(toAddr([]byte{0x01, 0x02}))
|
obj2 := s.state.GetOrNewStateObject(toAddr([]byte{0x01, 0x02}))
|
||||||
obj2.SetCode([]byte{3, 3, 3, 3, 3, 3, 3})
|
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
|
||||||
obj3 := s.state.GetOrNewStateObject(toAddr([]byte{0x02}))
|
obj3 := s.state.GetOrNewStateObject(toAddr([]byte{0x02}))
|
||||||
obj3.SetBalance(big.NewInt(44))
|
obj3.SetBalance(big.NewInt(44))
|
||||||
|
|
||||||
// write some of them to the trie
|
// write some of them to the trie
|
||||||
s.state.UpdateStateObject(obj1)
|
s.state.updateStateObject(obj1)
|
||||||
s.state.UpdateStateObject(obj2)
|
s.state.updateStateObject(obj2)
|
||||||
s.state.Commit()
|
s.state.Commit()
|
||||||
|
|
||||||
// check that dump contains the state objects that are in trie
|
// check that dump contains the state objects that are in trie
|
||||||
|
|
@ -115,12 +116,12 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
|
||||||
// set initial state object value
|
// set initial state object value
|
||||||
s.state.SetState(stateobjaddr, storageaddr, data1)
|
s.state.SetState(stateobjaddr, storageaddr, data1)
|
||||||
// get snapshot of current state
|
// get snapshot of current state
|
||||||
snapshot := s.state.Copy()
|
snapshot := s.state.Snapshot()
|
||||||
|
|
||||||
// set new state object value
|
// set new state object value
|
||||||
s.state.SetState(stateobjaddr, storageaddr, data2)
|
s.state.SetState(stateobjaddr, storageaddr, data2)
|
||||||
// restore snapshot
|
// restore snapshot
|
||||||
s.state.Set(snapshot)
|
s.state.RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
// get state storage value
|
// get state storage value
|
||||||
res := s.state.GetState(stateobjaddr, storageaddr)
|
res := s.state.GetState(stateobjaddr, storageaddr)
|
||||||
|
|
@ -128,6 +129,12 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
|
||||||
c.Assert(data1, checker.DeepEquals, res)
|
c.Assert(data1, checker.DeepEquals, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSnapshotEmpty(t *testing.T) {
|
||||||
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
state, _ := New(common.Hash{}, db)
|
||||||
|
state.RevertToSnapshot(state.Snapshot())
|
||||||
|
}
|
||||||
|
|
||||||
// use testing instead of checker because checker does not support
|
// use testing instead of checker because checker does not support
|
||||||
// printing/logging in tests (-check.vv does not work)
|
// printing/logging in tests (-check.vv does not work)
|
||||||
func TestSnapshot2(t *testing.T) {
|
func TestSnapshot2(t *testing.T) {
|
||||||
|
|
@ -146,80 +153,85 @@ func TestSnapshot2(t *testing.T) {
|
||||||
|
|
||||||
// db, trie are already non-empty values
|
// db, trie are already non-empty values
|
||||||
so0 := state.GetStateObject(stateobjaddr0)
|
so0 := state.GetStateObject(stateobjaddr0)
|
||||||
so0.balance = big.NewInt(42)
|
so0.SetBalance(big.NewInt(42))
|
||||||
so0.nonce = 43
|
so0.SetNonce(43)
|
||||||
so0.SetCode([]byte{'c', 'a', 'f', 'e'})
|
so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'})
|
||||||
so0.remove = true
|
so0.suicided = false
|
||||||
so0.deleted = false
|
so0.deleted = false
|
||||||
so0.dirty = false
|
state.setStateObject(so0)
|
||||||
state.SetStateObject(so0)
|
|
||||||
|
root, _ := state.Commit()
|
||||||
|
state.Reset(root)
|
||||||
|
|
||||||
// and one with deleted == true
|
// and one with deleted == true
|
||||||
so1 := state.GetStateObject(stateobjaddr1)
|
so1 := state.GetStateObject(stateobjaddr1)
|
||||||
so1.balance = big.NewInt(52)
|
so1.SetBalance(big.NewInt(52))
|
||||||
so1.nonce = 53
|
so1.SetNonce(53)
|
||||||
so1.SetCode([]byte{'c', 'a', 'f', 'e', '2'})
|
so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'})
|
||||||
so1.remove = true
|
so1.suicided = true
|
||||||
so1.deleted = true
|
so1.deleted = true
|
||||||
so1.dirty = true
|
state.setStateObject(so1)
|
||||||
state.SetStateObject(so1)
|
|
||||||
|
|
||||||
so1 = state.GetStateObject(stateobjaddr1)
|
so1 = state.GetStateObject(stateobjaddr1)
|
||||||
if so1 != nil {
|
if so1 != nil {
|
||||||
t.Fatalf("deleted object not nil when getting")
|
t.Fatalf("deleted object not nil when getting")
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshot := state.Copy()
|
snapshot := state.Snapshot()
|
||||||
state.Set(snapshot)
|
state.RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
so0Restored := state.GetStateObject(stateobjaddr0)
|
so0Restored := state.GetStateObject(stateobjaddr0)
|
||||||
so1Restored := state.GetStateObject(stateobjaddr1)
|
// Update lazily-loaded values before comparing.
|
||||||
|
so0Restored.GetState(db, storageaddr)
|
||||||
|
so0Restored.Code(db)
|
||||||
// non-deleted is equal (restored)
|
// non-deleted is equal (restored)
|
||||||
compareStateObjects(so0Restored, so0, t)
|
compareStateObjects(so0Restored, so0, t)
|
||||||
|
|
||||||
// deleted should be nil, both before and after restore of state copy
|
// deleted should be nil, both before and after restore of state copy
|
||||||
|
so1Restored := state.GetStateObject(stateobjaddr1)
|
||||||
if so1Restored != nil {
|
if so1Restored != nil {
|
||||||
t.Fatalf("deleted object not nil after restoring snapshot")
|
t.Fatalf("deleted object not nil after restoring snapshot: %+v", so1Restored)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareStateObjects(so0, so1 *StateObject, t *testing.T) {
|
func compareStateObjects(so0, so1 *StateObject, t *testing.T) {
|
||||||
if so0.address != so1.address {
|
if so0.Address() != so1.Address() {
|
||||||
t.Fatalf("Address mismatch: have %v, want %v", so0.address, so1.address)
|
t.Fatalf("Address mismatch: have %v, want %v", so0.address, so1.address)
|
||||||
}
|
}
|
||||||
if so0.balance.Cmp(so1.balance) != 0 {
|
if so0.Balance().Cmp(so1.Balance()) != 0 {
|
||||||
t.Fatalf("Balance mismatch: have %v, want %v", so0.balance, so1.balance)
|
t.Fatalf("Balance mismatch: have %v, want %v", so0.Balance(), so1.Balance())
|
||||||
}
|
}
|
||||||
if so0.nonce != so1.nonce {
|
if so0.Nonce() != so1.Nonce() {
|
||||||
t.Fatalf("Nonce mismatch: have %v, want %v", so0.nonce, so1.nonce)
|
t.Fatalf("Nonce mismatch: have %v, want %v", so0.Nonce(), so1.Nonce())
|
||||||
}
|
}
|
||||||
if !bytes.Equal(so0.codeHash, so1.codeHash) {
|
if so0.data.Root != so1.data.Root {
|
||||||
t.Fatalf("CodeHash mismatch: have %v, want %v", so0.codeHash, so1.codeHash)
|
t.Errorf("Root mismatch: have %x, want %x", so0.data.Root[:], so1.data.Root[:])
|
||||||
|
}
|
||||||
|
if !bytes.Equal(so0.CodeHash(), so1.CodeHash()) {
|
||||||
|
t.Fatalf("CodeHash mismatch: have %v, want %v", so0.CodeHash(), so1.CodeHash())
|
||||||
}
|
}
|
||||||
if !bytes.Equal(so0.code, so1.code) {
|
if !bytes.Equal(so0.code, so1.code) {
|
||||||
t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code)
|
t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(so0.initCode, so1.initCode) {
|
|
||||||
t.Fatalf("InitCode mismatch: have %v, want %v", so0.initCode, so1.initCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range so1.storage {
|
if len(so1.cachedStorage) != len(so0.cachedStorage) {
|
||||||
if so0.storage[k] != v {
|
t.Errorf("Storage size mismatch: have %d, want %d", len(so1.cachedStorage), len(so0.cachedStorage))
|
||||||
t.Fatalf("Storage key %s mismatch: have %v, want %v", k, so0.storage[k], v)
|
}
|
||||||
|
for k, v := range so1.cachedStorage {
|
||||||
|
if so0.cachedStorage[k] != v {
|
||||||
|
t.Errorf("Storage key %x mismatch: have %v, want %v", k, so0.cachedStorage[k], v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for k, v := range so0.storage {
|
for k, v := range so0.cachedStorage {
|
||||||
if so1.storage[k] != v {
|
if so1.cachedStorage[k] != v {
|
||||||
t.Fatalf("Storage key %s mismatch: have %v, want none.", k, v)
|
t.Errorf("Storage key %x mismatch: have %v, want none.", k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if so0.remove != so1.remove {
|
if so0.suicided != so1.suicided {
|
||||||
t.Fatalf("Remove mismatch: have %v, want %v", so0.remove, so1.remove)
|
t.Fatalf("suicided mismatch: have %v, want %v", so0.suicided, so1.suicided)
|
||||||
}
|
}
|
||||||
if so0.deleted != so1.deleted {
|
if so0.deleted != so1.deleted {
|
||||||
t.Fatalf("Deleted mismatch: have %v, want %v", so0.deleted, so1.deleted)
|
t.Fatalf("Deleted mismatch: have %v, want %v", so0.deleted, so1.deleted)
|
||||||
}
|
}
|
||||||
if so0.dirty != so1.dirty {
|
|
||||||
t.Fatalf("Dirty mismatch: have %v, want %v", so0.dirty, so1.dirty)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,38 +20,68 @@ package state
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/expanse-project/go-expanse/common"
|
"github.com/expanse-project/go-expanse/common"
|
||||||
"github.com/expanse-project/go-expanse/core/vm"
|
"github.com/expanse-project/go-expanse/core/vm"
|
||||||
|
"github.com/expanse-project/go-expanse/crypto"
|
||||||
"github.com/expanse-project/go-expanse/ethdb"
|
"github.com/expanse-project/go-expanse/ethdb"
|
||||||
"github.com/expanse-project/go-expanse/logger"
|
"github.com/expanse-project/go-expanse/logger"
|
||||||
"github.com/expanse-project/go-expanse/logger/glog"
|
"github.com/expanse-project/go-expanse/logger/glog"
|
||||||
"github.com/expanse-project/go-expanse/rlp"
|
"github.com/expanse-project/go-expanse/rlp"
|
||||||
"github.com/expanse-project/go-expanse/trie"
|
"github.com/expanse-project/go-expanse/trie"
|
||||||
|
lru "github.com/hashicorp/golang-lru"
|
||||||
)
|
)
|
||||||
|
|
||||||
// The starting nonce determines the default nonce when new accounts are being
|
// The starting nonce determines the default nonce when new accounts are being
|
||||||
// created.
|
// created.
|
||||||
var StartingNonce uint64
|
var StartingNonce uint64
|
||||||
|
|
||||||
// StateDBs within the expanse protocol are used to store anything
|
const (
|
||||||
|
// Number of past tries to keep. The arbitrarily chosen value here
|
||||||
|
// is max uncle depth + 1.
|
||||||
|
maxTrieCacheLength = 8
|
||||||
|
|
||||||
|
// Number of codehash->size associations to keep.
|
||||||
|
codeSizeCacheSize = 100000
|
||||||
|
)
|
||||||
|
|
||||||
|
type revision struct {
|
||||||
|
id int
|
||||||
|
journalIndex int
|
||||||
|
}
|
||||||
|
|
||||||
|
// StateDBs within the ethereum protocol are used to store anything
|
||||||
// within the merkle trie. StateDBs take care of caching and storing
|
// within the merkle trie. StateDBs take care of caching and storing
|
||||||
// nested states. It's the general query interface to retrieve:
|
// nested states. It's the general query interface to retrieve:
|
||||||
// * Contracts
|
// * Contracts
|
||||||
// * Accounts
|
// * Accounts
|
||||||
type StateDB struct {
|
type StateDB struct {
|
||||||
db ethdb.Database
|
db ethdb.Database
|
||||||
trie *trie.SecureTrie
|
trie *trie.SecureTrie
|
||||||
|
pastTries []*trie.SecureTrie
|
||||||
|
codeSizeCache *lru.Cache
|
||||||
|
|
||||||
stateObjects map[string]*StateObject
|
// This map holds 'live' objects, which will get modified while processing a state transition.
|
||||||
|
stateObjects map[common.Address]*StateObject
|
||||||
|
stateObjectsDirty map[common.Address]struct{}
|
||||||
|
|
||||||
|
// The refund counter, also used by state transitioning.
|
||||||
refund *big.Int
|
refund *big.Int
|
||||||
|
|
||||||
thash, bhash common.Hash
|
thash, bhash common.Hash
|
||||||
txIndex int
|
txIndex int
|
||||||
logs map[common.Hash]vm.Logs
|
logs map[common.Hash]vm.Logs
|
||||||
logSize uint
|
logSize uint
|
||||||
|
|
||||||
|
// Journal of state modifications. This is the backbone of
|
||||||
|
// Snapshot and RevertToSnapshot.
|
||||||
|
journal journal
|
||||||
|
validRevisions []revision
|
||||||
|
nextRevisionId int
|
||||||
|
|
||||||
|
lock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new state from a given trie
|
// Create a new state from a given trie
|
||||||
|
|
@ -60,35 +90,84 @@ func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
csc, _ := lru.New(codeSizeCacheSize)
|
||||||
return &StateDB{
|
return &StateDB{
|
||||||
db: db,
|
db: db,
|
||||||
trie: tr,
|
trie: tr,
|
||||||
stateObjects: make(map[string]*StateObject),
|
codeSizeCache: csc,
|
||||||
refund: new(big.Int),
|
stateObjects: make(map[common.Address]*StateObject),
|
||||||
logs: make(map[common.Hash]vm.Logs),
|
stateObjectsDirty: make(map[common.Address]struct{}),
|
||||||
|
refund: new(big.Int),
|
||||||
|
logs: make(map[common.Hash]vm.Logs),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new statedb by reusing any journalled tries to avoid costly
|
||||||
|
// disk io.
|
||||||
|
func (self *StateDB) New(root common.Hash) (*StateDB, error) {
|
||||||
|
self.lock.Lock()
|
||||||
|
defer self.lock.Unlock()
|
||||||
|
|
||||||
|
tr, err := self.openTrie(root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &StateDB{
|
||||||
|
db: self.db,
|
||||||
|
trie: tr,
|
||||||
|
codeSizeCache: self.codeSizeCache,
|
||||||
|
stateObjects: make(map[common.Address]*StateObject),
|
||||||
|
stateObjectsDirty: make(map[common.Address]struct{}),
|
||||||
|
refund: new(big.Int),
|
||||||
|
logs: make(map[common.Hash]vm.Logs),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset clears out all emphemeral state objects from the state db, but keeps
|
// Reset clears out all emphemeral state objects from the state db, but keeps
|
||||||
// the underlying state trie to avoid reloading data for the next operations.
|
// the underlying state trie to avoid reloading data for the next operations.
|
||||||
func (self *StateDB) Reset(root common.Hash) error {
|
func (self *StateDB) Reset(root common.Hash) error {
|
||||||
var (
|
self.lock.Lock()
|
||||||
err error
|
defer self.lock.Unlock()
|
||||||
tr = self.trie
|
|
||||||
)
|
tr, err := self.openTrie(root)
|
||||||
if self.trie.Hash() != root {
|
if err != nil {
|
||||||
if tr, err = trie.NewSecure(root, self.db); err != nil {
|
return err
|
||||||
return err
|
}
|
||||||
|
self.trie = tr
|
||||||
|
self.stateObjects = make(map[common.Address]*StateObject)
|
||||||
|
self.stateObjectsDirty = make(map[common.Address]struct{})
|
||||||
|
self.thash = common.Hash{}
|
||||||
|
self.bhash = common.Hash{}
|
||||||
|
self.txIndex = 0
|
||||||
|
self.logs = make(map[common.Hash]vm.Logs)
|
||||||
|
self.logSize = 0
|
||||||
|
self.clearJournalAndRefund()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// openTrie creates a trie. It uses an existing trie if one is available
|
||||||
|
// from the journal if available.
|
||||||
|
func (self *StateDB) openTrie(root common.Hash) (*trie.SecureTrie, error) {
|
||||||
|
for i := len(self.pastTries) - 1; i >= 0; i-- {
|
||||||
|
if self.pastTries[i].Hash() == root {
|
||||||
|
tr := *self.pastTries[i]
|
||||||
|
return &tr, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*self = StateDB{
|
return trie.NewSecure(root, self.db)
|
||||||
db: self.db,
|
}
|
||||||
trie: tr,
|
|
||||||
stateObjects: make(map[string]*StateObject),
|
func (self *StateDB) pushTrie(t *trie.SecureTrie) {
|
||||||
refund: new(big.Int),
|
self.lock.Lock()
|
||||||
logs: make(map[common.Hash]vm.Logs),
|
defer self.lock.Unlock()
|
||||||
|
|
||||||
|
if len(self.pastTries) >= maxTrieCacheLength {
|
||||||
|
copy(self.pastTries, self.pastTries[1:])
|
||||||
|
self.pastTries[len(self.pastTries)-1] = t
|
||||||
|
} else {
|
||||||
|
self.pastTries = append(self.pastTries, t)
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) {
|
func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) {
|
||||||
|
|
@ -98,6 +177,8 @@ func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) AddLog(log *vm.Log) {
|
func (self *StateDB) AddLog(log *vm.Log) {
|
||||||
|
self.journal = append(self.journal, addLogChange{txhash: self.thash})
|
||||||
|
|
||||||
log.TxHash = self.thash
|
log.TxHash = self.thash
|
||||||
log.BlockHash = self.bhash
|
log.BlockHash = self.bhash
|
||||||
log.TxIndex = uint(self.txIndex)
|
log.TxIndex = uint(self.txIndex)
|
||||||
|
|
@ -119,13 +200,12 @@ func (self *StateDB) Logs() vm.Logs {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) AddRefund(gas *big.Int) {
|
func (self *StateDB) AddRefund(gas *big.Int) {
|
||||||
|
self.journal = append(self.journal, refundChange{prev: new(big.Int).Set(self.refund)})
|
||||||
self.refund.Add(self.refund, gas)
|
self.refund.Add(self.refund, gas)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) HasAccount(addr common.Address) bool {
|
// Exist reports whether the given account address exists in the state.
|
||||||
return self.GetStateObject(addr) != nil
|
// Notably this also returns true for suicided accounts.
|
||||||
}
|
|
||||||
|
|
||||||
func (self *StateDB) Exist(addr common.Address) bool {
|
func (self *StateDB) Exist(addr common.Address) bool {
|
||||||
return self.GetStateObject(addr) != nil
|
return self.GetStateObject(addr) != nil
|
||||||
}
|
}
|
||||||
|
|
@ -138,16 +218,15 @@ func (self *StateDB) GetAccount(addr common.Address) vm.Account {
|
||||||
func (self *StateDB) GetBalance(addr common.Address) *big.Int {
|
func (self *StateDB) GetBalance(addr common.Address) *big.Int {
|
||||||
stateObject := self.GetStateObject(addr)
|
stateObject := self.GetStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.balance
|
return stateObject.Balance()
|
||||||
}
|
}
|
||||||
|
|
||||||
return common.Big0
|
return common.Big0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) GetNonce(addr common.Address) uint64 {
|
func (self *StateDB) GetNonce(addr common.Address) uint64 {
|
||||||
stateObject := self.GetStateObject(addr)
|
stateObject := self.GetStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.nonce
|
return stateObject.Nonce()
|
||||||
}
|
}
|
||||||
|
|
||||||
return StartingNonce
|
return StartingNonce
|
||||||
|
|
@ -156,25 +235,50 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 {
|
||||||
func (self *StateDB) GetCode(addr common.Address) []byte {
|
func (self *StateDB) GetCode(addr common.Address) []byte {
|
||||||
stateObject := self.GetStateObject(addr)
|
stateObject := self.GetStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.code
|
code := stateObject.Code(self.db)
|
||||||
|
key := common.BytesToHash(stateObject.CodeHash())
|
||||||
|
self.codeSizeCache.Add(key, len(code))
|
||||||
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *StateDB) GetCodeSize(addr common.Address) int {
|
||||||
|
stateObject := self.GetStateObject(addr)
|
||||||
|
if stateObject == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
key := common.BytesToHash(stateObject.CodeHash())
|
||||||
|
if cached, ok := self.codeSizeCache.Get(key); ok {
|
||||||
|
return cached.(int)
|
||||||
|
}
|
||||||
|
size := len(stateObject.Code(self.db))
|
||||||
|
if stateObject.dbErr == nil {
|
||||||
|
self.codeSizeCache.Add(key, size)
|
||||||
|
}
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *StateDB) GetCodeHash(addr common.Address) common.Hash {
|
||||||
|
stateObject := self.GetStateObject(addr)
|
||||||
|
if stateObject == nil {
|
||||||
|
return common.Hash{}
|
||||||
|
}
|
||||||
|
return common.BytesToHash(stateObject.CodeHash())
|
||||||
|
}
|
||||||
|
|
||||||
func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
|
func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
|
||||||
stateObject := self.GetStateObject(a)
|
stateObject := self.GetStateObject(a)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.GetState(b)
|
return stateObject.GetState(self.db, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
return common.Hash{}
|
return common.Hash{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) IsDeleted(addr common.Address) bool {
|
func (self *StateDB) HasSuicided(addr common.Address) bool {
|
||||||
stateObject := self.GetStateObject(addr)
|
stateObject := self.GetStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
return stateObject.remove
|
return stateObject.suicided
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -190,6 +294,13 @@ func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) {
|
||||||
|
stateObject := self.GetOrNewStateObject(addr)
|
||||||
|
if stateObject != nil {
|
||||||
|
stateObject.SetBalance(amount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (self *StateDB) SetNonce(addr common.Address, nonce uint64) {
|
func (self *StateDB) SetNonce(addr common.Address, nonce uint64) {
|
||||||
stateObject := self.GetOrNewStateObject(addr)
|
stateObject := self.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
|
|
@ -200,35 +311,43 @@ func (self *StateDB) SetNonce(addr common.Address, nonce uint64) {
|
||||||
func (self *StateDB) SetCode(addr common.Address, code []byte) {
|
func (self *StateDB) SetCode(addr common.Address, code []byte) {
|
||||||
stateObject := self.GetOrNewStateObject(addr)
|
stateObject := self.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
stateObject.SetCode(code)
|
stateObject.SetCode(crypto.Keccak256Hash(code), code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) SetState(addr common.Address, key common.Hash, value common.Hash) {
|
func (self *StateDB) SetState(addr common.Address, key common.Hash, value common.Hash) {
|
||||||
stateObject := self.GetOrNewStateObject(addr)
|
stateObject := self.GetOrNewStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject != nil {
|
||||||
stateObject.SetState(key, value)
|
stateObject.SetState(self.db, key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) Delete(addr common.Address) bool {
|
// Suicide marks the given account as suicided.
|
||||||
|
// This clears the account balance.
|
||||||
|
//
|
||||||
|
// The account's state object is still available until the state is committed,
|
||||||
|
// GetStateObject will return a non-nil account after Suicide.
|
||||||
|
func (self *StateDB) Suicide(addr common.Address) bool {
|
||||||
stateObject := self.GetStateObject(addr)
|
stateObject := self.GetStateObject(addr)
|
||||||
if stateObject != nil {
|
if stateObject == nil {
|
||||||
stateObject.MarkForDeletion()
|
return false
|
||||||
stateObject.balance = new(big.Int)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
self.journal = append(self.journal, suicideChange{
|
||||||
return false
|
account: &addr,
|
||||||
|
prev: stateObject.suicided,
|
||||||
|
prevbalance: new(big.Int).Set(stateObject.Balance()),
|
||||||
|
})
|
||||||
|
stateObject.markSuicided()
|
||||||
|
stateObject.data.Balance = new(big.Int)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Setting, updating & deleting state object methods
|
// Setting, updating & deleting state object methods
|
||||||
//
|
//
|
||||||
|
|
||||||
// Update the given state object and apply it to state trie
|
// updateStateObject writes the given object to the trie.
|
||||||
func (self *StateDB) UpdateStateObject(stateObject *StateObject) {
|
func (self *StateDB) updateStateObject(stateObject *StateObject) {
|
||||||
addr := stateObject.Address()
|
addr := stateObject.Address()
|
||||||
data, err := rlp.EncodeToBytes(stateObject)
|
data, err := rlp.EncodeToBytes(stateObject)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -237,117 +356,156 @@ func (self *StateDB) UpdateStateObject(stateObject *StateObject) {
|
||||||
self.trie.Update(addr[:], data)
|
self.trie.Update(addr[:], data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the given state object and delete it from the state trie
|
// deleteStateObject removes the given object from the state trie.
|
||||||
func (self *StateDB) DeleteStateObject(stateObject *StateObject) {
|
func (self *StateDB) deleteStateObject(stateObject *StateObject) {
|
||||||
stateObject.deleted = true
|
stateObject.deleted = true
|
||||||
|
|
||||||
addr := stateObject.Address()
|
addr := stateObject.Address()
|
||||||
self.trie.Delete(addr[:])
|
self.trie.Delete(addr[:])
|
||||||
//delete(self.stateObjects, addr.Str())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve a state object given my the address. Nil if not found
|
// Retrieve a state object given my the address. Returns nil if not found.
|
||||||
func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObject) {
|
func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObject) {
|
||||||
stateObject = self.stateObjects[addr.Str()]
|
// Prefer 'live' objects.
|
||||||
if stateObject != nil {
|
if obj := self.stateObjects[addr]; obj != nil {
|
||||||
if stateObject.deleted {
|
if obj.deleted {
|
||||||
stateObject = nil
|
return nil
|
||||||
}
|
}
|
||||||
|
return obj
|
||||||
return stateObject
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data := self.trie.Get(addr[:])
|
// Load the object from the database.
|
||||||
if len(data) == 0 {
|
enc := self.trie.Get(addr[:])
|
||||||
|
if len(enc) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
stateObject, err := DecodeObject(addr, self.db, data)
|
var data Account
|
||||||
if err != nil {
|
if err := rlp.DecodeBytes(enc, &data); err != nil {
|
||||||
glog.Errorf("can't decode object at %x: %v", addr[:], err)
|
glog.Errorf("can't decode object at %x: %v", addr[:], err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
self.SetStateObject(stateObject)
|
// Insert into the live set.
|
||||||
return stateObject
|
obj := newObject(self, addr, data, self.MarkStateObjectDirty)
|
||||||
|
self.setStateObject(obj)
|
||||||
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) SetStateObject(object *StateObject) {
|
func (self *StateDB) setStateObject(object *StateObject) {
|
||||||
self.stateObjects[object.Address().Str()] = object
|
self.stateObjects[object.Address()] = object
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve a state object or create a new state object if nil
|
// Retrieve a state object or create a new state object if nil
|
||||||
func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject {
|
func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject {
|
||||||
stateObject := self.GetStateObject(addr)
|
stateObject := self.GetStateObject(addr)
|
||||||
if stateObject == nil || stateObject.deleted {
|
if stateObject == nil || stateObject.deleted {
|
||||||
stateObject = self.CreateStateObject(addr)
|
stateObject, _ = self.createObject(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return stateObject
|
return stateObject
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStateObject create a state object whether it exist in the trie or not
|
// MarkStateObjectDirty adds the specified object to the dirty map to avoid costly
|
||||||
func (self *StateDB) newStateObject(addr common.Address) *StateObject {
|
// state object cache iteration to find a handful of modified ones.
|
||||||
if glog.V(logger.Core) {
|
func (self *StateDB) MarkStateObjectDirty(addr common.Address) {
|
||||||
glog.Infof("(+) %x\n", addr)
|
self.stateObjectsDirty[addr] = struct{}{}
|
||||||
}
|
|
||||||
|
|
||||||
stateObject := NewStateObject(addr, self.db)
|
|
||||||
stateObject.SetNonce(StartingNonce)
|
|
||||||
self.stateObjects[addr.Str()] = stateObject
|
|
||||||
|
|
||||||
return stateObject
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates creates a new state object and takes ownership. This is different from "NewStateObject"
|
// createObject creates a new state object. If there is an existing account with
|
||||||
func (self *StateDB) CreateStateObject(addr common.Address) *StateObject {
|
// the given address, it is overwritten and returned as the second return value.
|
||||||
// Get previous (if any)
|
func (self *StateDB) createObject(addr common.Address) (newobj, prev *StateObject) {
|
||||||
so := self.GetStateObject(addr)
|
prev = self.GetStateObject(addr)
|
||||||
// Create a new one
|
newobj = newObject(self, addr, Account{}, self.MarkStateObjectDirty)
|
||||||
newSo := self.newStateObject(addr)
|
newobj.setNonce(StartingNonce) // sets the object to dirty
|
||||||
|
if prev == nil {
|
||||||
// If it existed set the balance to the new account
|
if glog.V(logger.Core) {
|
||||||
if so != nil {
|
glog.Infof("(+) %x\n", addr)
|
||||||
newSo.balance = so.balance
|
}
|
||||||
|
self.journal = append(self.journal, createObjectChange{account: &addr})
|
||||||
|
} else {
|
||||||
|
self.journal = append(self.journal, resetObjectChange{prev: prev})
|
||||||
}
|
}
|
||||||
|
self.setStateObject(newobj)
|
||||||
return newSo
|
return newobj, prev
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateAccount explicitly creates a state object. If a state object with the address
|
||||||
|
// already exists the balance is carried over to the new account.
|
||||||
|
//
|
||||||
|
// CreateAccount is called during the EVM CREATE operation. The situation might arise that
|
||||||
|
// a contract does the following:
|
||||||
|
//
|
||||||
|
// 1. sends funds to sha(account ++ (nonce + 1))
|
||||||
|
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
|
||||||
|
//
|
||||||
|
// Carrying over the balance ensures that Ether doesn't disappear.
|
||||||
func (self *StateDB) CreateAccount(addr common.Address) vm.Account {
|
func (self *StateDB) CreateAccount(addr common.Address) vm.Account {
|
||||||
return self.CreateStateObject(addr)
|
new, prev := self.createObject(addr)
|
||||||
|
if prev != nil {
|
||||||
|
new.setBalance(prev.data.Balance)
|
||||||
|
}
|
||||||
|
return new
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// Copy creates a deep, independent copy of the state.
|
||||||
// Setting, copying of the state methods
|
// Snapshots of the copied state cannot be applied to the copy.
|
||||||
//
|
|
||||||
|
|
||||||
func (self *StateDB) Copy() *StateDB {
|
func (self *StateDB) Copy() *StateDB {
|
||||||
// ignore error - we assume state-to-be-copied always exists
|
self.lock.Lock()
|
||||||
state, _ := New(common.Hash{}, self.db)
|
defer self.lock.Unlock()
|
||||||
state.trie = self.trie
|
|
||||||
for k, stateObject := range self.stateObjects {
|
// Copy all the basic fields, initialize the memory ones
|
||||||
state.stateObjects[k] = stateObject.Copy()
|
state := &StateDB{
|
||||||
|
db: self.db,
|
||||||
|
trie: self.trie,
|
||||||
|
pastTries: self.pastTries,
|
||||||
|
codeSizeCache: self.codeSizeCache,
|
||||||
|
stateObjects: make(map[common.Address]*StateObject, len(self.stateObjectsDirty)),
|
||||||
|
stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)),
|
||||||
|
refund: new(big.Int).Set(self.refund),
|
||||||
|
logs: make(map[common.Hash]vm.Logs, len(self.logs)),
|
||||||
|
logSize: self.logSize,
|
||||||
|
}
|
||||||
|
// Copy the dirty states and logs
|
||||||
|
for addr, _ := range self.stateObjectsDirty {
|
||||||
|
state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state, state.MarkStateObjectDirty)
|
||||||
|
state.stateObjectsDirty[addr] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.refund.Set(self.refund)
|
|
||||||
|
|
||||||
for hash, logs := range self.logs {
|
for hash, logs := range self.logs {
|
||||||
state.logs[hash] = make(vm.Logs, len(logs))
|
state.logs[hash] = make(vm.Logs, len(logs))
|
||||||
copy(state.logs[hash], logs)
|
copy(state.logs[hash], logs)
|
||||||
}
|
}
|
||||||
state.logSize = self.logSize
|
|
||||||
|
|
||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *StateDB) Set(state *StateDB) {
|
// Snapshot returns an identifier for the current revision of the state.
|
||||||
self.trie = state.trie
|
func (self *StateDB) Snapshot() int {
|
||||||
self.stateObjects = state.stateObjects
|
id := self.nextRevisionId
|
||||||
|
self.nextRevisionId++
|
||||||
self.refund = state.refund
|
self.validRevisions = append(self.validRevisions, revision{id, len(self.journal)})
|
||||||
self.logs = state.logs
|
return id
|
||||||
self.logSize = state.logSize
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RevertToSnapshot reverts all state changes made since the given revision.
|
||||||
|
func (self *StateDB) RevertToSnapshot(revid int) {
|
||||||
|
// Find the snapshot in the stack of valid snapshots.
|
||||||
|
idx := sort.Search(len(self.validRevisions), func(i int) bool {
|
||||||
|
return self.validRevisions[i].id >= revid
|
||||||
|
})
|
||||||
|
if idx == len(self.validRevisions) || self.validRevisions[idx].id != revid {
|
||||||
|
panic(fmt.Errorf("revision id %v cannot be reverted", revid))
|
||||||
|
}
|
||||||
|
snapshot := self.validRevisions[idx].journalIndex
|
||||||
|
|
||||||
|
// Replay the journal to undo changes.
|
||||||
|
for i := len(self.journal) - 1; i >= snapshot; i-- {
|
||||||
|
self.journal[i].undo(self)
|
||||||
|
}
|
||||||
|
self.journal = self.journal[:snapshot]
|
||||||
|
|
||||||
|
// Remove invalidated snapshots from the stack.
|
||||||
|
self.validRevisions = self.validRevisions[:idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRefund returns the current value of the refund counter.
|
||||||
|
// The return value must not be modified by the caller and will become
|
||||||
|
// invalid at the next call to AddRefund.
|
||||||
func (self *StateDB) GetRefund() *big.Int {
|
func (self *StateDB) GetRefund() *big.Int {
|
||||||
return self.refund
|
return self.refund
|
||||||
}
|
}
|
||||||
|
|
@ -356,18 +514,17 @@ func (self *StateDB) GetRefund() *big.Int {
|
||||||
// It is called in between transactions to get the root hash that
|
// It is called in between transactions to get the root hash that
|
||||||
// goes into transaction receipts.
|
// goes into transaction receipts.
|
||||||
func (s *StateDB) IntermediateRoot() common.Hash {
|
func (s *StateDB) IntermediateRoot() common.Hash {
|
||||||
s.refund = new(big.Int)
|
for addr, _ := range s.stateObjectsDirty {
|
||||||
for _, stateObject := range s.stateObjects {
|
stateObject := s.stateObjects[addr]
|
||||||
if stateObject.dirty {
|
if stateObject.suicided {
|
||||||
if stateObject.remove {
|
s.deleteStateObject(stateObject)
|
||||||
s.DeleteStateObject(stateObject)
|
} else {
|
||||||
} else {
|
stateObject.updateRoot(s.db)
|
||||||
stateObject.Update()
|
s.updateStateObject(stateObject)
|
||||||
s.UpdateStateObject(stateObject)
|
|
||||||
}
|
|
||||||
stateObject.dirty = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Invalidate journal because reverting across transactions is not allowed.
|
||||||
|
s.clearJournalAndRefund()
|
||||||
return s.trie.Hash()
|
return s.trie.Hash()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -377,18 +534,18 @@ func (s *StateDB) IntermediateRoot() common.Hash {
|
||||||
// DeleteSuicides should not be used for consensus related updates
|
// DeleteSuicides should not be used for consensus related updates
|
||||||
// under any circumstances.
|
// under any circumstances.
|
||||||
func (s *StateDB) DeleteSuicides() {
|
func (s *StateDB) DeleteSuicides() {
|
||||||
// Reset refund so that any used-gas calculations can use
|
// Reset refund so that any used-gas calculations can use this method.
|
||||||
// this method.
|
s.clearJournalAndRefund()
|
||||||
s.refund = new(big.Int)
|
|
||||||
for _, stateObject := range s.stateObjects {
|
for addr, _ := range s.stateObjectsDirty {
|
||||||
if stateObject.dirty {
|
stateObject := s.stateObjects[addr]
|
||||||
// If the object has been removed by a suicide
|
|
||||||
// flag the object as deleted.
|
// If the object has been removed by a suicide
|
||||||
if stateObject.remove {
|
// flag the object as deleted.
|
||||||
stateObject.deleted = true
|
if stateObject.suicided {
|
||||||
}
|
stateObject.deleted = true
|
||||||
stateObject.dirty = false
|
|
||||||
}
|
}
|
||||||
|
delete(s.stateObjectsDirty, addr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -407,46 +564,42 @@ func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) {
|
||||||
return root, batch
|
return root, batch
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *StateDB) commit(db trie.DatabaseWriter) (common.Hash, error) {
|
func (s *StateDB) clearJournalAndRefund() {
|
||||||
|
s.journal = nil
|
||||||
|
s.validRevisions = s.validRevisions[:0]
|
||||||
s.refund = new(big.Int)
|
s.refund = new(big.Int)
|
||||||
|
}
|
||||||
|
|
||||||
for _, stateObject := range s.stateObjects {
|
func (s *StateDB) commit(dbw trie.DatabaseWriter) (root common.Hash, err error) {
|
||||||
if stateObject.remove {
|
defer s.clearJournalAndRefund()
|
||||||
|
|
||||||
|
// Commit objects to the trie.
|
||||||
|
for addr, stateObject := range s.stateObjects {
|
||||||
|
if stateObject.suicided {
|
||||||
// If the object has been removed, don't bother syncing it
|
// If the object has been removed, don't bother syncing it
|
||||||
// and just mark it for deletion in the trie.
|
// and just mark it for deletion in the trie.
|
||||||
s.DeleteStateObject(stateObject)
|
s.deleteStateObject(stateObject)
|
||||||
} else {
|
} else if _, ok := s.stateObjectsDirty[addr]; ok {
|
||||||
// Write any contract code associated with the state object
|
// Write any contract code associated with the state object
|
||||||
if len(stateObject.code) > 0 {
|
if stateObject.code != nil && stateObject.dirtyCode {
|
||||||
if err := db.Put(stateObject.codeHash, stateObject.code); err != nil {
|
if err := dbw.Put(stateObject.CodeHash(), stateObject.code); err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
stateObject.dirtyCode = false
|
||||||
}
|
}
|
||||||
// Write any storage changes in the state object to its trie.
|
// Write any storage changes in the state object to its storage trie.
|
||||||
stateObject.Update()
|
if err := stateObject.CommitTrie(s.db, dbw); err != nil {
|
||||||
|
|
||||||
// Commit the trie of the object to the batch.
|
|
||||||
// This updates the trie root internally, so
|
|
||||||
// getting the root hash of the storage trie
|
|
||||||
// through UpdateStateObject is fast.
|
|
||||||
if _, err := stateObject.trie.CommitTo(db); err != nil {
|
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
// Update the object in the account trie.
|
// Update the object in the main account trie.
|
||||||
s.UpdateStateObject(stateObject)
|
s.updateStateObject(stateObject)
|
||||||
}
|
}
|
||||||
stateObject.dirty = false
|
delete(s.stateObjectsDirty, addr)
|
||||||
}
|
}
|
||||||
return s.trie.CommitTo(db)
|
// Write trie changes.
|
||||||
}
|
root, err = s.trie.CommitTo(dbw)
|
||||||
|
if err == nil {
|
||||||
func (self *StateDB) Refunds() *big.Int {
|
s.pushTrie(s.trie)
|
||||||
return self.refund
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug stuff
|
|
||||||
func (self *StateDB) CreateOutputForDiff() {
|
|
||||||
for _, stateObject := range self.stateObjects {
|
|
||||||
stateObject.CreateOutputForDiff()
|
|
||||||
}
|
}
|
||||||
|
return root, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,19 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"math/rand"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"testing/quick"
|
||||||
|
|
||||||
"github.com/expanse-project/go-expanse/common"
|
"github.com/expanse-project/go-expanse/common"
|
||||||
|
"github.com/expanse-project/go-expanse/core/vm"
|
||||||
"github.com/expanse-project/go-expanse/ethdb"
|
"github.com/expanse-project/go-expanse/ethdb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -33,16 +42,16 @@ func TestUpdateLeaks(t *testing.T) {
|
||||||
|
|
||||||
// Update it with some accounts
|
// Update it with some accounts
|
||||||
for i := byte(0); i < 255; i++ {
|
for i := byte(0); i < 255; i++ {
|
||||||
obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
addr := common.BytesToAddress([]byte{i})
|
||||||
obj.AddBalance(big.NewInt(int64(11 * i)))
|
state.AddBalance(addr, big.NewInt(int64(11*i)))
|
||||||
obj.SetNonce(uint64(42 * i))
|
state.SetNonce(addr, uint64(42*i))
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
obj.SetState(common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i}))
|
state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i}))
|
||||||
}
|
}
|
||||||
if i%3 == 0 {
|
if i%3 == 0 {
|
||||||
obj.SetCode([]byte{i, i, i, i, i})
|
state.SetCode(addr, []byte{i, i, i, i, i})
|
||||||
}
|
}
|
||||||
state.UpdateStateObject(obj)
|
state.IntermediateRoot()
|
||||||
}
|
}
|
||||||
// Ensure that no data was leaked into the database
|
// Ensure that no data was leaked into the database
|
||||||
for _, key := range db.Keys() {
|
for _, key := range db.Keys() {
|
||||||
|
|
@ -60,51 +69,38 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||||
transState, _ := New(common.Hash{}, transDb)
|
transState, _ := New(common.Hash{}, transDb)
|
||||||
finalState, _ := New(common.Hash{}, finalDb)
|
finalState, _ := New(common.Hash{}, finalDb)
|
||||||
|
|
||||||
// Update the states with some objects
|
modify := func(state *StateDB, addr common.Address, i, tweak byte) {
|
||||||
for i := byte(0); i < 255; i++ {
|
state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak)))
|
||||||
// Create a new state object with some data into the transition database
|
state.SetNonce(addr, uint64(42*i+tweak))
|
||||||
obj := transState.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
|
||||||
obj.SetBalance(big.NewInt(int64(11 * i)))
|
|
||||||
obj.SetNonce(uint64(42 * i))
|
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
obj.SetState(common.BytesToHash([]byte{i, i, i, 0}), common.BytesToHash([]byte{i, i, i, i, 0}))
|
state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{})
|
||||||
|
state.SetState(addr, common.Hash{i, i, i, tweak}, common.Hash{i, i, i, i, tweak})
|
||||||
}
|
}
|
||||||
if i%3 == 0 {
|
if i%3 == 0 {
|
||||||
obj.SetCode([]byte{i, i, i, i, i, 0})
|
state.SetCode(addr, []byte{i, i, i, i, i, tweak})
|
||||||
}
|
}
|
||||||
transState.UpdateStateObject(obj)
|
|
||||||
|
|
||||||
// Overwrite all the data with new values in the transition database
|
|
||||||
obj.SetBalance(big.NewInt(int64(11*i + 1)))
|
|
||||||
obj.SetNonce(uint64(42*i + 1))
|
|
||||||
if i%2 == 0 {
|
|
||||||
obj.SetState(common.BytesToHash([]byte{i, i, i, 0}), common.Hash{})
|
|
||||||
obj.SetState(common.BytesToHash([]byte{i, i, i, 1}), common.BytesToHash([]byte{i, i, i, i, 1}))
|
|
||||||
}
|
|
||||||
if i%3 == 0 {
|
|
||||||
obj.SetCode([]byte{i, i, i, i, i, 1})
|
|
||||||
}
|
|
||||||
transState.UpdateStateObject(obj)
|
|
||||||
|
|
||||||
// Create the final state object directly in the final database
|
|
||||||
obj = finalState.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
|
||||||
obj.SetBalance(big.NewInt(int64(11*i + 1)))
|
|
||||||
obj.SetNonce(uint64(42*i + 1))
|
|
||||||
if i%2 == 0 {
|
|
||||||
obj.SetState(common.BytesToHash([]byte{i, i, i, 1}), common.BytesToHash([]byte{i, i, i, i, 1}))
|
|
||||||
}
|
|
||||||
if i%3 == 0 {
|
|
||||||
obj.SetCode([]byte{i, i, i, i, i, 1})
|
|
||||||
}
|
|
||||||
finalState.UpdateStateObject(obj)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Modify the transient state.
|
||||||
|
for i := byte(0); i < 255; i++ {
|
||||||
|
modify(transState, common.Address{byte(i)}, i, 0)
|
||||||
|
}
|
||||||
|
// Write modifications to trie.
|
||||||
|
transState.IntermediateRoot()
|
||||||
|
|
||||||
|
// Overwrite all the data with new values in the transient database.
|
||||||
|
for i := byte(0); i < 255; i++ {
|
||||||
|
modify(transState, common.Address{byte(i)}, i, 99)
|
||||||
|
modify(finalState, common.Address{byte(i)}, i, 99)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit and cross check the databases.
|
||||||
if _, err := transState.Commit(); err != nil {
|
if _, err := transState.Commit(); err != nil {
|
||||||
t.Fatalf("failed to commit transition state: %v", err)
|
t.Fatalf("failed to commit transition state: %v", err)
|
||||||
}
|
}
|
||||||
if _, err := finalState.Commit(); err != nil {
|
if _, err := finalState.Commit(); err != nil {
|
||||||
t.Fatalf("failed to commit final state: %v", err)
|
t.Fatalf("failed to commit final state: %v", err)
|
||||||
}
|
}
|
||||||
// Cross check the databases to ensure they are the same
|
|
||||||
for _, key := range finalDb.Keys() {
|
for _, key := range finalDb.Keys() {
|
||||||
if _, err := transDb.Get(key); err != nil {
|
if _, err := transDb.Get(key); err != nil {
|
||||||
val, _ := finalDb.Get(key)
|
val, _ := finalDb.Get(key)
|
||||||
|
|
@ -118,3 +114,243 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSnapshotRandom(t *testing.T) {
|
||||||
|
config := &quick.Config{MaxCount: 1000}
|
||||||
|
err := quick.Check((*snapshotTest).run, config)
|
||||||
|
if cerr, ok := err.(*quick.CheckError); ok {
|
||||||
|
test := cerr.In[0].(*snapshotTest)
|
||||||
|
t.Errorf("%v:\n%s", test.err, test)
|
||||||
|
} else if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A snapshotTest checks that reverting StateDB snapshots properly undoes all changes
|
||||||
|
// captured by the snapshot. Instances of this test with pseudorandom content are created
|
||||||
|
// by Generate.
|
||||||
|
//
|
||||||
|
// The test works as follows:
|
||||||
|
//
|
||||||
|
// A new state is created and all actions are applied to it. Several snapshots are taken
|
||||||
|
// in between actions. The test then reverts each snapshot. For each snapshot the actions
|
||||||
|
// leading up to it are replayed on a fresh, empty state. The behaviour of all public
|
||||||
|
// accessor methods on the reverted state must match the return value of the equivalent
|
||||||
|
// methods on the replayed state.
|
||||||
|
type snapshotTest struct {
|
||||||
|
addrs []common.Address // all account addresses
|
||||||
|
actions []testAction // modifications to the state
|
||||||
|
snapshots []int // actions indexes at which snapshot is taken
|
||||||
|
err error // failure details are reported through this field
|
||||||
|
}
|
||||||
|
|
||||||
|
type testAction struct {
|
||||||
|
name string
|
||||||
|
fn func(testAction, *StateDB)
|
||||||
|
args []int64
|
||||||
|
noAddr bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// newTestAction creates a random action that changes state.
|
||||||
|
func newTestAction(addr common.Address, r *rand.Rand) testAction {
|
||||||
|
actions := []testAction{
|
||||||
|
{
|
||||||
|
name: "SetBalance",
|
||||||
|
fn: func(a testAction, s *StateDB) {
|
||||||
|
s.SetBalance(addr, big.NewInt(a.args[0]))
|
||||||
|
},
|
||||||
|
args: make([]int64, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AddBalance",
|
||||||
|
fn: func(a testAction, s *StateDB) {
|
||||||
|
s.AddBalance(addr, big.NewInt(a.args[0]))
|
||||||
|
},
|
||||||
|
args: make([]int64, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SetNonce",
|
||||||
|
fn: func(a testAction, s *StateDB) {
|
||||||
|
s.SetNonce(addr, uint64(a.args[0]))
|
||||||
|
},
|
||||||
|
args: make([]int64, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SetState",
|
||||||
|
fn: func(a testAction, s *StateDB) {
|
||||||
|
var key, val common.Hash
|
||||||
|
binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
|
||||||
|
binary.BigEndian.PutUint16(val[:], uint16(a.args[1]))
|
||||||
|
s.SetState(addr, key, val)
|
||||||
|
},
|
||||||
|
args: make([]int64, 2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "SetCode",
|
||||||
|
fn: func(a testAction, s *StateDB) {
|
||||||
|
code := make([]byte, 16)
|
||||||
|
binary.BigEndian.PutUint64(code, uint64(a.args[0]))
|
||||||
|
binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
|
||||||
|
s.SetCode(addr, code)
|
||||||
|
},
|
||||||
|
args: make([]int64, 2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "CreateAccount",
|
||||||
|
fn: func(a testAction, s *StateDB) {
|
||||||
|
s.CreateAccount(addr)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Suicide",
|
||||||
|
fn: func(a testAction, s *StateDB) {
|
||||||
|
s.Suicide(addr)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AddRefund",
|
||||||
|
fn: func(a testAction, s *StateDB) {
|
||||||
|
s.AddRefund(big.NewInt(a.args[0]))
|
||||||
|
},
|
||||||
|
args: make([]int64, 1),
|
||||||
|
noAddr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AddLog",
|
||||||
|
fn: func(a testAction, s *StateDB) {
|
||||||
|
data := make([]byte, 2)
|
||||||
|
binary.BigEndian.PutUint16(data, uint16(a.args[0]))
|
||||||
|
s.AddLog(&vm.Log{Address: addr, Data: data})
|
||||||
|
},
|
||||||
|
args: make([]int64, 1),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
action := actions[r.Intn(len(actions))]
|
||||||
|
var nameargs []string
|
||||||
|
if !action.noAddr {
|
||||||
|
nameargs = append(nameargs, addr.Hex())
|
||||||
|
}
|
||||||
|
for _, i := range action.args {
|
||||||
|
action.args[i] = rand.Int63n(100)
|
||||||
|
nameargs = append(nameargs, fmt.Sprint(action.args[i]))
|
||||||
|
}
|
||||||
|
action.name += strings.Join(nameargs, ", ")
|
||||||
|
return action
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate returns a new snapshot test of the given size. All randomness is
|
||||||
|
// derived from r.
|
||||||
|
func (*snapshotTest) Generate(r *rand.Rand, size int) reflect.Value {
|
||||||
|
// Generate random actions.
|
||||||
|
addrs := make([]common.Address, 50)
|
||||||
|
for i := range addrs {
|
||||||
|
addrs[i][0] = byte(i)
|
||||||
|
}
|
||||||
|
actions := make([]testAction, size)
|
||||||
|
for i := range actions {
|
||||||
|
addr := addrs[r.Intn(len(addrs))]
|
||||||
|
actions[i] = newTestAction(addr, r)
|
||||||
|
}
|
||||||
|
// Generate snapshot indexes.
|
||||||
|
nsnapshots := int(math.Sqrt(float64(size)))
|
||||||
|
if size > 0 && nsnapshots == 0 {
|
||||||
|
nsnapshots = 1
|
||||||
|
}
|
||||||
|
snapshots := make([]int, nsnapshots)
|
||||||
|
snaplen := len(actions) / nsnapshots
|
||||||
|
for i := range snapshots {
|
||||||
|
// Try to place the snapshots some number of actions apart from each other.
|
||||||
|
snapshots[i] = (i * snaplen) + r.Intn(snaplen)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(&snapshotTest{addrs, actions, snapshots, nil})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (test *snapshotTest) String() string {
|
||||||
|
out := new(bytes.Buffer)
|
||||||
|
sindex := 0
|
||||||
|
for i, action := range test.actions {
|
||||||
|
if len(test.snapshots) > sindex && i == test.snapshots[sindex] {
|
||||||
|
fmt.Fprintf(out, "---- snapshot %d ----\n", sindex)
|
||||||
|
sindex++
|
||||||
|
}
|
||||||
|
fmt.Fprintf(out, "%4d: %s\n", i, action.name)
|
||||||
|
}
|
||||||
|
return out.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (test *snapshotTest) run() bool {
|
||||||
|
// Run all actions and create snapshots.
|
||||||
|
var (
|
||||||
|
db, _ = ethdb.NewMemDatabase()
|
||||||
|
state, _ = New(common.Hash{}, db)
|
||||||
|
snapshotRevs = make([]int, len(test.snapshots))
|
||||||
|
sindex = 0
|
||||||
|
)
|
||||||
|
for i, action := range test.actions {
|
||||||
|
if len(test.snapshots) > sindex && i == test.snapshots[sindex] {
|
||||||
|
snapshotRevs[sindex] = state.Snapshot()
|
||||||
|
sindex++
|
||||||
|
}
|
||||||
|
action.fn(action, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revert all snapshots in reverse order. Each revert must yield a state
|
||||||
|
// that is equivalent to fresh state with all actions up the snapshot applied.
|
||||||
|
for sindex--; sindex >= 0; sindex-- {
|
||||||
|
checkstate, _ := New(common.Hash{}, db)
|
||||||
|
for _, action := range test.actions[:test.snapshots[sindex]] {
|
||||||
|
action.fn(action, checkstate)
|
||||||
|
}
|
||||||
|
state.RevertToSnapshot(snapshotRevs[sindex])
|
||||||
|
if err := test.checkEqual(state, checkstate); err != nil {
|
||||||
|
test.err = fmt.Errorf("state mismatch after revert to snapshot %d\n%v", sindex, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkEqual checks that methods of state and checkstate return the same values.
|
||||||
|
func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
|
||||||
|
for _, addr := range test.addrs {
|
||||||
|
var err error
|
||||||
|
checkeq := func(op string, a, b interface{}) bool {
|
||||||
|
if err == nil && !reflect.DeepEqual(a, b) {
|
||||||
|
err = fmt.Errorf("got %s(%s) == %v, want %v", op, addr.Hex(), a, b)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// Check basic accessor methods.
|
||||||
|
checkeq("Exist", state.Exist(addr), checkstate.Exist(addr))
|
||||||
|
checkeq("HasSuicided", state.HasSuicided(addr), checkstate.HasSuicided(addr))
|
||||||
|
checkeq("GetBalance", state.GetBalance(addr), checkstate.GetBalance(addr))
|
||||||
|
checkeq("GetNonce", state.GetNonce(addr), checkstate.GetNonce(addr))
|
||||||
|
checkeq("GetCode", state.GetCode(addr), checkstate.GetCode(addr))
|
||||||
|
checkeq("GetCodeHash", state.GetCodeHash(addr), checkstate.GetCodeHash(addr))
|
||||||
|
checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr))
|
||||||
|
// Check storage.
|
||||||
|
if obj := state.GetStateObject(addr); obj != nil {
|
||||||
|
obj.ForEachStorage(func(key, val common.Hash) bool {
|
||||||
|
return checkeq("GetState("+key.Hex()+")", val, checkstate.GetState(addr, key))
|
||||||
|
})
|
||||||
|
checkobj := checkstate.GetStateObject(addr)
|
||||||
|
checkobj.ForEachStorage(func(key, checkval common.Hash) bool {
|
||||||
|
return checkeq("GetState("+key.Hex()+")", state.GetState(addr, key), checkval)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if state.GetRefund().Cmp(checkstate.GetRefund()) != 0 {
|
||||||
|
return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d",
|
||||||
|
state.GetRefund(), checkstate.GetRefund())
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) {
|
||||||
|
return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v",
|
||||||
|
state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{}))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,17 +54,14 @@ func makeTestState() (ethdb.Database, common.Hash, []*testAccount) {
|
||||||
acc.nonce = uint64(42 * i)
|
acc.nonce = uint64(42 * i)
|
||||||
|
|
||||||
if i%3 == 0 {
|
if i%3 == 0 {
|
||||||
obj.SetCode([]byte{i, i, i, i, i})
|
obj.SetCode(crypto.Keccak256Hash([]byte{i, i, i, i, i}), []byte{i, i, i, i, i})
|
||||||
acc.code = []byte{i, i, i, i, i}
|
acc.code = []byte{i, i, i, i, i}
|
||||||
}
|
}
|
||||||
state.UpdateStateObject(obj)
|
state.updateStateObject(obj)
|
||||||
accounts = append(accounts, acc)
|
accounts = append(accounts, acc)
|
||||||
}
|
}
|
||||||
root, _ := state.Commit()
|
root, _ := state.Commit()
|
||||||
|
|
||||||
// Remove any potentially cached data from the test state creation
|
|
||||||
trie.ClearGlobalCache()
|
|
||||||
|
|
||||||
// Return the generated state
|
// Return the generated state
|
||||||
return db, root, accounts
|
return db, root, accounts
|
||||||
}
|
}
|
||||||
|
|
@ -72,9 +69,6 @@ func makeTestState() (ethdb.Database, common.Hash, []*testAccount) {
|
||||||
// checkStateAccounts cross references a reconstructed state with an expected
|
// checkStateAccounts cross references a reconstructed state with an expected
|
||||||
// account array.
|
// account array.
|
||||||
func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accounts []*testAccount) {
|
func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accounts []*testAccount) {
|
||||||
// Remove any potentially cached data from the state synchronisation
|
|
||||||
trie.ClearGlobalCache()
|
|
||||||
|
|
||||||
// Check root availability and state contents
|
// Check root availability and state contents
|
||||||
state, err := New(root, db)
|
state, err := New(root, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -98,9 +92,6 @@ func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accou
|
||||||
|
|
||||||
// checkStateConsistency checks that all nodes in a state trie are indeed present.
|
// checkStateConsistency checks that all nodes in a state trie are indeed present.
|
||||||
func checkStateConsistency(db ethdb.Database, root common.Hash) error {
|
func checkStateConsistency(db ethdb.Database, root common.Hash) error {
|
||||||
// Remove any potentially cached data from the test state creation or previous checks
|
|
||||||
trie.ClearGlobalCache()
|
|
||||||
|
|
||||||
// Create and iterate a state trie rooted in a sub-node
|
// Create and iterate a state trie rooted in a sub-node
|
||||||
if _, err := db.Get(root.Bytes()); err != nil {
|
if _, err := db.Get(root.Bytes()); err != nil {
|
||||||
return nil // Consider a non existent state consistent
|
return nil // Consider a non existent state consistent
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,11 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||||
allLogs vm.Logs
|
allLogs vm.Logs
|
||||||
gp = new(GasPool).AddGas(block.GasLimit())
|
gp = new(GasPool).AddGas(block.GasLimit())
|
||||||
)
|
)
|
||||||
|
// Mutate the the block and state according to any hard-fork specs
|
||||||
|
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
|
||||||
|
ApplyDAOHardFork(statedb)
|
||||||
|
}
|
||||||
|
// Iterate over and process the individual transactions
|
||||||
for i, tx := range block.Transactions() {
|
for i, tx := range block.Transactions() {
|
||||||
statedb.StartRecord(tx.Hash(), block.Hash(), i)
|
statedb.StartRecord(tx.Hash(), block.Hash(), i)
|
||||||
receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
|
receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
|
||||||
|
|
|
||||||
|
|
@ -240,7 +240,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
|
||||||
|
|
||||||
// Make sure the account exist. Non existent accounts
|
// Make sure the account exist. Non existent accounts
|
||||||
// haven't got funds and well therefor never pass.
|
// haven't got funds and well therefor never pass.
|
||||||
if !currentState.HasAccount(from) {
|
if !currentState.Exist(from) {
|
||||||
return ErrNonExistentAccount
|
return ErrNonExistentAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ type ContractRef interface {
|
||||||
ReturnGas(*big.Int, *big.Int)
|
ReturnGas(*big.Int, *big.Int)
|
||||||
Address() common.Address
|
Address() common.Address
|
||||||
Value() *big.Int
|
Value() *big.Int
|
||||||
SetCode([]byte)
|
SetCode(common.Hash, []byte)
|
||||||
ForEachStorage(callback func(key, value common.Hash) bool)
|
ForEachStorage(callback func(key, value common.Hash) bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,8 +45,9 @@ type Contract struct {
|
||||||
jumpdests destinations // result of JUMPDEST analysis.
|
jumpdests destinations // result of JUMPDEST analysis.
|
||||||
|
|
||||||
Code []byte
|
Code []byte
|
||||||
Input []byte
|
CodeHash common.Hash
|
||||||
CodeAddr *common.Address
|
CodeAddr *common.Address
|
||||||
|
Input []byte
|
||||||
|
|
||||||
value, Gas, UsedGas, Price *big.Int
|
value, Gas, UsedGas, Price *big.Int
|
||||||
|
|
||||||
|
|
@ -144,14 +145,16 @@ func (c *Contract) Value() *big.Int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCode sets the code to the contract
|
// SetCode sets the code to the contract
|
||||||
func (self *Contract) SetCode(code []byte) {
|
func (self *Contract) SetCode(hash common.Hash, code []byte) {
|
||||||
self.Code = code
|
self.Code = code
|
||||||
|
self.CodeHash = hash
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCallCode sets the code of the contract and address of the backing data
|
// SetCallCode sets the code of the contract and address of the backing data
|
||||||
// object
|
// object
|
||||||
func (self *Contract) SetCallCode(addr *common.Address, code []byte) {
|
func (self *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) {
|
||||||
self.Code = code
|
self.Code = code
|
||||||
|
self.CodeHash = hash
|
||||||
self.CodeAddr = addr
|
self.CodeAddr = addr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ func ecrecoverFunc(in []byte) []byte {
|
||||||
|
|
||||||
// tighter sig s values in homestead only apply to tx sigs
|
// tighter sig s values in homestead only apply to tx sigs
|
||||||
if !crypto.ValidateSignatureValues(v, r, s, false) {
|
if !crypto.ValidateSignatureValues(v, r, s, false) {
|
||||||
glog.V(logger.Debug).Infof("EC RECOVER FAIL: v, r or s value invalid")
|
glog.V(logger.Detail).Infof("ECRECOVER error: v, r or s value invalid")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,7 +106,7 @@ func ecrecoverFunc(in []byte) []byte {
|
||||||
pubKey, err := crypto.Ecrecover(in[:32], rsv)
|
pubKey, err := crypto.Ecrecover(in[:32], rsv)
|
||||||
// make sure the public key is a valid one
|
// make sure the public key is a valid one
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Error).Infof("EC RECOVER FAIL: ", err)
|
glog.V(logger.Detail).Infoln("ECRECOVER error: ", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,9 @@ type Environment interface {
|
||||||
// The state database
|
// The state database
|
||||||
Db() Database
|
Db() Database
|
||||||
// Creates a restorable snapshot
|
// Creates a restorable snapshot
|
||||||
MakeSnapshot() Database
|
SnapshotDatabase() int
|
||||||
// Set database to previous snapshot
|
// Set database to previous snapshot
|
||||||
SetSnapshot(Database)
|
RevertToSnapshot(int)
|
||||||
// Address of the original invoker (first occurrence of the VM invoker)
|
// Address of the original invoker (first occurrence of the VM invoker)
|
||||||
Origin() common.Address
|
Origin() common.Address
|
||||||
// The block number this VM is invoked on
|
// The block number this VM is invoked on
|
||||||
|
|
@ -95,6 +95,8 @@ type Database interface {
|
||||||
GetNonce(common.Address) uint64
|
GetNonce(common.Address) uint64
|
||||||
SetNonce(common.Address, uint64)
|
SetNonce(common.Address, uint64)
|
||||||
|
|
||||||
|
GetCodeHash(common.Address) common.Hash
|
||||||
|
GetCodeSize(common.Address) int
|
||||||
GetCode(common.Address) []byte
|
GetCode(common.Address) []byte
|
||||||
SetCode(common.Address, []byte)
|
SetCode(common.Address, []byte)
|
||||||
|
|
||||||
|
|
@ -104,9 +106,12 @@ type Database interface {
|
||||||
GetState(common.Address, common.Hash) common.Hash
|
GetState(common.Address, common.Hash) common.Hash
|
||||||
SetState(common.Address, common.Hash, common.Hash)
|
SetState(common.Address, common.Hash, common.Hash)
|
||||||
|
|
||||||
Delete(common.Address) bool
|
Suicide(common.Address) bool
|
||||||
|
HasSuicided(common.Address) bool
|
||||||
|
|
||||||
|
// Exist reports whether the given account exists in state.
|
||||||
|
// Notably this should also return true for suicided accounts.
|
||||||
Exist(common.Address) bool
|
Exist(common.Address) bool
|
||||||
IsDeleted(common.Address) bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Account represents a contract or basic expanse account.
|
// Account represents a contract or basic expanse account.
|
||||||
|
|
@ -118,7 +123,7 @@ type Account interface {
|
||||||
Balance() *big.Int
|
Balance() *big.Int
|
||||||
Address() common.Address
|
Address() common.Address
|
||||||
ReturnGas(*big.Int, *big.Int)
|
ReturnGas(*big.Int, *big.Int)
|
||||||
SetCode([]byte)
|
SetCode(common.Hash, []byte)
|
||||||
ForEachStorage(cb func(key, value common.Hash) bool)
|
ForEachStorage(cb func(key, value common.Hash) bool)
|
||||||
Value() *big.Int
|
Value() *big.Int
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -365,7 +365,7 @@ func opCalldataCopy(instr instruction, pc *uint64, env Environment, contract *Co
|
||||||
|
|
||||||
func opExtCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
|
func opExtCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
|
||||||
addr := common.BigToAddress(stack.pop())
|
addr := common.BigToAddress(stack.pop())
|
||||||
l := big.NewInt(int64(len(env.Db().GetCode(addr))))
|
l := big.NewInt(int64(env.Db().GetCodeSize(addr)))
|
||||||
stack.push(l)
|
stack.push(l)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -616,7 +616,7 @@ func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contrac
|
||||||
balance := env.Db().GetBalance(contract.Address())
|
balance := env.Db().GetBalance(contract.Address())
|
||||||
env.Db().AddBalance(common.BigToAddress(stack.pop()), balance)
|
env.Db().AddBalance(common.BigToAddress(stack.pop()), balance)
|
||||||
|
|
||||||
env.Db().Delete(contract.Address())
|
env.Db().Suicide(contract.Address())
|
||||||
}
|
}
|
||||||
|
|
||||||
// following functions are used by the instruction jump table
|
// following functions are used by the instruction jump table
|
||||||
|
|
|
||||||
|
|
@ -427,7 +427,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
|
||||||
}
|
}
|
||||||
gas.Set(g)
|
gas.Set(g)
|
||||||
case SUICIDE:
|
case SUICIDE:
|
||||||
if !statedb.IsDeleted(contract.Address()) {
|
if !statedb.HasSuicided(contract.Address()) {
|
||||||
statedb.AddRefund(params.SuicideRefundGas)
|
statedb.AddRefund(params.SuicideRefundGas)
|
||||||
}
|
}
|
||||||
case MLOAD:
|
case MLOAD:
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ func (account) SetNonce(uint64) {}
|
||||||
func (account) Balance() *big.Int { return nil }
|
func (account) Balance() *big.Int { return nil }
|
||||||
func (account) Address() common.Address { return common.Address{} }
|
func (account) Address() common.Address { return common.Address{} }
|
||||||
func (account) ReturnGas(*big.Int, *big.Int) {}
|
func (account) ReturnGas(*big.Int, *big.Int) {}
|
||||||
func (account) SetCode([]byte) {}
|
func (account) SetCode(common.Hash, []byte) {}
|
||||||
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
||||||
|
|
||||||
func runVmBench(test vmBench, b *testing.B) {
|
func runVmBench(test vmBench, b *testing.B) {
|
||||||
|
|
@ -187,8 +187,8 @@ func (self *Env) StructLogs() []StructLog {
|
||||||
|
|
||||||
//func (self *Env) PrevHash() []byte { return self.parent }
|
//func (self *Env) PrevHash() []byte { return self.parent }
|
||||||
func (self *Env) Coinbase() common.Address { return common.Address{} }
|
func (self *Env) Coinbase() common.Address { return common.Address{} }
|
||||||
func (self *Env) MakeSnapshot() Database { return nil }
|
func (self *Env) SnapshotDatabase() int { return 0 }
|
||||||
func (self *Env) SetSnapshot(Database) {}
|
func (self *Env) RevertToSnapshot(int) {}
|
||||||
func (self *Env) Time() *big.Int { return big.NewInt(time.Now().Unix()) }
|
func (self *Env) Time() *big.Int { return big.NewInt(time.Now().Unix()) }
|
||||||
func (self *Env) Difficulty() *big.Int { return big.NewInt(0) }
|
func (self *Env) Difficulty() *big.Int { return big.NewInt(0) }
|
||||||
func (self *Env) Db() Database { return nil }
|
func (self *Env) Db() Database { return nil }
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ type dummyContractRef struct {
|
||||||
func (dummyContractRef) ReturnGas(*big.Int, *big.Int) {}
|
func (dummyContractRef) ReturnGas(*big.Int, *big.Int) {}
|
||||||
func (dummyContractRef) Address() common.Address { return common.Address{} }
|
func (dummyContractRef) Address() common.Address { return common.Address{} }
|
||||||
func (dummyContractRef) Value() *big.Int { return new(big.Int) }
|
func (dummyContractRef) Value() *big.Int { return new(big.Int) }
|
||||||
func (dummyContractRef) SetCode([]byte) {}
|
func (dummyContractRef) SetCode(common.Hash, []byte) {}
|
||||||
func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) {
|
func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) {
|
||||||
d.calledForEach = true
|
d.calledForEach = true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,11 +100,11 @@ func (self *Env) SetDepth(i int) { self.depth = i }
|
||||||
func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
|
func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
|
||||||
return self.state.GetBalance(from).Cmp(balance) >= 0
|
return self.state.GetBalance(from).Cmp(balance) >= 0
|
||||||
}
|
}
|
||||||
func (self *Env) MakeSnapshot() vm.Database {
|
func (self *Env) SnapshotDatabase() int {
|
||||||
return self.state.Copy()
|
return self.state.Snapshot()
|
||||||
}
|
}
|
||||||
func (self *Env) SetSnapshot(copy vm.Database) {
|
func (self *Env) RevertToSnapshot(snapshot int) {
|
||||||
self.state.Set(copy.(*state.StateDB))
|
self.state.RevertToSnapshot(snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {
|
func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
||||||
receiver = cfg.State.CreateAccount(common.StringToAddress("contract"))
|
receiver = cfg.State.CreateAccount(common.StringToAddress("contract"))
|
||||||
)
|
)
|
||||||
// set the receiver's (the executing contract) code for execution.
|
// set the receiver's (the executing contract) code for execution.
|
||||||
receiver.SetCode(code)
|
receiver.SetCode(crypto.Keccak256Hash(code), code)
|
||||||
|
|
||||||
// Call the code with the given configuration.
|
// Call the code with the given configuration.
|
||||||
ret, err := vmenv.Call(
|
ret, err := vmenv.Call(
|
||||||
|
|
|
||||||
|
|
@ -78,10 +78,11 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
codehash := contract.CodeHash // codehash is used when doing jump dest caching
|
||||||
codehash = crypto.Keccak256Hash(contract.Code) // codehash is used when doing jump dest caching
|
if codehash == (common.Hash{}) {
|
||||||
program *Program
|
codehash = crypto.Keccak256Hash(contract.Code)
|
||||||
)
|
}
|
||||||
|
var program *Program
|
||||||
if evm.cfg.EnableJit {
|
if evm.cfg.EnableJit {
|
||||||
// If the JIT is enabled check the status of the JIT program,
|
// If the JIT is enabled check the status of the JIT program,
|
||||||
// if it doesn't exist compile a new program in a separate
|
// if it doesn't exist compile a new program in a separate
|
||||||
|
|
@ -309,7 +310,7 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
|
||||||
}
|
}
|
||||||
gas.Set(g)
|
gas.Set(g)
|
||||||
case SUICIDE:
|
case SUICIDE:
|
||||||
if !statedb.IsDeleted(contract.Address()) {
|
if !statedb.HasSuicided(contract.Address()) {
|
||||||
statedb.AddRefund(params.SuicideRefundGas)
|
statedb.AddRefund(params.SuicideRefundGas)
|
||||||
}
|
}
|
||||||
case MLOAD:
|
case MLOAD:
|
||||||
|
|
|
||||||
|
|
@ -95,12 +95,12 @@ func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
|
||||||
return self.state.GetBalance(from).Cmp(balance) >= 0
|
return self.state.GetBalance(from).Cmp(balance) >= 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *VMEnv) MakeSnapshot() vm.Database {
|
func (self *VMEnv) SnapshotDatabase() int {
|
||||||
return self.state.Copy()
|
return self.state.Snapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *VMEnv) SetSnapshot(copy vm.Database) {
|
func (self *VMEnv) RevertToSnapshot(snapshot int) {
|
||||||
self.state.Set(copy.(*state.StateDB))
|
self.state.RevertToSnapshot(snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
|
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build !amd64 appengine gccgo
|
||||||
|
|
||||||
package sha3
|
package sha3
|
||||||
|
|
||||||
// rc stores the round constants for use in the ι step.
|
// rc stores the round constants for use in the ι step.
|
||||||
|
|
|
||||||
13
crypto/sha3/keccakf_amd64.go
Normal file
13
crypto/sha3/keccakf_amd64.go
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build amd64,!appengine,!gccgo
|
||||||
|
|
||||||
|
package sha3
|
||||||
|
|
||||||
|
// This function is implemented in keccakf_amd64.s.
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
|
||||||
|
func keccakF1600(state *[25]uint64)
|
||||||
392
crypto/sha3/keccakf_amd64.s
Normal file
392
crypto/sha3/keccakf_amd64.s
Normal file
|
|
@ -0,0 +1,392 @@
|
||||||
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build amd64,!appengine,!gccgo
|
||||||
|
|
||||||
|
// This code was translated into a form compatible with 6a from the public
|
||||||
|
// domain sources at https://github.com/gvanas/KeccakCodePackage
|
||||||
|
|
||||||
|
// Offsets in state
|
||||||
|
#define _ba (0*8)
|
||||||
|
#define _be (1*8)
|
||||||
|
#define _bi (2*8)
|
||||||
|
#define _bo (3*8)
|
||||||
|
#define _bu (4*8)
|
||||||
|
#define _ga (5*8)
|
||||||
|
#define _ge (6*8)
|
||||||
|
#define _gi (7*8)
|
||||||
|
#define _go (8*8)
|
||||||
|
#define _gu (9*8)
|
||||||
|
#define _ka (10*8)
|
||||||
|
#define _ke (11*8)
|
||||||
|
#define _ki (12*8)
|
||||||
|
#define _ko (13*8)
|
||||||
|
#define _ku (14*8)
|
||||||
|
#define _ma (15*8)
|
||||||
|
#define _me (16*8)
|
||||||
|
#define _mi (17*8)
|
||||||
|
#define _mo (18*8)
|
||||||
|
#define _mu (19*8)
|
||||||
|
#define _sa (20*8)
|
||||||
|
#define _se (21*8)
|
||||||
|
#define _si (22*8)
|
||||||
|
#define _so (23*8)
|
||||||
|
#define _su (24*8)
|
||||||
|
|
||||||
|
// Temporary registers
|
||||||
|
#define rT1 AX
|
||||||
|
|
||||||
|
// Round vars
|
||||||
|
#define rpState DI
|
||||||
|
#define rpStack SP
|
||||||
|
|
||||||
|
#define rDa BX
|
||||||
|
#define rDe CX
|
||||||
|
#define rDi DX
|
||||||
|
#define rDo R8
|
||||||
|
#define rDu R9
|
||||||
|
|
||||||
|
#define rBa R10
|
||||||
|
#define rBe R11
|
||||||
|
#define rBi R12
|
||||||
|
#define rBo R13
|
||||||
|
#define rBu R14
|
||||||
|
|
||||||
|
#define rCa SI
|
||||||
|
#define rCe BP
|
||||||
|
#define rCi rBi
|
||||||
|
#define rCo rBo
|
||||||
|
#define rCu R15
|
||||||
|
|
||||||
|
#define MOVQ_RBI_RCE MOVQ rBi, rCe
|
||||||
|
#define XORQ_RT1_RCA XORQ rT1, rCa
|
||||||
|
#define XORQ_RT1_RCE XORQ rT1, rCe
|
||||||
|
#define XORQ_RBA_RCU XORQ rBa, rCu
|
||||||
|
#define XORQ_RBE_RCU XORQ rBe, rCu
|
||||||
|
#define XORQ_RDU_RCU XORQ rDu, rCu
|
||||||
|
#define XORQ_RDA_RCA XORQ rDa, rCa
|
||||||
|
#define XORQ_RDE_RCE XORQ rDe, rCe
|
||||||
|
|
||||||
|
#define mKeccakRound(iState, oState, rc, B_RBI_RCE, G_RT1_RCA, G_RT1_RCE, G_RBA_RCU, K_RT1_RCA, K_RT1_RCE, K_RBA_RCU, M_RT1_RCA, M_RT1_RCE, M_RBE_RCU, S_RDU_RCU, S_RDA_RCA, S_RDE_RCE) \
|
||||||
|
/* Prepare round */ \
|
||||||
|
MOVQ rCe, rDa; \
|
||||||
|
ROLQ $1, rDa; \
|
||||||
|
\
|
||||||
|
MOVQ _bi(iState), rCi; \
|
||||||
|
XORQ _gi(iState), rDi; \
|
||||||
|
XORQ rCu, rDa; \
|
||||||
|
XORQ _ki(iState), rCi; \
|
||||||
|
XORQ _mi(iState), rDi; \
|
||||||
|
XORQ rDi, rCi; \
|
||||||
|
\
|
||||||
|
MOVQ rCi, rDe; \
|
||||||
|
ROLQ $1, rDe; \
|
||||||
|
\
|
||||||
|
MOVQ _bo(iState), rCo; \
|
||||||
|
XORQ _go(iState), rDo; \
|
||||||
|
XORQ rCa, rDe; \
|
||||||
|
XORQ _ko(iState), rCo; \
|
||||||
|
XORQ _mo(iState), rDo; \
|
||||||
|
XORQ rDo, rCo; \
|
||||||
|
\
|
||||||
|
MOVQ rCo, rDi; \
|
||||||
|
ROLQ $1, rDi; \
|
||||||
|
\
|
||||||
|
MOVQ rCu, rDo; \
|
||||||
|
XORQ rCe, rDi; \
|
||||||
|
ROLQ $1, rDo; \
|
||||||
|
\
|
||||||
|
MOVQ rCa, rDu; \
|
||||||
|
XORQ rCi, rDo; \
|
||||||
|
ROLQ $1, rDu; \
|
||||||
|
\
|
||||||
|
/* Result b */ \
|
||||||
|
MOVQ _ba(iState), rBa; \
|
||||||
|
MOVQ _ge(iState), rBe; \
|
||||||
|
XORQ rCo, rDu; \
|
||||||
|
MOVQ _ki(iState), rBi; \
|
||||||
|
MOVQ _mo(iState), rBo; \
|
||||||
|
MOVQ _su(iState), rBu; \
|
||||||
|
XORQ rDe, rBe; \
|
||||||
|
ROLQ $44, rBe; \
|
||||||
|
XORQ rDi, rBi; \
|
||||||
|
XORQ rDa, rBa; \
|
||||||
|
ROLQ $43, rBi; \
|
||||||
|
\
|
||||||
|
MOVQ rBe, rCa; \
|
||||||
|
MOVQ rc, rT1; \
|
||||||
|
ORQ rBi, rCa; \
|
||||||
|
XORQ rBa, rT1; \
|
||||||
|
XORQ rT1, rCa; \
|
||||||
|
MOVQ rCa, _ba(oState); \
|
||||||
|
\
|
||||||
|
XORQ rDu, rBu; \
|
||||||
|
ROLQ $14, rBu; \
|
||||||
|
MOVQ rBa, rCu; \
|
||||||
|
ANDQ rBe, rCu; \
|
||||||
|
XORQ rBu, rCu; \
|
||||||
|
MOVQ rCu, _bu(oState); \
|
||||||
|
\
|
||||||
|
XORQ rDo, rBo; \
|
||||||
|
ROLQ $21, rBo; \
|
||||||
|
MOVQ rBo, rT1; \
|
||||||
|
ANDQ rBu, rT1; \
|
||||||
|
XORQ rBi, rT1; \
|
||||||
|
MOVQ rT1, _bi(oState); \
|
||||||
|
\
|
||||||
|
NOTQ rBi; \
|
||||||
|
ORQ rBa, rBu; \
|
||||||
|
ORQ rBo, rBi; \
|
||||||
|
XORQ rBo, rBu; \
|
||||||
|
XORQ rBe, rBi; \
|
||||||
|
MOVQ rBu, _bo(oState); \
|
||||||
|
MOVQ rBi, _be(oState); \
|
||||||
|
B_RBI_RCE; \
|
||||||
|
\
|
||||||
|
/* Result g */ \
|
||||||
|
MOVQ _gu(iState), rBe; \
|
||||||
|
XORQ rDu, rBe; \
|
||||||
|
MOVQ _ka(iState), rBi; \
|
||||||
|
ROLQ $20, rBe; \
|
||||||
|
XORQ rDa, rBi; \
|
||||||
|
ROLQ $3, rBi; \
|
||||||
|
MOVQ _bo(iState), rBa; \
|
||||||
|
MOVQ rBe, rT1; \
|
||||||
|
ORQ rBi, rT1; \
|
||||||
|
XORQ rDo, rBa; \
|
||||||
|
MOVQ _me(iState), rBo; \
|
||||||
|
MOVQ _si(iState), rBu; \
|
||||||
|
ROLQ $28, rBa; \
|
||||||
|
XORQ rBa, rT1; \
|
||||||
|
MOVQ rT1, _ga(oState); \
|
||||||
|
G_RT1_RCA; \
|
||||||
|
\
|
||||||
|
XORQ rDe, rBo; \
|
||||||
|
ROLQ $45, rBo; \
|
||||||
|
MOVQ rBi, rT1; \
|
||||||
|
ANDQ rBo, rT1; \
|
||||||
|
XORQ rBe, rT1; \
|
||||||
|
MOVQ rT1, _ge(oState); \
|
||||||
|
G_RT1_RCE; \
|
||||||
|
\
|
||||||
|
XORQ rDi, rBu; \
|
||||||
|
ROLQ $61, rBu; \
|
||||||
|
MOVQ rBu, rT1; \
|
||||||
|
ORQ rBa, rT1; \
|
||||||
|
XORQ rBo, rT1; \
|
||||||
|
MOVQ rT1, _go(oState); \
|
||||||
|
\
|
||||||
|
ANDQ rBe, rBa; \
|
||||||
|
XORQ rBu, rBa; \
|
||||||
|
MOVQ rBa, _gu(oState); \
|
||||||
|
NOTQ rBu; \
|
||||||
|
G_RBA_RCU; \
|
||||||
|
\
|
||||||
|
ORQ rBu, rBo; \
|
||||||
|
XORQ rBi, rBo; \
|
||||||
|
MOVQ rBo, _gi(oState); \
|
||||||
|
\
|
||||||
|
/* Result k */ \
|
||||||
|
MOVQ _be(iState), rBa; \
|
||||||
|
MOVQ _gi(iState), rBe; \
|
||||||
|
MOVQ _ko(iState), rBi; \
|
||||||
|
MOVQ _mu(iState), rBo; \
|
||||||
|
MOVQ _sa(iState), rBu; \
|
||||||
|
XORQ rDi, rBe; \
|
||||||
|
ROLQ $6, rBe; \
|
||||||
|
XORQ rDo, rBi; \
|
||||||
|
ROLQ $25, rBi; \
|
||||||
|
MOVQ rBe, rT1; \
|
||||||
|
ORQ rBi, rT1; \
|
||||||
|
XORQ rDe, rBa; \
|
||||||
|
ROLQ $1, rBa; \
|
||||||
|
XORQ rBa, rT1; \
|
||||||
|
MOVQ rT1, _ka(oState); \
|
||||||
|
K_RT1_RCA; \
|
||||||
|
\
|
||||||
|
XORQ rDu, rBo; \
|
||||||
|
ROLQ $8, rBo; \
|
||||||
|
MOVQ rBi, rT1; \
|
||||||
|
ANDQ rBo, rT1; \
|
||||||
|
XORQ rBe, rT1; \
|
||||||
|
MOVQ rT1, _ke(oState); \
|
||||||
|
K_RT1_RCE; \
|
||||||
|
\
|
||||||
|
XORQ rDa, rBu; \
|
||||||
|
ROLQ $18, rBu; \
|
||||||
|
NOTQ rBo; \
|
||||||
|
MOVQ rBo, rT1; \
|
||||||
|
ANDQ rBu, rT1; \
|
||||||
|
XORQ rBi, rT1; \
|
||||||
|
MOVQ rT1, _ki(oState); \
|
||||||
|
\
|
||||||
|
MOVQ rBu, rT1; \
|
||||||
|
ORQ rBa, rT1; \
|
||||||
|
XORQ rBo, rT1; \
|
||||||
|
MOVQ rT1, _ko(oState); \
|
||||||
|
\
|
||||||
|
ANDQ rBe, rBa; \
|
||||||
|
XORQ rBu, rBa; \
|
||||||
|
MOVQ rBa, _ku(oState); \
|
||||||
|
K_RBA_RCU; \
|
||||||
|
\
|
||||||
|
/* Result m */ \
|
||||||
|
MOVQ _ga(iState), rBe; \
|
||||||
|
XORQ rDa, rBe; \
|
||||||
|
MOVQ _ke(iState), rBi; \
|
||||||
|
ROLQ $36, rBe; \
|
||||||
|
XORQ rDe, rBi; \
|
||||||
|
MOVQ _bu(iState), rBa; \
|
||||||
|
ROLQ $10, rBi; \
|
||||||
|
MOVQ rBe, rT1; \
|
||||||
|
MOVQ _mi(iState), rBo; \
|
||||||
|
ANDQ rBi, rT1; \
|
||||||
|
XORQ rDu, rBa; \
|
||||||
|
MOVQ _so(iState), rBu; \
|
||||||
|
ROLQ $27, rBa; \
|
||||||
|
XORQ rBa, rT1; \
|
||||||
|
MOVQ rT1, _ma(oState); \
|
||||||
|
M_RT1_RCA; \
|
||||||
|
\
|
||||||
|
XORQ rDi, rBo; \
|
||||||
|
ROLQ $15, rBo; \
|
||||||
|
MOVQ rBi, rT1; \
|
||||||
|
ORQ rBo, rT1; \
|
||||||
|
XORQ rBe, rT1; \
|
||||||
|
MOVQ rT1, _me(oState); \
|
||||||
|
M_RT1_RCE; \
|
||||||
|
\
|
||||||
|
XORQ rDo, rBu; \
|
||||||
|
ROLQ $56, rBu; \
|
||||||
|
NOTQ rBo; \
|
||||||
|
MOVQ rBo, rT1; \
|
||||||
|
ORQ rBu, rT1; \
|
||||||
|
XORQ rBi, rT1; \
|
||||||
|
MOVQ rT1, _mi(oState); \
|
||||||
|
\
|
||||||
|
ORQ rBa, rBe; \
|
||||||
|
XORQ rBu, rBe; \
|
||||||
|
MOVQ rBe, _mu(oState); \
|
||||||
|
\
|
||||||
|
ANDQ rBa, rBu; \
|
||||||
|
XORQ rBo, rBu; \
|
||||||
|
MOVQ rBu, _mo(oState); \
|
||||||
|
M_RBE_RCU; \
|
||||||
|
\
|
||||||
|
/* Result s */ \
|
||||||
|
MOVQ _bi(iState), rBa; \
|
||||||
|
MOVQ _go(iState), rBe; \
|
||||||
|
MOVQ _ku(iState), rBi; \
|
||||||
|
XORQ rDi, rBa; \
|
||||||
|
MOVQ _ma(iState), rBo; \
|
||||||
|
ROLQ $62, rBa; \
|
||||||
|
XORQ rDo, rBe; \
|
||||||
|
MOVQ _se(iState), rBu; \
|
||||||
|
ROLQ $55, rBe; \
|
||||||
|
\
|
||||||
|
XORQ rDu, rBi; \
|
||||||
|
MOVQ rBa, rDu; \
|
||||||
|
XORQ rDe, rBu; \
|
||||||
|
ROLQ $2, rBu; \
|
||||||
|
ANDQ rBe, rDu; \
|
||||||
|
XORQ rBu, rDu; \
|
||||||
|
MOVQ rDu, _su(oState); \
|
||||||
|
\
|
||||||
|
ROLQ $39, rBi; \
|
||||||
|
S_RDU_RCU; \
|
||||||
|
NOTQ rBe; \
|
||||||
|
XORQ rDa, rBo; \
|
||||||
|
MOVQ rBe, rDa; \
|
||||||
|
ANDQ rBi, rDa; \
|
||||||
|
XORQ rBa, rDa; \
|
||||||
|
MOVQ rDa, _sa(oState); \
|
||||||
|
S_RDA_RCA; \
|
||||||
|
\
|
||||||
|
ROLQ $41, rBo; \
|
||||||
|
MOVQ rBi, rDe; \
|
||||||
|
ORQ rBo, rDe; \
|
||||||
|
XORQ rBe, rDe; \
|
||||||
|
MOVQ rDe, _se(oState); \
|
||||||
|
S_RDE_RCE; \
|
||||||
|
\
|
||||||
|
MOVQ rBo, rDi; \
|
||||||
|
MOVQ rBu, rDo; \
|
||||||
|
ANDQ rBu, rDi; \
|
||||||
|
ORQ rBa, rDo; \
|
||||||
|
XORQ rBi, rDi; \
|
||||||
|
XORQ rBo, rDo; \
|
||||||
|
MOVQ rDi, _si(oState); \
|
||||||
|
MOVQ rDo, _so(oState) \
|
||||||
|
|
||||||
|
// func keccakF1600(state *[25]uint64)
|
||||||
|
TEXT ·keccakF1600(SB), 0, $200-8
|
||||||
|
MOVQ state+0(FP), rpState
|
||||||
|
SUBQ $(8*25), SP
|
||||||
|
|
||||||
|
// Convert the user state into an internal state
|
||||||
|
NOTQ _be(rpState)
|
||||||
|
NOTQ _bi(rpState)
|
||||||
|
NOTQ _go(rpState)
|
||||||
|
NOTQ _ki(rpState)
|
||||||
|
NOTQ _mi(rpState)
|
||||||
|
NOTQ _sa(rpState)
|
||||||
|
|
||||||
|
// Execute the KeccakF permutation
|
||||||
|
MOVQ _ba(rpState), rCa
|
||||||
|
MOVQ _be(rpState), rCe
|
||||||
|
MOVQ _bu(rpState), rCu
|
||||||
|
|
||||||
|
XORQ _ga(rpState), rCa
|
||||||
|
XORQ _ge(rpState), rCe
|
||||||
|
XORQ _gu(rpState), rCu
|
||||||
|
|
||||||
|
XORQ _ka(rpState), rCa
|
||||||
|
XORQ _ke(rpState), rCe
|
||||||
|
XORQ _ku(rpState), rCu
|
||||||
|
|
||||||
|
XORQ _ma(rpState), rCa
|
||||||
|
XORQ _me(rpState), rCe
|
||||||
|
XORQ _mu(rpState), rCu
|
||||||
|
|
||||||
|
XORQ _sa(rpState), rCa
|
||||||
|
XORQ _se(rpState), rCe
|
||||||
|
MOVQ _si(rpState), rDi
|
||||||
|
MOVQ _so(rpState), rDo
|
||||||
|
XORQ _su(rpState), rCu
|
||||||
|
|
||||||
|
mKeccakRound(rpState, rpStack, $0x0000000000000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpStack, rpState, $0x0000000000008082, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpState, rpStack, $0x800000000000808a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpStack, rpState, $0x8000000080008000, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpState, rpStack, $0x000000000000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpStack, rpState, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpStack, rpState, $0x8000000000008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpState, rpStack, $0x000000000000008a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpStack, rpState, $0x0000000000000088, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpState, rpStack, $0x0000000080008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpStack, rpState, $0x000000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpState, rpStack, $0x000000008000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpStack, rpState, $0x800000000000008b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpState, rpStack, $0x8000000000008089, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpStack, rpState, $0x8000000000008003, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpState, rpStack, $0x8000000000008002, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpStack, rpState, $0x8000000000000080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpState, rpStack, $0x000000000000800a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpStack, rpState, $0x800000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpStack, rpState, $0x8000000000008080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpState, rpStack, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||||
|
mKeccakRound(rpStack, rpState, $0x8000000080008008, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP)
|
||||||
|
|
||||||
|
// Revert the internal state to the user state
|
||||||
|
NOTQ _be(rpState)
|
||||||
|
NOTQ _bi(rpState)
|
||||||
|
NOTQ _go(rpState)
|
||||||
|
NOTQ _ki(rpState)
|
||||||
|
NOTQ _mi(rpState)
|
||||||
|
NOTQ _sa(rpState)
|
||||||
|
|
||||||
|
ADDQ $(8*25), SP
|
||||||
|
RET
|
||||||
|
|
@ -42,7 +42,7 @@ type state struct {
|
||||||
storage [maxRate]byte
|
storage [maxRate]byte
|
||||||
|
|
||||||
// Specific to SHA-3 and SHAKE.
|
// Specific to SHA-3 and SHAKE.
|
||||||
fixedOutput bool // whether this is a fixed-ouput-length instance
|
fixedOutput bool // whether this is a fixed-output-length instance
|
||||||
outputLen int // the default output size in bytes
|
outputLen int // the default output size in bytes
|
||||||
state spongeDirection // whether the sponge is absorbing or squeezing
|
state spongeDirection // whether the sponge is absorbing or squeezing
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build !amd64,!386 appengine
|
// +build !amd64,!386,!ppc64le appengine
|
||||||
|
|
||||||
package sha3
|
package sha3
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
// Use of this source code is governed by a BSD-style
|
// Use of this source code is governed by a BSD-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
// +build amd64 386
|
// +build amd64 386 ppc64le
|
||||||
// +build !appengine
|
// +build !appengine
|
||||||
|
|
||||||
package sha3
|
package sha3
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/expanse-project/go-expanse/metrics"
|
"github.com/expanse-project/go-expanse/metrics"
|
||||||
"github.com/syndtr/goleveldb/leveldb"
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
"github.com/syndtr/goleveldb/leveldb/errors"
|
"github.com/syndtr/goleveldb/leveldb/errors"
|
||||||
|
"github.com/syndtr/goleveldb/leveldb/filter"
|
||||||
"github.com/syndtr/goleveldb/leveldb/iterator"
|
"github.com/syndtr/goleveldb/leveldb/iterator"
|
||||||
"github.com/syndtr/goleveldb/leveldb/opt"
|
"github.com/syndtr/goleveldb/leveldb/opt"
|
||||||
|
|
||||||
|
|
@ -86,6 +87,7 @@ func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) {
|
||||||
OpenFilesCacheCapacity: handles,
|
OpenFilesCacheCapacity: handles,
|
||||||
BlockCacheCapacity: cache / 2 * opt.MiB,
|
BlockCacheCapacity: cache / 2 * opt.MiB,
|
||||||
WriteBuffer: cache / 4 * opt.MiB, // Two of these are used internally
|
WriteBuffer: cache / 4 * opt.MiB, // Two of these are used internally
|
||||||
|
Filter: filter.NewBloomFilter(10),
|
||||||
})
|
})
|
||||||
if _, corrupted := err.(*errors.ErrCorrupted); corrupted {
|
if _, corrupted := err.(*errors.ErrCorrupted); corrupted {
|
||||||
db, err = leveldb.RecoverFile(file, nil)
|
db, err = leveldb.RecoverFile(file, nil)
|
||||||
|
|
|
||||||
12
exp/api.go
12
exp/api.go
|
|
@ -1575,14 +1575,14 @@ func NewPublicDebugAPI(exp *Expanse) *PublicDebugAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DumpBlock retrieves the entire state of the database at a given block.
|
// DumpBlock retrieves the entire state of the database at a given block.
|
||||||
func (api *PublicDebugAPI) DumpBlock(number uint64) (state.World, error) {
|
func (api *PublicDebugAPI) DumpBlock(number uint64) (state.Dump, error) {
|
||||||
block := api.exp.BlockChain().GetBlockByNumber(number)
|
block := api.exp.BlockChain().GetBlockByNumber(number)
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return state.World{}, fmt.Errorf("block #%d not found", number)
|
return state.Dump{}, fmt.Errorf("block #%d not found", number)
|
||||||
}
|
}
|
||||||
stateDb, err := state.New(block.Root(), api.exp.ChainDb())
|
stateDb, err := api.exp.BlockChain().StateAt(block.Root())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return state.World{}, err
|
return state.Dump{}, err
|
||||||
}
|
}
|
||||||
return stateDb.RawDump(), nil
|
return stateDb.RawDump(), nil
|
||||||
}
|
}
|
||||||
|
|
@ -1748,7 +1748,7 @@ func (api *PrivateDebugAPI) traceBlock(block *types.Block, config *vm.Config) (b
|
||||||
if err := core.ValidateHeader(api.config, blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash()), true, false); err != nil {
|
if err := core.ValidateHeader(api.config, blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash()), true, false); err != nil {
|
||||||
return false, collector.traces, err
|
return false, collector.traces, err
|
||||||
}
|
}
|
||||||
statedb, err := state.New(blockchain.GetBlock(block.ParentHash()).Root(), api.exp.ChainDb())
|
statedb, err := blockchain.StateAt(blockchain.GetBlock(block.ParentHash()).Root())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, collector.traces, err
|
return false, collector.traces, err
|
||||||
}
|
}
|
||||||
|
|
@ -1850,7 +1850,7 @@ func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogC
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
return nil, fmt.Errorf("block parent %x not found", block.ParentHash())
|
return nil, fmt.Errorf("block parent %x not found", block.ParentHash())
|
||||||
}
|
}
|
||||||
stateDb, err := state.New(parent.Root(), api.exp.ChainDb())
|
stateDb, err := api.exp.BlockChain().StateAt(parent.Root())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -250,6 +250,8 @@ func New(ctx *node.ServiceContext, config *Config) (*Expanse, error) {
|
||||||
if config.ChainConfig == nil {
|
if config.ChainConfig == nil {
|
||||||
return nil, errors.New("missing chain config")
|
return nil, errors.New("missing chain config")
|
||||||
}
|
}
|
||||||
|
core.WriteChainConfig(chainDb, genesis.Hash(), config.ChainConfig)
|
||||||
|
|
||||||
exp.chainConfig = config.ChainConfig
|
exp.chainConfig = config.ChainConfig
|
||||||
exp.chainConfig.VmConfig = vm.Config{
|
exp.chainConfig.VmConfig = vm.Config{
|
||||||
EnableJit: config.EnableJit,
|
EnableJit: config.EnableJit,
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ func TestMipmapUpgrade(t *testing.T) {
|
||||||
addr := common.BytesToAddress([]byte("jeff"))
|
addr := common.BytesToAddress([]byte("jeff"))
|
||||||
genesis := core.WriteGenesisBlockForTesting(db)
|
genesis := core.WriteGenesisBlockForTesting(db)
|
||||||
|
|
||||||
chain, receipts := core.GenerateChain(genesis, db, 10, func(i int, gen *core.BlockGen) {
|
chain, receipts := core.GenerateChain(nil, genesis, db, 10, func(i int, gen *core.BlockGen) {
|
||||||
var receipts types.Receipts
|
var receipts types.Receipts
|
||||||
switch i {
|
switch i {
|
||||||
case 1:
|
case 1:
|
||||||
|
|
|
||||||
|
|
@ -48,23 +48,17 @@ var (
|
||||||
MaxReceiptFetch = 256 // Amount of transaction receipts to allow fetching per request
|
MaxReceiptFetch = 256 // Amount of transaction receipts to allow fetching per request
|
||||||
MaxStateFetch = 384 // Amount of node state values to allow fetching per request
|
MaxStateFetch = 384 // Amount of node state values to allow fetching per request
|
||||||
|
|
||||||
MaxForkAncestry = 3 * params.EpochDuration.Uint64() // Maximum chain reorganisation
|
MaxForkAncestry = 3 * params.EpochDuration.Uint64() // Maximum chain reorganisation
|
||||||
|
rttMinEstimate = 2 * time.Second // Minimum round-trip time to target for download requests
|
||||||
hashTTL = 3 * time.Second // [eth/61] Time it takes for a hash request to time out
|
rttMaxEstimate = 20 * time.Second // Maximum rount-trip time to target for download requests
|
||||||
blockTargetRTT = 3 * time.Second / 2 // [eth/61] Target time for completing a block retrieval request
|
rttMinConfidence = 0.1 // Worse confidence factor in our estimated RTT value
|
||||||
blockTTL = 3 * blockTargetRTT // [eth/61] Maximum time allowance before a block request is considered expired
|
ttlScaling = 3 // Constant scaling factor for RTT -> TTL conversion
|
||||||
|
ttlLimit = time.Minute // Maximum TTL allowance to prevent reaching crazy timeouts
|
||||||
rttMinEstimate = 2 * time.Second // Minimum round-trip time to target for download requests
|
|
||||||
rttMaxEstimate = 20 * time.Second // Maximum rount-trip time to target for download requests
|
|
||||||
rttMinConfidence = 0.1 // Worse confidence factor in our estimated RTT value
|
|
||||||
ttlScaling = 3 // Constant scaling factor for RTT -> TTL conversion
|
|
||||||
ttlLimit = time.Minute // Maximum TTL allowance to prevent reaching crazy timeouts
|
|
||||||
|
|
||||||
qosTuningPeers = 5 // Number of peers to tune based on (best peers)
|
qosTuningPeers = 5 // Number of peers to tune based on (best peers)
|
||||||
qosConfidenceCap = 10 // Number of peers above which not to modify RTT confidence
|
qosConfidenceCap = 10 // Number of peers above which not to modify RTT confidence
|
||||||
qosTuningImpact = 0.25 // Impact that a new tuning target has on the previous value
|
qosTuningImpact = 0.25 // Impact that a new tuning target has on the previous value
|
||||||
|
|
||||||
maxQueuedHashes = 32 * 1024 // [eth/61] Maximum number of hashes to queue for import (DOS protection)
|
|
||||||
maxQueuedHeaders = 32 * 1024 // [eth/62] Maximum number of headers to queue for import (DOS protection)
|
maxQueuedHeaders = 32 * 1024 // [eth/62] Maximum number of headers to queue for import (DOS protection)
|
||||||
maxHeadersProcess = 2048 // Number of header download results to import at once into the chain
|
maxHeadersProcess = 2048 // Number of header download results to import at once into the chain
|
||||||
maxResultsProcess = 2048 // Number of content download results to import at once into the chain
|
maxResultsProcess = 2048 // Number of content download results to import at once into the chain
|
||||||
|
|
@ -84,16 +78,13 @@ var (
|
||||||
errStallingPeer = errors.New("peer is stalling")
|
errStallingPeer = errors.New("peer is stalling")
|
||||||
errNoPeers = errors.New("no peers to keep download active")
|
errNoPeers = errors.New("no peers to keep download active")
|
||||||
errTimeout = errors.New("timeout")
|
errTimeout = errors.New("timeout")
|
||||||
errEmptyHashSet = errors.New("empty hash set by peer")
|
|
||||||
errEmptyHeaderSet = errors.New("empty header set by peer")
|
errEmptyHeaderSet = errors.New("empty header set by peer")
|
||||||
errPeersUnavailable = errors.New("no peers available or all tried for download")
|
errPeersUnavailable = errors.New("no peers available or all tried for download")
|
||||||
errAlreadyInPool = errors.New("hash already in pool")
|
|
||||||
errInvalidAncestor = errors.New("retrieved ancestor is invalid")
|
errInvalidAncestor = errors.New("retrieved ancestor is invalid")
|
||||||
errInvalidChain = errors.New("retrieved hash chain is invalid")
|
errInvalidChain = errors.New("retrieved hash chain is invalid")
|
||||||
errInvalidBlock = errors.New("retrieved block is invalid")
|
errInvalidBlock = errors.New("retrieved block is invalid")
|
||||||
errInvalidBody = errors.New("retrieved block body is invalid")
|
errInvalidBody = errors.New("retrieved block body is invalid")
|
||||||
errInvalidReceipt = errors.New("retrieved receipt is invalid")
|
errInvalidReceipt = errors.New("retrieved receipt is invalid")
|
||||||
errCancelHashFetch = errors.New("hash download canceled (requested)")
|
|
||||||
errCancelBlockFetch = errors.New("block download canceled (requested)")
|
errCancelBlockFetch = errors.New("block download canceled (requested)")
|
||||||
errCancelHeaderFetch = errors.New("block header download canceled (requested)")
|
errCancelHeaderFetch = errors.New("block header download canceled (requested)")
|
||||||
errCancelBodyFetch = errors.New("block body download canceled (requested)")
|
errCancelBodyFetch = errors.New("block body download canceled (requested)")
|
||||||
|
|
@ -102,6 +93,7 @@ var (
|
||||||
errCancelHeaderProcessing = errors.New("header processing canceled (requested)")
|
errCancelHeaderProcessing = errors.New("header processing canceled (requested)")
|
||||||
errCancelContentProcessing = errors.New("content processing canceled (requested)")
|
errCancelContentProcessing = errors.New("content processing canceled (requested)")
|
||||||
errNoSyncActive = errors.New("no sync active")
|
errNoSyncActive = errors.New("no sync active")
|
||||||
|
errTooOld = errors.New("peer doesn't speak recent enough protocol version (need version >= 62)")
|
||||||
)
|
)
|
||||||
|
|
||||||
type Downloader struct {
|
type Downloader struct {
|
||||||
|
|
@ -146,20 +138,19 @@ type Downloader struct {
|
||||||
|
|
||||||
// Channels
|
// Channels
|
||||||
newPeerCh chan *peer
|
newPeerCh chan *peer
|
||||||
hashCh chan dataPack // [eth/61] Channel receiving inbound hashes
|
|
||||||
blockCh chan dataPack // [eth/61] Channel receiving inbound blocks
|
|
||||||
headerCh chan dataPack // [eth/62] Channel receiving inbound block headers
|
headerCh chan dataPack // [eth/62] Channel receiving inbound block headers
|
||||||
bodyCh chan dataPack // [eth/62] Channel receiving inbound block bodies
|
bodyCh chan dataPack // [eth/62] Channel receiving inbound block bodies
|
||||||
receiptCh chan dataPack // [eth/63] Channel receiving inbound receipts
|
receiptCh chan dataPack // [eth/63] Channel receiving inbound receipts
|
||||||
stateCh chan dataPack // [eth/63] Channel receiving inbound node state data
|
stateCh chan dataPack // [eth/63] Channel receiving inbound node state data
|
||||||
blockWakeCh chan bool // [eth/61] Channel to signal the block fetcher of new tasks
|
|
||||||
bodyWakeCh chan bool // [eth/62] Channel to signal the block body fetcher of new tasks
|
bodyWakeCh chan bool // [eth/62] Channel to signal the block body fetcher of new tasks
|
||||||
receiptWakeCh chan bool // [eth/63] Channel to signal the receipt fetcher of new tasks
|
receiptWakeCh chan bool // [eth/63] Channel to signal the receipt fetcher of new tasks
|
||||||
stateWakeCh chan bool // [eth/63] Channel to signal the state fetcher of new tasks
|
stateWakeCh chan bool // [eth/63] Channel to signal the state fetcher of new tasks
|
||||||
headerProcCh chan []*types.Header // [eth/62] Channel to feed the header processor new tasks
|
headerProcCh chan []*types.Header // [eth/62] Channel to feed the header processor new tasks
|
||||||
|
|
||||||
|
// Cancellation and termination
|
||||||
|
cancelPeer string // Identifier of the peer currently being used as the master (cancel on drop)
|
||||||
cancelCh chan struct{} // Channel to cancel mid-flight syncs
|
cancelCh chan struct{} // Channel to cancel mid-flight syncs
|
||||||
cancelLock sync.RWMutex // Lock to protect the cancel channel in delivers
|
cancelLock sync.RWMutex // Lock to protect the cancel channel and peer in delivers
|
||||||
|
|
||||||
quitCh chan struct{} // Quit channel to signal termination
|
quitCh chan struct{} // Quit channel to signal termination
|
||||||
quitLock sync.RWMutex // Lock to prevent double closes
|
quitLock sync.RWMutex // Lock to prevent double closes
|
||||||
|
|
@ -199,13 +190,10 @@ func New(stateDb ethdb.Database, mux *event.TypeMux, hasHeader headerCheckFn, ha
|
||||||
rollback: rollback,
|
rollback: rollback,
|
||||||
dropPeer: dropPeer,
|
dropPeer: dropPeer,
|
||||||
newPeerCh: make(chan *peer, 1),
|
newPeerCh: make(chan *peer, 1),
|
||||||
hashCh: make(chan dataPack, 1),
|
|
||||||
blockCh: make(chan dataPack, 1),
|
|
||||||
headerCh: make(chan dataPack, 1),
|
headerCh: make(chan dataPack, 1),
|
||||||
bodyCh: make(chan dataPack, 1),
|
bodyCh: make(chan dataPack, 1),
|
||||||
receiptCh: make(chan dataPack, 1),
|
receiptCh: make(chan dataPack, 1),
|
||||||
stateCh: make(chan dataPack, 1),
|
stateCh: make(chan dataPack, 1),
|
||||||
blockWakeCh: make(chan bool, 1),
|
|
||||||
bodyWakeCh: make(chan bool, 1),
|
bodyWakeCh: make(chan bool, 1),
|
||||||
receiptWakeCh: make(chan bool, 1),
|
receiptWakeCh: make(chan bool, 1),
|
||||||
stateWakeCh: make(chan bool, 1),
|
stateWakeCh: make(chan bool, 1),
|
||||||
|
|
@ -250,13 +238,12 @@ func (d *Downloader) Synchronising() bool {
|
||||||
|
|
||||||
// RegisterPeer injects a new download peer into the set of block source to be
|
// RegisterPeer injects a new download peer into the set of block source to be
|
||||||
// used for fetching hashes and blocks from.
|
// used for fetching hashes and blocks from.
|
||||||
func (d *Downloader) RegisterPeer(id string, version int, head common.Hash,
|
func (d *Downloader) RegisterPeer(id string, version int, currentHead currentHeadRetrievalFn,
|
||||||
getRelHashes relativeHashFetcherFn, getAbsHashes absoluteHashFetcherFn, getBlocks blockFetcherFn, // eth/61 callbacks, remove when upgrading
|
|
||||||
getRelHeaders relativeHeaderFetcherFn, getAbsHeaders absoluteHeaderFetcherFn, getBlockBodies blockBodyFetcherFn,
|
getRelHeaders relativeHeaderFetcherFn, getAbsHeaders absoluteHeaderFetcherFn, getBlockBodies blockBodyFetcherFn,
|
||||||
getReceipts receiptFetcherFn, getNodeData stateFetcherFn) error {
|
getReceipts receiptFetcherFn, getNodeData stateFetcherFn) error {
|
||||||
|
|
||||||
glog.V(logger.Detail).Infoln("Registering peer", id)
|
glog.V(logger.Detail).Infoln("Registering peer", id)
|
||||||
if err := d.peers.Register(newPeer(id, version, head, getRelHashes, getAbsHashes, getBlocks, getRelHeaders, getAbsHeaders, getBlockBodies, getReceipts, getNodeData)); err != nil {
|
if err := d.peers.Register(newPeer(id, version, currentHead, getRelHeaders, getAbsHeaders, getBlockBodies, getReceipts, getNodeData)); err != nil {
|
||||||
glog.V(logger.Error).Infoln("Register failed:", err)
|
glog.V(logger.Error).Infoln("Register failed:", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -269,12 +256,22 @@ func (d *Downloader) RegisterPeer(id string, version int, head common.Hash,
|
||||||
// the specified peer. An effort is also made to return any pending fetches into
|
// the specified peer. An effort is also made to return any pending fetches into
|
||||||
// the queue.
|
// the queue.
|
||||||
func (d *Downloader) UnregisterPeer(id string) error {
|
func (d *Downloader) UnregisterPeer(id string) error {
|
||||||
|
// Unregister the peer from the active peer set and revoke any fetch tasks
|
||||||
glog.V(logger.Detail).Infoln("Unregistering peer", id)
|
glog.V(logger.Detail).Infoln("Unregistering peer", id)
|
||||||
if err := d.peers.Unregister(id); err != nil {
|
if err := d.peers.Unregister(id); err != nil {
|
||||||
glog.V(logger.Error).Infoln("Unregister failed:", err)
|
glog.V(logger.Error).Infoln("Unregister failed:", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
d.queue.Revoke(id)
|
d.queue.Revoke(id)
|
||||||
|
|
||||||
|
// If this peer was the master peer, abort sync immediately
|
||||||
|
d.cancelLock.RLock()
|
||||||
|
master := id == d.cancelPeer
|
||||||
|
d.cancelLock.RUnlock()
|
||||||
|
|
||||||
|
if master {
|
||||||
|
d.cancel()
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -291,7 +288,9 @@ func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode
|
||||||
case errBusy:
|
case errBusy:
|
||||||
glog.V(logger.Detail).Infof("Synchronisation already in progress")
|
glog.V(logger.Detail).Infof("Synchronisation already in progress")
|
||||||
|
|
||||||
case errTimeout, errBadPeer, errStallingPeer, errEmptyHashSet, errEmptyHeaderSet, errPeersUnavailable, errInvalidAncestor, errInvalidChain:
|
case errTimeout, errBadPeer, errStallingPeer,
|
||||||
|
errEmptyHeaderSet, errPeersUnavailable, errTooOld,
|
||||||
|
errInvalidAncestor, errInvalidChain:
|
||||||
glog.V(logger.Debug).Infof("Removing peer %v: %v", id, err)
|
glog.V(logger.Debug).Infof("Removing peer %v: %v", id, err)
|
||||||
d.dropPeer(id)
|
d.dropPeer(id)
|
||||||
|
|
||||||
|
|
@ -323,13 +322,13 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode
|
||||||
d.queue.Reset()
|
d.queue.Reset()
|
||||||
d.peers.Reset()
|
d.peers.Reset()
|
||||||
|
|
||||||
for _, ch := range []chan bool{d.blockWakeCh, d.bodyWakeCh, d.receiptWakeCh, d.stateWakeCh} {
|
for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh, d.stateWakeCh} {
|
||||||
select {
|
select {
|
||||||
case <-ch:
|
case <-ch:
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, ch := range []chan dataPack{d.hashCh, d.blockCh, d.headerCh, d.bodyCh, d.receiptCh, d.stateCh} {
|
for _, ch := range []chan dataPack{d.headerCh, d.bodyCh, d.receiptCh, d.stateCh} {
|
||||||
for empty := false; !empty; {
|
for empty := false; !empty; {
|
||||||
select {
|
select {
|
||||||
case <-ch:
|
case <-ch:
|
||||||
|
|
@ -345,9 +344,10 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode
|
||||||
empty = true
|
empty = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Create cancel channel for aborting mid-flight
|
// Create cancel channel for aborting mid-flight and mark the master peer
|
||||||
d.cancelLock.Lock()
|
d.cancelLock.Lock()
|
||||||
d.cancelCh = make(chan struct{})
|
d.cancelCh = make(chan struct{})
|
||||||
|
d.cancelPeer = id
|
||||||
d.cancelLock.Unlock()
|
d.cancelLock.Unlock()
|
||||||
|
|
||||||
defer d.cancel() // No matter what, we can't leave the cancel channel open
|
defer d.cancel() // No matter what, we can't leave the cancel channel open
|
||||||
|
|
@ -377,104 +377,73 @@ func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err e
|
||||||
d.mux.Post(DoneEvent{})
|
d.mux.Post(DoneEvent{})
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
if p.version < 62 {
|
||||||
|
return errTooOld
|
||||||
|
}
|
||||||
|
|
||||||
glog.V(logger.Debug).Infof("Synchronising with the network using: %s [eth/%d]", p.id, p.version)
|
glog.V(logger.Debug).Infof("Synchronising with the network using: %s [eth/%d]", p.id, p.version)
|
||||||
defer func(start time.Time) {
|
defer func(start time.Time) {
|
||||||
glog.V(logger.Debug).Infof("Synchronisation terminated after %v", time.Since(start))
|
glog.V(logger.Debug).Infof("Synchronisation terminated after %v", time.Since(start))
|
||||||
}(time.Now())
|
}(time.Now())
|
||||||
switch {
|
|
||||||
case p.version == 61:
|
|
||||||
// Look up the sync boundaries: the common ancestor and the target block
|
|
||||||
latest, err := d.fetchHeight61(p)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
origin, err := d.findAncestor61(p, latest)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.syncStatsLock.Lock()
|
|
||||||
if d.syncStatsChainHeight <= origin || d.syncStatsChainOrigin > origin {
|
|
||||||
d.syncStatsChainOrigin = origin
|
|
||||||
}
|
|
||||||
d.syncStatsChainHeight = latest
|
|
||||||
d.syncStatsLock.Unlock()
|
|
||||||
|
|
||||||
// Initiate the sync using a concurrent hash and block retrieval algorithm
|
// Look up the sync boundaries: the common ancestor and the target block
|
||||||
d.queue.Prepare(origin+1, d.mode, 0, nil)
|
latest, err := d.fetchHeight(p)
|
||||||
if d.syncInitHook != nil {
|
if err != nil {
|
||||||
d.syncInitHook(origin, latest)
|
return err
|
||||||
}
|
|
||||||
return d.spawnSync(origin+1,
|
|
||||||
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
|
|
||||||
latest, err := d.fetchHeight(p)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
height := latest.Number.Uint64()
|
|
||||||
|
|
||||||
origin, err := d.findAncestor(p, height)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.syncStatsLock.Lock()
|
|
||||||
if d.syncStatsChainHeight <= origin || d.syncStatsChainOrigin > origin {
|
|
||||||
d.syncStatsChainOrigin = origin
|
|
||||||
}
|
|
||||||
d.syncStatsChainHeight = height
|
|
||||||
d.syncStatsLock.Unlock()
|
|
||||||
|
|
||||||
// Initiate the sync using a concurrent header and content retrieval algorithm
|
|
||||||
pivot := uint64(0)
|
|
||||||
switch d.mode {
|
|
||||||
case LightSync:
|
|
||||||
pivot = height
|
|
||||||
case FastSync:
|
|
||||||
// Calculate the new fast/slow sync pivot point
|
|
||||||
if d.fsPivotLock == nil {
|
|
||||||
pivotOffset, err := rand.Int(rand.Reader, big.NewInt(int64(fsPivotInterval)))
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("Failed to access crypto random source: %v", err))
|
|
||||||
}
|
|
||||||
if height > uint64(fsMinFullBlocks)+pivotOffset.Uint64() {
|
|
||||||
pivot = height - uint64(fsMinFullBlocks) - pivotOffset.Uint64()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Pivot point locked in, use this and do not pick a new one!
|
|
||||||
pivot = d.fsPivotLock.Number.Uint64()
|
|
||||||
}
|
|
||||||
// If the point is below the origin, move origin back to ensure state download
|
|
||||||
if pivot < origin {
|
|
||||||
if pivot > 0 {
|
|
||||||
origin = pivot - 1
|
|
||||||
} else {
|
|
||||||
origin = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
glog.V(logger.Debug).Infof("Fast syncing until pivot block #%d", pivot)
|
|
||||||
}
|
|
||||||
d.queue.Prepare(origin+1, d.mode, pivot, latest)
|
|
||||||
if d.syncInitHook != nil {
|
|
||||||
d.syncInitHook(origin, height)
|
|
||||||
}
|
|
||||||
return d.spawnSync(origin+1,
|
|
||||||
func() error { return d.fetchHeaders(p, origin+1) }, // Headers are always retrieved
|
|
||||||
func() error { return d.processHeaders(origin+1, td) }, // 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 exp protocol: %d", p.version)
|
|
||||||
return errBadPeer
|
|
||||||
}
|
}
|
||||||
|
height := latest.Number.Uint64()
|
||||||
|
|
||||||
|
origin, err := d.findAncestor(p, height)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.syncStatsLock.Lock()
|
||||||
|
if d.syncStatsChainHeight <= origin || d.syncStatsChainOrigin > origin {
|
||||||
|
d.syncStatsChainOrigin = origin
|
||||||
|
}
|
||||||
|
d.syncStatsChainHeight = height
|
||||||
|
d.syncStatsLock.Unlock()
|
||||||
|
|
||||||
|
// Initiate the sync using a concurrent header and content retrieval algorithm
|
||||||
|
pivot := uint64(0)
|
||||||
|
switch d.mode {
|
||||||
|
case LightSync:
|
||||||
|
pivot = height
|
||||||
|
case FastSync:
|
||||||
|
// Calculate the new fast/slow sync pivot point
|
||||||
|
if d.fsPivotLock == nil {
|
||||||
|
pivotOffset, err := rand.Int(rand.Reader, big.NewInt(int64(fsPivotInterval)))
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Failed to access crypto random source: %v", err))
|
||||||
|
}
|
||||||
|
if height > uint64(fsMinFullBlocks)+pivotOffset.Uint64() {
|
||||||
|
pivot = height - uint64(fsMinFullBlocks) - pivotOffset.Uint64()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Pivot point locked in, use this and do not pick a new one!
|
||||||
|
pivot = d.fsPivotLock.Number.Uint64()
|
||||||
|
}
|
||||||
|
// If the point is below the origin, move origin back to ensure state download
|
||||||
|
if pivot < origin {
|
||||||
|
if pivot > 0 {
|
||||||
|
origin = pivot - 1
|
||||||
|
} else {
|
||||||
|
origin = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glog.V(logger.Debug).Infof("Fast syncing until pivot block #%d", pivot)
|
||||||
|
}
|
||||||
|
d.queue.Prepare(origin+1, d.mode, pivot, latest)
|
||||||
|
if d.syncInitHook != nil {
|
||||||
|
d.syncInitHook(origin, height)
|
||||||
|
}
|
||||||
|
return d.spawnSync(origin+1,
|
||||||
|
func() error { return d.fetchHeaders(p, origin+1) }, // Headers are always retrieved
|
||||||
|
func() error { return d.processHeaders(origin+1, td) }, // 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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// spawnSync runs d.process and all given fetcher functions to completion in
|
// spawnSync runs d.process and all given fetcher functions to completion in
|
||||||
|
|
@ -539,459 +508,14 @@ func (d *Downloader) Terminate() {
|
||||||
d.cancel()
|
d.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchHeight61 retrieves the head block of the remote peer to aid in estimating
|
|
||||||
// the total time a pending synchronisation would take.
|
|
||||||
func (d *Downloader) fetchHeight61(p *peer) (uint64, error) {
|
|
||||||
glog.V(logger.Debug).Infof("%v: retrieving remote chain height", p)
|
|
||||||
|
|
||||||
// Request the advertised remote head block and wait for the response
|
|
||||||
go p.getBlocks([]common.Hash{p.head})
|
|
||||||
|
|
||||||
timeout := time.After(hashTTL)
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-d.cancelCh:
|
|
||||||
return 0, errCancelBlockFetch
|
|
||||||
|
|
||||||
case packet := <-d.blockCh:
|
|
||||||
// Discard anything not from the origin peer
|
|
||||||
if packet.PeerId() != p.id {
|
|
||||||
glog.V(logger.Debug).Infof("Received blocks from incorrect peer(%s)", packet.PeerId())
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Make sure the peer actually gave something valid
|
|
||||||
blocks := packet.(*blockPack).blocks
|
|
||||||
if len(blocks) != 1 {
|
|
||||||
glog.V(logger.Debug).Infof("%v: invalid number of head blocks: %d != 1", p, len(blocks))
|
|
||||||
return 0, errBadPeer
|
|
||||||
}
|
|
||||||
return blocks[0].NumberU64(), nil
|
|
||||||
|
|
||||||
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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// findAncestor61 tries to locate the common ancestor block of the local chain and
|
|
||||||
// a remote peers blockchain. In the general case when our node was in sync and
|
|
||||||
// on the correct chain, checking the top N blocks should already get us a match.
|
|
||||||
// In the rare scenario when we ended up on a long reorganisation (i.e. none of
|
|
||||||
// the head blocks match), we do a binary search to find the common ancestor.
|
|
||||||
func (d *Downloader) findAncestor61(p *peer, height uint64) (uint64, error) {
|
|
||||||
glog.V(logger.Debug).Infof("%v: looking for common ancestor", p)
|
|
||||||
|
|
||||||
// Figure out the valid ancestor range to prevent rewrite attacks
|
|
||||||
floor, ceil := int64(-1), d.headBlock().NumberU64()
|
|
||||||
if ceil >= MaxForkAncestry {
|
|
||||||
floor = int64(ceil - MaxForkAncestry)
|
|
||||||
}
|
|
||||||
// Request the topmost blocks to short circuit binary ancestor lookup
|
|
||||||
head := ceil
|
|
||||||
if head > height {
|
|
||||||
head = height
|
|
||||||
}
|
|
||||||
from := int64(head) - int64(MaxHashFetch) + 1
|
|
||||||
if from < 0 {
|
|
||||||
from = 0
|
|
||||||
}
|
|
||||||
go p.getAbsHashes(uint64(from), MaxHashFetch)
|
|
||||||
|
|
||||||
// Wait for the remote response to the head fetch
|
|
||||||
number, hash := uint64(0), common.Hash{}
|
|
||||||
timeout := time.After(hashTTL)
|
|
||||||
|
|
||||||
for finished := false; !finished; {
|
|
||||||
select {
|
|
||||||
case <-d.cancelCh:
|
|
||||||
return 0, errCancelHashFetch
|
|
||||||
|
|
||||||
case packet := <-d.hashCh:
|
|
||||||
// Discard anything not from the origin peer
|
|
||||||
if packet.PeerId() != p.id {
|
|
||||||
glog.V(logger.Debug).Infof("Received hashes from incorrect peer(%s)", packet.PeerId())
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Make sure the peer actually gave something valid
|
|
||||||
hashes := packet.(*hashPack).hashes
|
|
||||||
if len(hashes) == 0 {
|
|
||||||
glog.V(logger.Debug).Infof("%v: empty head hash set", p)
|
|
||||||
return 0, errEmptyHashSet
|
|
||||||
}
|
|
||||||
// Check if a common ancestor was found
|
|
||||||
finished = true
|
|
||||||
for i := len(hashes) - 1; i >= 0; i-- {
|
|
||||||
// Skip any headers that underflow/overflow our requested set
|
|
||||||
header := d.getHeader(hashes[i])
|
|
||||||
if header == nil || header.Number.Int64() < from || header.Number.Uint64() > head {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Otherwise check if we already know the header or not
|
|
||||||
if d.hasBlockAndState(hashes[i]) {
|
|
||||||
number, hash = header.Number.Uint64(), header.Hash()
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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:
|
|
||||||
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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If the head fetch already found an ancestor, return
|
|
||||||
if !common.EmptyHash(hash) {
|
|
||||||
if int64(number) <= floor {
|
|
||||||
glog.V(logger.Warn).Infof("%v: potential rewrite attack: #%d [%x…] <= #%d limit", p, number, hash[:4], floor)
|
|
||||||
return 0, errInvalidAncestor
|
|
||||||
}
|
|
||||||
glog.V(logger.Debug).Infof("%v: common ancestor: #%d [%x…]", p, number, hash[:4])
|
|
||||||
return number, nil
|
|
||||||
}
|
|
||||||
// Ancestor not found, we need to binary search over our chain
|
|
||||||
start, end := uint64(0), head
|
|
||||||
if floor > 0 {
|
|
||||||
start = uint64(floor)
|
|
||||||
}
|
|
||||||
for start+1 < end {
|
|
||||||
// Split our chain interval in two, and request the hash to cross check
|
|
||||||
check := (start + end) / 2
|
|
||||||
|
|
||||||
timeout := time.After(hashTTL)
|
|
||||||
go p.getAbsHashes(uint64(check), 1)
|
|
||||||
|
|
||||||
// Wait until a reply arrives to this request
|
|
||||||
for arrived := false; !arrived; {
|
|
||||||
select {
|
|
||||||
case <-d.cancelCh:
|
|
||||||
return 0, errCancelHashFetch
|
|
||||||
|
|
||||||
case packet := <-d.hashCh:
|
|
||||||
// Discard anything not from the origin peer
|
|
||||||
if packet.PeerId() != p.id {
|
|
||||||
glog.V(logger.Debug).Infof("Received hashes from incorrect peer(%s)", packet.PeerId())
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Make sure the peer actually gave something valid
|
|
||||||
hashes := packet.(*hashPack).hashes
|
|
||||||
if len(hashes) != 1 {
|
|
||||||
glog.V(logger.Debug).Infof("%v: invalid search hash set (%d)", p, len(hashes))
|
|
||||||
return 0, errBadPeer
|
|
||||||
}
|
|
||||||
arrived = true
|
|
||||||
|
|
||||||
// Modify the search interval based on the response
|
|
||||||
if !d.hasBlockAndState(hashes[0]) {
|
|
||||||
end = check
|
|
||||||
break
|
|
||||||
}
|
|
||||||
block := d.getBlock(hashes[0]) // this doesn't check state, hence the above explicit check
|
|
||||||
if block.NumberU64() != check {
|
|
||||||
glog.V(logger.Debug).Infof("%v: non requested hash #%d [%x…], instead of #%d", p, block.NumberU64(), block.Hash().Bytes()[:4], check)
|
|
||||||
return 0, errBadPeer
|
|
||||||
}
|
|
||||||
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:
|
|
||||||
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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Ensure valid ancestry and return
|
|
||||||
if int64(start) <= floor {
|
|
||||||
glog.V(logger.Warn).Infof("%v: potential rewrite attack: #%d [%x…] <= #%d limit", p, start, hash[:4], floor)
|
|
||||||
return 0, errInvalidAncestor
|
|
||||||
}
|
|
||||||
glog.V(logger.Debug).Infof("%v: common ancestor: #%d [%x…]", p, start, hash[:4])
|
|
||||||
return start, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetchHashes61 keeps retrieving hashes from the requested number, until no more
|
|
||||||
// are returned, potentially throttling on the way.
|
|
||||||
func (d *Downloader) fetchHashes61(p *peer, td *big.Int, from uint64) error {
|
|
||||||
glog.V(logger.Debug).Infof("%v: downloading hashes from #%d", p, from)
|
|
||||||
|
|
||||||
// Create a timeout timer, and the associated hash fetcher
|
|
||||||
request := time.Now() // time of the last fetch request
|
|
||||||
timeout := time.NewTimer(0) // timer to dump a non-responsive active peer
|
|
||||||
<-timeout.C // timeout channel should be initially empty
|
|
||||||
defer timeout.Stop()
|
|
||||||
|
|
||||||
getHashes := func(from uint64) {
|
|
||||||
glog.V(logger.Detail).Infof("%v: fetching %d hashes from #%d", p, MaxHashFetch, from)
|
|
||||||
|
|
||||||
request = time.Now()
|
|
||||||
timeout.Reset(hashTTL)
|
|
||||||
go p.getAbsHashes(from, MaxHashFetch)
|
|
||||||
}
|
|
||||||
// Start pulling hashes, until all are exhausted
|
|
||||||
getHashes(from)
|
|
||||||
gotHashes := false
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-d.cancelCh:
|
|
||||||
return errCancelHashFetch
|
|
||||||
|
|
||||||
case packet := <-d.hashCh:
|
|
||||||
// Make sure the active peer is giving us the hashes
|
|
||||||
if packet.PeerId() != p.id {
|
|
||||||
glog.V(logger.Debug).Infof("Received hashes from incorrect peer(%s)", packet.PeerId())
|
|
||||||
break
|
|
||||||
}
|
|
||||||
hashReqTimer.UpdateSince(request)
|
|
||||||
timeout.Stop()
|
|
||||||
|
|
||||||
// If no more hashes are inbound, notify the block fetcher and return
|
|
||||||
if packet.Items() == 0 {
|
|
||||||
glog.V(logger.Debug).Infof("%v: no available hashes", p)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case d.blockWakeCh <- false:
|
|
||||||
case <-d.cancelCh:
|
|
||||||
}
|
|
||||||
// If no hashes were retrieved at all, the peer violated it's TD promise that it had a
|
|
||||||
// better chain compared to ours. The only exception is if it's promised blocks were
|
|
||||||
// already imported by other means (e.g. fetcher):
|
|
||||||
//
|
|
||||||
// R <remote peer>, L <local node>: Both at block 10
|
|
||||||
// R: Mine block 11, and propagate it to L
|
|
||||||
// L: Queue block 11 for import
|
|
||||||
// L: Notice that R's head and TD increased compared to ours, start sync
|
|
||||||
// L: Import of block 11 finishes
|
|
||||||
// L: Sync begins, and finds common ancestor at 11
|
|
||||||
// L: Request new hashes up from 11 (R's TD was higher, it must have something)
|
|
||||||
// R: Nothing to give
|
|
||||||
if !gotHashes && td.Cmp(d.getTd(d.headBlock().Hash())) > 0 {
|
|
||||||
return errStallingPeer
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
gotHashes = true
|
|
||||||
hashes := packet.(*hashPack).hashes
|
|
||||||
|
|
||||||
// Otherwise insert all the new hashes, aborting in case of junk
|
|
||||||
glog.V(logger.Detail).Infof("%v: scheduling %d hashes from #%d", p, len(hashes), from)
|
|
||||||
|
|
||||||
inserts := d.queue.Schedule61(hashes, true)
|
|
||||||
if len(inserts) != len(hashes) {
|
|
||||||
glog.V(logger.Debug).Infof("%v: stale hashes", p)
|
|
||||||
return errBadPeer
|
|
||||||
}
|
|
||||||
// Notify the block fetcher of new hashes, but stop if queue is full
|
|
||||||
if d.queue.PendingBlocks() < maxQueuedHashes {
|
|
||||||
// We still have hashes to fetch, send continuation wake signal (potential)
|
|
||||||
select {
|
|
||||||
case d.blockWakeCh <- true:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Hash limit reached, send a termination wake signal (enforced)
|
|
||||||
select {
|
|
||||||
case d.blockWakeCh <- false:
|
|
||||||
case <-d.cancelCh:
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Queue not yet full, fetch the next batch
|
|
||||||
from += uint64(len(hashes))
|
|
||||||
getHashes(from)
|
|
||||||
|
|
||||||
case <-timeout.C:
|
|
||||||
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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetchBlocks61 iteratively downloads the scheduled hashes, taking any available
|
|
||||||
// peers, reserving a chunk of blocks for each, waiting for delivery and also
|
|
||||||
// periodically checking for timeouts.
|
|
||||||
func (d *Downloader) fetchBlocks61(from uint64) error {
|
|
||||||
glog.V(logger.Debug).Infof("Downloading blocks from #%d", from)
|
|
||||||
defer glog.V(logger.Debug).Infof("Block download terminated")
|
|
||||||
|
|
||||||
// Create a timeout timer for scheduling expiration tasks
|
|
||||||
ticker := time.NewTicker(100 * time.Millisecond)
|
|
||||||
defer ticker.Stop()
|
|
||||||
|
|
||||||
update := make(chan struct{}, 1)
|
|
||||||
|
|
||||||
// Fetch blocks until the hash fetcher's done
|
|
||||||
finished := false
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-d.cancelCh:
|
|
||||||
return errCancelBlockFetch
|
|
||||||
|
|
||||||
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 {
|
|
||||||
blocks := packet.(*blockPack).blocks
|
|
||||||
|
|
||||||
// Deliver the received chunk of blocks and check chain validity
|
|
||||||
accepted, err := d.queue.DeliverBlocks(peer.id, blocks)
|
|
||||||
if err == errInvalidChain {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Unless a peer delivered something completely else than requested (usually
|
|
||||||
// caused by a timed out request which came through in the end), set it to
|
|
||||||
// idle. If the delivery's stale, the peer should have already been idled.
|
|
||||||
if err != errStaleDelivery {
|
|
||||||
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:
|
|
||||||
glog.V(logger.Detail).Infof("%s: delivery failed: %v", peer, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Blocks arrived, try to update the progress
|
|
||||||
select {
|
|
||||||
case update <- struct{}{}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
case cont := <-d.blockWakeCh:
|
|
||||||
// The hash fetcher sent a continuation flag, check if it's done
|
|
||||||
if !cont {
|
|
||||||
finished = true
|
|
||||||
}
|
|
||||||
// Hashes arrive, try to update the progress
|
|
||||||
select {
|
|
||||||
case update <- struct{}{}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
case <-ticker.C:
|
|
||||||
// Sanity check update the progress
|
|
||||||
select {
|
|
||||||
case update <- struct{}{}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
case <-update:
|
|
||||||
// Short circuit if we lost all our peers
|
|
||||||
if d.peers.Len() == 0 {
|
|
||||||
return errNoPeers
|
|
||||||
}
|
|
||||||
// Check for block request timeouts and demote the responsible peers
|
|
||||||
for pid, fails := range d.queue.ExpireBlocks(blockTTL) {
|
|
||||||
if peer := d.peers.Peer(pid); peer != nil {
|
|
||||||
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
|
|
||||||
if d.queue.PendingBlocks() == 0 {
|
|
||||||
if !d.queue.InFlightBlocks() && finished {
|
|
||||||
glog.V(logger.Debug).Infof("Block fetching completed")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Send a download request to all idle peers, until throttled
|
|
||||||
throttled := false
|
|
||||||
idles, total := d.peers.BlockIdlePeers()
|
|
||||||
|
|
||||||
for _, peer := range idles {
|
|
||||||
// Short circuit if throttling activated
|
|
||||||
if d.queue.ShouldThrottleBlocks() {
|
|
||||||
throttled = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Reserve a chunk of hashes for a peer. A nil can mean either that
|
|
||||||
// no more hashes are available, or that the peer is known not to
|
|
||||||
// have them.
|
|
||||||
request := d.queue.ReserveBlocks(peer, peer.BlockCapacity(blockTargetRTT))
|
|
||||||
if request == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if glog.V(logger.Detail) {
|
|
||||||
glog.Infof("%s: requesting %d blocks", peer, len(request.Hashes))
|
|
||||||
}
|
|
||||||
// Fetch the chunk and make sure any errors return the hashes to the queue
|
|
||||||
if err := peer.Fetch61(request); err != nil {
|
|
||||||
// Although we could try and make an attempt to fix this, this error really
|
|
||||||
// means that we've double allocated a fetch task to a peer. If that is the
|
|
||||||
// case, the internal state of the downloader and the queue is very wrong so
|
|
||||||
// better hard crash and note the error instead of silently accumulating into
|
|
||||||
// a much bigger issue.
|
|
||||||
panic(fmt.Sprintf("%v: fetch assignment failed", peer))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Make sure that we have peers available for fetching. If all peers have been tried
|
|
||||||
// and all failed throw an 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.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetchHeight retrieves the head header of the remote peer to aid in estimating
|
// fetchHeight retrieves the head header of the remote peer to aid in estimating
|
||||||
// the total time a pending synchronisation would take.
|
// the total time a pending synchronisation would take.
|
||||||
func (d *Downloader) fetchHeight(p *peer) (*types.Header, error) {
|
func (d *Downloader) fetchHeight(p *peer) (*types.Header, error) {
|
||||||
glog.V(logger.Debug).Infof("%v: retrieving remote chain height", p)
|
glog.V(logger.Debug).Infof("%v: retrieving remote chain height", p)
|
||||||
|
|
||||||
// Request the advertised remote head block and wait for the response
|
// Request the advertised remote head block and wait for the response
|
||||||
go p.getRelHeaders(p.head, 1, 0, false)
|
head, _ := p.currentHead()
|
||||||
|
go p.getRelHeaders(head, 1, 0, false)
|
||||||
|
|
||||||
timeout := time.After(d.requestTTL())
|
timeout := time.After(d.requestTTL())
|
||||||
for {
|
for {
|
||||||
|
|
@ -1021,11 +545,6 @@ func (d *Downloader) fetchHeight(p *peer) (*types.Header, error) {
|
||||||
case <-d.stateCh:
|
case <-d.stateCh:
|
||||||
case <-d.receiptCh:
|
case <-d.receiptCh:
|
||||||
// Out of bounds delivery, ignore
|
// 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.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1036,7 +555,7 @@ func (d *Downloader) fetchHeight(p *peer) (*types.Header, error) {
|
||||||
// In the rare scenario when we ended up on a long reorganisation (i.e. none of
|
// In the rare scenario when we ended up on a long reorganisation (i.e. none of
|
||||||
// the head links match), we do a binary search to find the common ancestor.
|
// the head links match), we do a binary search to find the common ancestor.
|
||||||
func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
|
func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
|
||||||
glog.V(logger.Debug).Infof("%v: looking for common ancestor", p)
|
glog.V(logger.Debug).Infof("%v: looking for common ancestor (remote height %d)", p, height)
|
||||||
|
|
||||||
// Figure out the valid ancestor range to prevent rewrite attacks
|
// Figure out the valid ancestor range to prevent rewrite attacks
|
||||||
floor, ceil := int64(-1), d.headHeader().Number.Uint64()
|
floor, ceil := int64(-1), d.headHeader().Number.Uint64()
|
||||||
|
|
@ -1053,11 +572,17 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
|
||||||
if head > height {
|
if head > height {
|
||||||
head = height
|
head = height
|
||||||
}
|
}
|
||||||
from := int64(head) - int64(MaxHeaderFetch) + 1
|
from := int64(head) - int64(MaxHeaderFetch)
|
||||||
if from < 0 {
|
if from < 0 {
|
||||||
from = 0
|
from = 0
|
||||||
}
|
}
|
||||||
go p.getAbsHeaders(uint64(from), MaxHeaderFetch, 0, false)
|
// Span out with 15 block gaps into the future to catch bad head reports
|
||||||
|
limit := 2 * MaxHeaderFetch / 16
|
||||||
|
count := 1 + int((int64(ceil)-from)/16)
|
||||||
|
if count > limit {
|
||||||
|
count = limit
|
||||||
|
}
|
||||||
|
go p.getAbsHeaders(uint64(from), count, 15, false)
|
||||||
|
|
||||||
// Wait for the remote response to the head fetch
|
// Wait for the remote response to the head fetch
|
||||||
number, hash := uint64(0), common.Hash{}
|
number, hash := uint64(0), common.Hash{}
|
||||||
|
|
@ -1066,7 +591,7 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
|
||||||
for finished := false; !finished; {
|
for finished := false; !finished; {
|
||||||
select {
|
select {
|
||||||
case <-d.cancelCh:
|
case <-d.cancelCh:
|
||||||
return 0, errCancelHashFetch
|
return 0, errCancelHeaderFetch
|
||||||
|
|
||||||
case packet := <-d.headerCh:
|
case packet := <-d.headerCh:
|
||||||
// Discard anything not from the origin peer
|
// Discard anything not from the origin peer
|
||||||
|
|
@ -1082,12 +607,8 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
|
||||||
}
|
}
|
||||||
// Make sure the peer's reply conforms to the request
|
// Make sure the peer's reply conforms to the request
|
||||||
for i := 0; i < len(headers); i++ {
|
for i := 0; i < len(headers); i++ {
|
||||||
if number := headers[i].Number.Int64(); number != from+int64(i) {
|
if number := headers[i].Number.Int64(); number != from+int64(i)*16 {
|
||||||
glog.V(logger.Warn).Infof("%v: head header set (item %d) broke chain ordering: requested %d, got %d", p, i, from+int64(i), number)
|
glog.V(logger.Warn).Infof("%v: head header set (item %d) broke chain ordering: requested %d, got %d", p, i, from+int64(i)*16, number)
|
||||||
return 0, errInvalidChain
|
|
||||||
}
|
|
||||||
if i > 0 && headers[i-1].Hash() != headers[i].ParentHash {
|
|
||||||
glog.V(logger.Warn).Infof("%v: head header set (item %d) broke chain ancestry: expected [%x], got [%x]", p, i, headers[i-1].Hash().Bytes()[:4], headers[i].ParentHash[:4])
|
|
||||||
return 0, errInvalidChain
|
return 0, errInvalidChain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1096,12 +617,18 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
|
||||||
for i := len(headers) - 1; i >= 0; i-- {
|
for i := len(headers) - 1; i >= 0; i-- {
|
||||||
|
|
||||||
// Skip any headers that underflow/overflow our requested set
|
// Skip any headers that underflow/overflow our requested set
|
||||||
if headers[i].Number.Int64() < from || headers[i].Number.Uint64() > head {
|
if headers[i].Number.Int64() < from || headers[i].Number.Uint64() > ceil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Otherwise check if we already know the header or not
|
// Otherwise check if we already know the header or not
|
||||||
if (d.mode == FullSync && d.hasBlockAndState(headers[i].Hash())) || (d.mode != FullSync && d.hasHeader(headers[i].Hash())) {
|
if (d.mode == FullSync && d.hasBlockAndState(headers[i].Hash())) || (d.mode != FullSync && d.hasHeader(headers[i].Hash())) {
|
||||||
number, hash = headers[i].Number.Uint64(), headers[i].Hash()
|
number, hash = headers[i].Number.Uint64(), headers[i].Hash()
|
||||||
|
|
||||||
|
// If every header is known, even future ones, the peer straight out lied about its head
|
||||||
|
if number > height && i == limit-1 {
|
||||||
|
glog.V(logger.Warn).Infof("%v: lied about chain head: reported %d, found above %d", p, height, number)
|
||||||
|
return 0, errStallingPeer
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1114,11 +641,6 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
|
||||||
case <-d.stateCh:
|
case <-d.stateCh:
|
||||||
case <-d.receiptCh:
|
case <-d.receiptCh:
|
||||||
// Out of bounds delivery, ignore
|
// 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
|
// If the head fetch already found an ancestor, return
|
||||||
|
|
@ -1146,7 +668,7 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
|
||||||
for arrived := false; !arrived; {
|
for arrived := false; !arrived; {
|
||||||
select {
|
select {
|
||||||
case <-d.cancelCh:
|
case <-d.cancelCh:
|
||||||
return 0, errCancelHashFetch
|
return 0, errCancelHeaderFetch
|
||||||
|
|
||||||
case packer := <-d.headerCh:
|
case packer := <-d.headerCh:
|
||||||
// Discard anything not from the origin peer
|
// Discard anything not from the origin peer
|
||||||
|
|
@ -1182,11 +704,6 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
|
||||||
case <-d.stateCh:
|
case <-d.stateCh:
|
||||||
case <-d.receiptCh:
|
case <-d.receiptCh:
|
||||||
// Out of bounds delivery, ignore
|
// 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.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1305,11 +822,6 @@ func (d *Downloader) fetchHeaders(p *peer, from uint64) error {
|
||||||
case <-d.cancelCh:
|
case <-d.cancelCh:
|
||||||
}
|
}
|
||||||
return errBadPeer
|
return errBadPeer
|
||||||
|
|
||||||
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.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1555,7 +1067,14 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
|
||||||
// Check for fetch request timeouts and demote the responsible peers
|
// Check for fetch request timeouts and demote the responsible peers
|
||||||
for pid, fails := range expire() {
|
for pid, fails := range expire() {
|
||||||
if peer := d.peers.Peer(pid); peer != nil {
|
if peer := d.peers.Peer(pid); peer != nil {
|
||||||
if fails > 1 {
|
// If a lot of retrieval elements expired, we might have overestimated the remote peer or perhaps
|
||||||
|
// ourselves. Only reset to minimal throughput but don't drop just yet. If even the minimal times
|
||||||
|
// out that sync wise we need to get rid of the peer.
|
||||||
|
//
|
||||||
|
// The reason the minimum threshold is 2 is because the downloader tries to estimate the bandwidth
|
||||||
|
// and latency of a peer separately, which requires pushing the measures capacity a bit and seeing
|
||||||
|
// how response times reacts, to it always requests one more than the minimum (i.e. min 2).
|
||||||
|
if fails > 2 {
|
||||||
glog.V(logger.Detail).Infof("%s: %s delivery timeout", peer, strings.ToLower(kind))
|
glog.V(logger.Detail).Infof("%s: %s delivery timeout", peer, strings.ToLower(kind))
|
||||||
setIdle(peer, 0)
|
setIdle(peer, 0)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1623,11 +1142,6 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
|
||||||
if !progressed && !throttled && !running && len(idles) == total && pending() > 0 {
|
if !progressed && !throttled && !running && len(idles) == total && pending() > 0 {
|
||||||
return errPeersUnavailable
|
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.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1859,7 +1373,7 @@ func (d *Downloader) processContent() error {
|
||||||
}
|
}
|
||||||
if err != nil {
|
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)
|
glog.V(logger.Debug).Infof("Result #%d [%x…] processing failed: %v", results[index].Header.Number, results[index].Header.Hash().Bytes()[:4], err)
|
||||||
return err
|
return errInvalidChain
|
||||||
}
|
}
|
||||||
// Shift the results to the next batch
|
// Shift the results to the next batch
|
||||||
results = results[items:]
|
results = results[items:]
|
||||||
|
|
@ -1867,19 +1381,6 @@ func (d *Downloader) processContent() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverHashes injects a new batch of hashes received from a remote node into
|
|
||||||
// the download schedule. This is usually invoked through the BlockHashesMsg by
|
|
||||||
// the protocol handler.
|
|
||||||
func (d *Downloader) DeliverHashes(id string, hashes []common.Hash) (err error) {
|
|
||||||
return d.deliver(id, d.hashCh, &hashPack{id, hashes}, hashInMeter, hashDropMeter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeliverBlocks injects a new batch of blocks received from a remote node.
|
|
||||||
// This is usually invoked through the BlocksMsg by the protocol handler.
|
|
||||||
func (d *Downloader) DeliverBlocks(id string, blocks []*types.Block) (err error) {
|
|
||||||
return d.deliver(id, d.blockCh, &blockPack{id, blocks}, blockInMeter, blockDropMeter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeliverHeaders injects a new batch of block headers received from a remote
|
// DeliverHeaders injects a new batch of block headers received from a remote
|
||||||
// node into the download schedule.
|
// node into the download schedule.
|
||||||
func (d *Downloader) DeliverHeaders(id string, headers []*types.Header) (err error) {
|
func (d *Downloader) DeliverHeaders(id string, headers []*types.Header) (err error) {
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ func init() {
|
||||||
// reassembly.
|
// reassembly.
|
||||||
func makeChain(n int, seed byte, parent *types.Block, parentReceipts types.Receipts, heavy bool) ([]common.Hash, map[common.Hash]*types.Header, map[common.Hash]*types.Block, map[common.Hash]types.Receipts) {
|
func makeChain(n int, seed byte, parent *types.Block, parentReceipts types.Receipts, heavy bool) ([]common.Hash, map[common.Hash]*types.Header, map[common.Hash]*types.Block, map[common.Hash]types.Receipts) {
|
||||||
// Generate the block chain
|
// Generate the block chain
|
||||||
blocks, receipts := core.GenerateChain(parent, testdb, n, func(i int, block *core.BlockGen) {
|
blocks, receipts := core.GenerateChain(nil, parent, testdb, n, func(i int, block *core.BlockGen) {
|
||||||
block.SetCoinbase(common.Address{seed})
|
block.SetCoinbase(common.Address{seed})
|
||||||
|
|
||||||
// If a heavy chain is requested, delay blocks to raise difficulty
|
// If a heavy chain is requested, delay blocks to raise difficulty
|
||||||
|
|
@ -399,14 +399,12 @@ func (dl *downloadTester) newSlowPeer(id string, version int, hashes []common.Ha
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
switch version {
|
switch version {
|
||||||
case 61:
|
|
||||||
err = dl.downloader.RegisterPeer(id, version, hashes[0], dl.peerGetRelHashesFn(id, delay), dl.peerGetAbsHashesFn(id, delay), dl.peerGetBlocksFn(id, delay), nil, nil, nil, nil, nil)
|
|
||||||
case 62:
|
case 62:
|
||||||
err = dl.downloader.RegisterPeer(id, version, hashes[0], nil, nil, nil, dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), nil, nil)
|
err = dl.downloader.RegisterPeer(id, version, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), nil, nil)
|
||||||
case 63:
|
case 63:
|
||||||
err = dl.downloader.RegisterPeer(id, version, hashes[0], nil, nil, nil, dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay))
|
err = dl.downloader.RegisterPeer(id, version, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay))
|
||||||
case 64:
|
case 64:
|
||||||
err = dl.downloader.RegisterPeer(id, version, hashes[0], nil, nil, nil, dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay))
|
err = dl.downloader.RegisterPeer(id, version, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay))
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Assign the owned hashes, headers and blocks to the peer (deep copy)
|
// Assign the owned hashes, headers and blocks to the peer (deep copy)
|
||||||
|
|
@ -465,83 +463,14 @@ func (dl *downloadTester) dropPeer(id string) {
|
||||||
dl.downloader.UnregisterPeer(id)
|
dl.downloader.UnregisterPeer(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// peerGetRelHashesFn constructs a GetHashes function associated with a specific
|
// peerCurrentHeadFn constructs a function to retrieve a peer's current head hash
|
||||||
// peer in the download tester. The returned function can be used to retrieve
|
// and total difficulty.
|
||||||
// batches of hashes from the particularly requested peer.
|
func (dl *downloadTester) peerCurrentHeadFn(id string) func() (common.Hash, *big.Int) {
|
||||||
func (dl *downloadTester) peerGetRelHashesFn(id string, delay time.Duration) func(head common.Hash) error {
|
return func() (common.Hash, *big.Int) {
|
||||||
return func(head common.Hash) error {
|
|
||||||
time.Sleep(delay)
|
|
||||||
|
|
||||||
dl.lock.RLock()
|
dl.lock.RLock()
|
||||||
defer dl.lock.RUnlock()
|
defer dl.lock.RUnlock()
|
||||||
|
|
||||||
// Gather the next batch of hashes
|
return dl.peerHashes[id][0], nil
|
||||||
hashes := dl.peerHashes[id]
|
|
||||||
result := make([]common.Hash, 0, MaxHashFetch)
|
|
||||||
for i, hash := range hashes {
|
|
||||||
if hash == head {
|
|
||||||
i++
|
|
||||||
for len(result) < cap(result) && i < len(hashes) {
|
|
||||||
result = append(result, hashes[i])
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Delay delivery a bit to allow attacks to unfold
|
|
||||||
go func() {
|
|
||||||
time.Sleep(time.Millisecond)
|
|
||||||
dl.downloader.DeliverHashes(id, result)
|
|
||||||
}()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// peerGetAbsHashesFn constructs a GetHashesFromNumber function associated with
|
|
||||||
// a particular peer in the download tester. The returned function can be used to
|
|
||||||
// retrieve batches of hashes from the particularly requested peer.
|
|
||||||
func (dl *downloadTester) peerGetAbsHashesFn(id string, delay time.Duration) func(uint64, int) error {
|
|
||||||
return func(head uint64, count int) error {
|
|
||||||
time.Sleep(delay)
|
|
||||||
|
|
||||||
dl.lock.RLock()
|
|
||||||
defer dl.lock.RUnlock()
|
|
||||||
|
|
||||||
// Gather the next batch of hashes
|
|
||||||
hashes := dl.peerHashes[id]
|
|
||||||
result := make([]common.Hash, 0, count)
|
|
||||||
for i := 0; i < count && len(hashes)-int(head)-1-i >= 0; i++ {
|
|
||||||
result = append(result, hashes[len(hashes)-int(head)-1-i])
|
|
||||||
}
|
|
||||||
// Delay delivery a bit to allow attacks to unfold
|
|
||||||
go func() {
|
|
||||||
time.Sleep(time.Millisecond)
|
|
||||||
dl.downloader.DeliverHashes(id, result)
|
|
||||||
}()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// peerGetBlocksFn constructs a getBlocks function associated with a particular
|
|
||||||
// peer in the download tester. The returned function can be used to retrieve
|
|
||||||
// batches of blocks from the particularly requested peer.
|
|
||||||
func (dl *downloadTester) peerGetBlocksFn(id string, delay time.Duration) func([]common.Hash) error {
|
|
||||||
return func(hashes []common.Hash) error {
|
|
||||||
time.Sleep(delay)
|
|
||||||
|
|
||||||
dl.lock.RLock()
|
|
||||||
defer dl.lock.RUnlock()
|
|
||||||
|
|
||||||
blocks := dl.peerBlocks[id]
|
|
||||||
result := make([]*types.Block, 0, len(hashes))
|
|
||||||
for _, hash := range hashes {
|
|
||||||
if block, ok := blocks[hash]; ok {
|
|
||||||
result = append(result, block)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
go dl.downloader.DeliverBlocks(id, result)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -730,7 +659,6 @@ func assertOwnForkedChain(t *testing.T, tester *downloadTester, common int, leng
|
||||||
// Tests that simple synchronization against a canonical chain works correctly.
|
// Tests that simple synchronization against a canonical chain works correctly.
|
||||||
// In this test common ancestor lookup should be short circuited and not require
|
// In this test common ancestor lookup should be short circuited and not require
|
||||||
// binary searching.
|
// binary searching.
|
||||||
func TestCanonicalSynchronisation61(t *testing.T) { testCanonicalSynchronisation(t, 61, FullSync) }
|
|
||||||
func TestCanonicalSynchronisation62(t *testing.T) { testCanonicalSynchronisation(t, 62, FullSync) }
|
func TestCanonicalSynchronisation62(t *testing.T) { testCanonicalSynchronisation(t, 62, FullSync) }
|
||||||
func TestCanonicalSynchronisation63Full(t *testing.T) { testCanonicalSynchronisation(t, 63, FullSync) }
|
func TestCanonicalSynchronisation63Full(t *testing.T) { testCanonicalSynchronisation(t, 63, FullSync) }
|
||||||
func TestCanonicalSynchronisation63Fast(t *testing.T) { testCanonicalSynchronisation(t, 63, FastSync) }
|
func TestCanonicalSynchronisation63Fast(t *testing.T) { testCanonicalSynchronisation(t, 63, FastSync) }
|
||||||
|
|
@ -759,7 +687,6 @@ func testCanonicalSynchronisation(t *testing.T, protocol int, mode SyncMode) {
|
||||||
|
|
||||||
// Tests that if a large batch of blocks are being downloaded, it is throttled
|
// Tests that if a large batch of blocks are being downloaded, it is throttled
|
||||||
// until the cached blocks are retrieved.
|
// until the cached blocks are retrieved.
|
||||||
func TestThrottling61(t *testing.T) { testThrottling(t, 61, FullSync) }
|
|
||||||
func TestThrottling62(t *testing.T) { testThrottling(t, 62, FullSync) }
|
func TestThrottling62(t *testing.T) { testThrottling(t, 62, FullSync) }
|
||||||
func TestThrottling63Full(t *testing.T) { testThrottling(t, 63, FullSync) }
|
func TestThrottling63Full(t *testing.T) { testThrottling(t, 63, FullSync) }
|
||||||
func TestThrottling63Fast(t *testing.T) { testThrottling(t, 63, FastSync) }
|
func TestThrottling63Fast(t *testing.T) { testThrottling(t, 63, FastSync) }
|
||||||
|
|
@ -845,7 +772,6 @@ func testThrottling(t *testing.T, protocol int, mode SyncMode) {
|
||||||
// Tests that simple synchronization against a forked chain works correctly. In
|
// Tests that simple synchronization against a forked chain works correctly. In
|
||||||
// this test common ancestor lookup should *not* be short circuited, and a full
|
// this test common ancestor lookup should *not* be short circuited, and a full
|
||||||
// binary search should be executed.
|
// binary search should be executed.
|
||||||
func TestForkedSync61(t *testing.T) { testForkedSync(t, 61, FullSync) }
|
|
||||||
func TestForkedSync62(t *testing.T) { testForkedSync(t, 62, FullSync) }
|
func TestForkedSync62(t *testing.T) { testForkedSync(t, 62, FullSync) }
|
||||||
func TestForkedSync63Full(t *testing.T) { testForkedSync(t, 63, FullSync) }
|
func TestForkedSync63Full(t *testing.T) { testForkedSync(t, 63, FullSync) }
|
||||||
func TestForkedSync63Fast(t *testing.T) { testForkedSync(t, 63, FastSync) }
|
func TestForkedSync63Fast(t *testing.T) { testForkedSync(t, 63, FastSync) }
|
||||||
|
|
@ -881,7 +807,6 @@ func testForkedSync(t *testing.T, protocol int, mode SyncMode) {
|
||||||
|
|
||||||
// Tests that synchronising against a much shorter but much heavyer fork works
|
// Tests that synchronising against a much shorter but much heavyer fork works
|
||||||
// corrently and is not dropped.
|
// corrently and is not dropped.
|
||||||
func TestHeavyForkedSync61(t *testing.T) { testHeavyForkedSync(t, 61, FullSync) }
|
|
||||||
func TestHeavyForkedSync62(t *testing.T) { testHeavyForkedSync(t, 62, FullSync) }
|
func TestHeavyForkedSync62(t *testing.T) { testHeavyForkedSync(t, 62, FullSync) }
|
||||||
func TestHeavyForkedSync63Full(t *testing.T) { testHeavyForkedSync(t, 63, FullSync) }
|
func TestHeavyForkedSync63Full(t *testing.T) { testHeavyForkedSync(t, 63, FullSync) }
|
||||||
func TestHeavyForkedSync63Fast(t *testing.T) { testHeavyForkedSync(t, 63, FastSync) }
|
func TestHeavyForkedSync63Fast(t *testing.T) { testHeavyForkedSync(t, 63, FastSync) }
|
||||||
|
|
@ -915,24 +840,9 @@ func testHeavyForkedSync(t *testing.T, protocol int, mode SyncMode) {
|
||||||
assertOwnForkedChain(t, tester, common+1, []int{common + fork + 1, common + fork/2 + 1})
|
assertOwnForkedChain(t, tester, common+1, []int{common + fork + 1, common + fork/2 + 1})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
|
||||||
if err := tester.downloader.DeliverHashes("bad peer", []common.Hash{}); err != errNoSyncActive {
|
|
||||||
t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive)
|
|
||||||
}
|
|
||||||
if err := tester.downloader.DeliverBlocks("bad peer", []*types.Block{}); err != errNoSyncActive {
|
|
||||||
t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that chain forks are contained within a certain interval of the current
|
// Tests that chain forks are contained within a certain interval of the current
|
||||||
// chain head, ensuring that malicious peers cannot waste resources by feeding
|
// chain head, ensuring that malicious peers cannot waste resources by feeding
|
||||||
// long dead chains.
|
// long dead chains.
|
||||||
func TestBoundedForkedSync61(t *testing.T) { testBoundedForkedSync(t, 61, FullSync) }
|
|
||||||
func TestBoundedForkedSync62(t *testing.T) { testBoundedForkedSync(t, 62, FullSync) }
|
func TestBoundedForkedSync62(t *testing.T) { testBoundedForkedSync(t, 62, FullSync) }
|
||||||
func TestBoundedForkedSync63Full(t *testing.T) { testBoundedForkedSync(t, 63, FullSync) }
|
func TestBoundedForkedSync63Full(t *testing.T) { testBoundedForkedSync(t, 63, FullSync) }
|
||||||
func TestBoundedForkedSync63Fast(t *testing.T) { testBoundedForkedSync(t, 63, FastSync) }
|
func TestBoundedForkedSync63Fast(t *testing.T) { testBoundedForkedSync(t, 63, FastSync) }
|
||||||
|
|
@ -968,7 +878,6 @@ func testBoundedForkedSync(t *testing.T, protocol int, mode SyncMode) {
|
||||||
// Tests that chain forks are contained within a certain interval of the current
|
// Tests that chain forks are contained within a certain interval of the current
|
||||||
// chain head for short but heavy forks too. These are a bit special because they
|
// chain head for short but heavy forks too. These are a bit special because they
|
||||||
// take different ancestor lookup paths.
|
// take different ancestor lookup paths.
|
||||||
func TestBoundedHeavyForkedSync61(t *testing.T) { testBoundedHeavyForkedSync(t, 61, FullSync) }
|
|
||||||
func TestBoundedHeavyForkedSync62(t *testing.T) { testBoundedHeavyForkedSync(t, 62, FullSync) }
|
func TestBoundedHeavyForkedSync62(t *testing.T) { testBoundedHeavyForkedSync(t, 62, FullSync) }
|
||||||
func TestBoundedHeavyForkedSync63Full(t *testing.T) { testBoundedHeavyForkedSync(t, 63, FullSync) }
|
func TestBoundedHeavyForkedSync63Full(t *testing.T) { testBoundedHeavyForkedSync(t, 63, FullSync) }
|
||||||
func TestBoundedHeavyForkedSync63Fast(t *testing.T) { testBoundedHeavyForkedSync(t, 63, FastSync) }
|
func TestBoundedHeavyForkedSync63Fast(t *testing.T) { testBoundedHeavyForkedSync(t, 63, FastSync) }
|
||||||
|
|
@ -1039,7 +948,6 @@ func TestInactiveDownloader63(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that a canceled download wipes all previously accumulated state.
|
// Tests that a canceled download wipes all previously accumulated state.
|
||||||
func TestCancel61(t *testing.T) { testCancel(t, 61, FullSync) }
|
|
||||||
func TestCancel62(t *testing.T) { testCancel(t, 62, FullSync) }
|
func TestCancel62(t *testing.T) { testCancel(t, 62, FullSync) }
|
||||||
func TestCancel63Full(t *testing.T) { testCancel(t, 63, FullSync) }
|
func TestCancel63Full(t *testing.T) { testCancel(t, 63, FullSync) }
|
||||||
func TestCancel63Fast(t *testing.T) { testCancel(t, 63, FastSync) }
|
func TestCancel63Fast(t *testing.T) { testCancel(t, 63, FastSync) }
|
||||||
|
|
@ -1081,7 +989,6 @@ func testCancel(t *testing.T, protocol int, mode SyncMode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that synchronisation from multiple peers works as intended (multi thread sanity test).
|
// Tests that synchronisation from multiple peers works as intended (multi thread sanity test).
|
||||||
func TestMultiSynchronisation61(t *testing.T) { testMultiSynchronisation(t, 61, FullSync) }
|
|
||||||
func TestMultiSynchronisation62(t *testing.T) { testMultiSynchronisation(t, 62, FullSync) }
|
func TestMultiSynchronisation62(t *testing.T) { testMultiSynchronisation(t, 62, FullSync) }
|
||||||
func TestMultiSynchronisation63Full(t *testing.T) { testMultiSynchronisation(t, 63, FullSync) }
|
func TestMultiSynchronisation63Full(t *testing.T) { testMultiSynchronisation(t, 63, FullSync) }
|
||||||
func TestMultiSynchronisation63Fast(t *testing.T) { testMultiSynchronisation(t, 63, FastSync) }
|
func TestMultiSynchronisation63Fast(t *testing.T) { testMultiSynchronisation(t, 63, FastSync) }
|
||||||
|
|
@ -1112,7 +1019,6 @@ func testMultiSynchronisation(t *testing.T, protocol int, mode SyncMode) {
|
||||||
|
|
||||||
// Tests that synchronisations behave well in multi-version protocol environments
|
// Tests that synchronisations behave well in multi-version protocol environments
|
||||||
// and not wreak havoc on other nodes in the network.
|
// and not wreak havoc on other nodes in the network.
|
||||||
func TestMultiProtoSynchronisation61(t *testing.T) { testMultiProtoSync(t, 61, FullSync) }
|
|
||||||
func TestMultiProtoSynchronisation62(t *testing.T) { testMultiProtoSync(t, 62, FullSync) }
|
func TestMultiProtoSynchronisation62(t *testing.T) { testMultiProtoSync(t, 62, FullSync) }
|
||||||
func TestMultiProtoSynchronisation63Full(t *testing.T) { testMultiProtoSync(t, 63, FullSync) }
|
func TestMultiProtoSynchronisation63Full(t *testing.T) { testMultiProtoSync(t, 63, FullSync) }
|
||||||
func TestMultiProtoSynchronisation63Fast(t *testing.T) { testMultiProtoSync(t, 63, FastSync) }
|
func TestMultiProtoSynchronisation63Fast(t *testing.T) { testMultiProtoSync(t, 63, FastSync) }
|
||||||
|
|
@ -1131,7 +1037,6 @@ func testMultiProtoSync(t *testing.T, protocol int, mode SyncMode) {
|
||||||
tester := newTester()
|
tester := newTester()
|
||||||
defer tester.terminate()
|
defer tester.terminate()
|
||||||
|
|
||||||
tester.newPeer("peer 61", 61, hashes, nil, blocks, nil)
|
|
||||||
tester.newPeer("peer 62", 62, hashes, headers, blocks, nil)
|
tester.newPeer("peer 62", 62, hashes, headers, blocks, nil)
|
||||||
tester.newPeer("peer 63", 63, hashes, headers, blocks, receipts)
|
tester.newPeer("peer 63", 63, hashes, headers, blocks, receipts)
|
||||||
tester.newPeer("peer 64", 64, hashes, headers, blocks, receipts)
|
tester.newPeer("peer 64", 64, hashes, headers, blocks, receipts)
|
||||||
|
|
@ -1143,7 +1048,7 @@ func testMultiProtoSync(t *testing.T, protocol int, mode SyncMode) {
|
||||||
assertOwnChain(t, tester, targetBlocks+1)
|
assertOwnChain(t, tester, targetBlocks+1)
|
||||||
|
|
||||||
// Check that no peers have been dropped off
|
// Check that no peers have been dropped off
|
||||||
for _, version := range []int{61, 62, 63, 64} {
|
for _, version := range []int{62, 63, 64} {
|
||||||
peer := fmt.Sprintf("peer %d", version)
|
peer := fmt.Sprintf("peer %d", version)
|
||||||
if _, ok := tester.peerHashes[peer]; !ok {
|
if _, ok := tester.peerHashes[peer]; !ok {
|
||||||
t.Errorf("%s dropped", peer)
|
t.Errorf("%s dropped", peer)
|
||||||
|
|
@ -1368,7 +1273,6 @@ func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) {
|
||||||
|
|
||||||
// Tests that a peer advertising an high TD doesn't get to stall the downloader
|
// Tests that a peer advertising an high TD doesn't get to stall the downloader
|
||||||
// afterwards by not sending any useful hashes.
|
// afterwards by not sending any useful hashes.
|
||||||
func TestHighTDStarvationAttack61(t *testing.T) { testHighTDStarvationAttack(t, 61, FullSync) }
|
|
||||||
func TestHighTDStarvationAttack62(t *testing.T) { testHighTDStarvationAttack(t, 62, FullSync) }
|
func TestHighTDStarvationAttack62(t *testing.T) { testHighTDStarvationAttack(t, 62, FullSync) }
|
||||||
func TestHighTDStarvationAttack63Full(t *testing.T) { testHighTDStarvationAttack(t, 63, FullSync) }
|
func TestHighTDStarvationAttack63Full(t *testing.T) { testHighTDStarvationAttack(t, 63, FullSync) }
|
||||||
func TestHighTDStarvationAttack63Fast(t *testing.T) { testHighTDStarvationAttack(t, 63, FastSync) }
|
func TestHighTDStarvationAttack63Fast(t *testing.T) { testHighTDStarvationAttack(t, 63, FastSync) }
|
||||||
|
|
@ -1391,7 +1295,6 @@ func testHighTDStarvationAttack(t *testing.T, protocol int, mode SyncMode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that misbehaving peers are disconnected, whilst behaving ones are not.
|
// Tests that misbehaving peers are disconnected, whilst behaving ones are not.
|
||||||
func TestBlockHeaderAttackerDropping61(t *testing.T) { testBlockHeaderAttackerDropping(t, 61) }
|
|
||||||
func TestBlockHeaderAttackerDropping62(t *testing.T) { testBlockHeaderAttackerDropping(t, 62) }
|
func TestBlockHeaderAttackerDropping62(t *testing.T) { testBlockHeaderAttackerDropping(t, 62) }
|
||||||
func TestBlockHeaderAttackerDropping63(t *testing.T) { testBlockHeaderAttackerDropping(t, 63) }
|
func TestBlockHeaderAttackerDropping63(t *testing.T) { testBlockHeaderAttackerDropping(t, 63) }
|
||||||
func TestBlockHeaderAttackerDropping64(t *testing.T) { testBlockHeaderAttackerDropping(t, 64) }
|
func TestBlockHeaderAttackerDropping64(t *testing.T) { testBlockHeaderAttackerDropping(t, 64) }
|
||||||
|
|
@ -1409,7 +1312,6 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol int) {
|
||||||
{errStallingPeer, true}, // Peer was detected to be stalling, drop it
|
{errStallingPeer, true}, // Peer was detected to be stalling, drop it
|
||||||
{errNoPeers, false}, // No peers to download from, soft race, no issue
|
{errNoPeers, false}, // No peers to download from, soft race, no issue
|
||||||
{errTimeout, true}, // No hashes received in due time, drop the peer
|
{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
|
{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
|
{errPeersUnavailable, true}, // Nobody had the advertised blocks, drop the advertiser
|
||||||
{errInvalidAncestor, true}, // Agreed upon ancestor is not acceptable, drop the chain rewriter
|
{errInvalidAncestor, true}, // Agreed upon ancestor is not acceptable, drop the chain rewriter
|
||||||
|
|
@ -1417,7 +1319,6 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol int) {
|
||||||
{errInvalidBlock, false}, // A bad peer was detected, but not the sync origin
|
{errInvalidBlock, false}, // A bad peer was detected, but not the sync origin
|
||||||
{errInvalidBody, 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
|
{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
|
{errCancelBlockFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
|
||||||
{errCancelHeaderFetch, 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
|
{errCancelBodyFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
|
||||||
|
|
@ -1450,7 +1351,6 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol int) {
|
||||||
|
|
||||||
// Tests that synchronisation progress (origin block number, current block number
|
// Tests that synchronisation progress (origin block number, current block number
|
||||||
// and highest block number) is tracked and updated correctly.
|
// and highest block number) is tracked and updated correctly.
|
||||||
func TestSyncProgress61(t *testing.T) { testSyncProgress(t, 61, FullSync) }
|
|
||||||
func TestSyncProgress62(t *testing.T) { testSyncProgress(t, 62, FullSync) }
|
func TestSyncProgress62(t *testing.T) { testSyncProgress(t, 62, FullSync) }
|
||||||
func TestSyncProgress63Full(t *testing.T) { testSyncProgress(t, 63, FullSync) }
|
func TestSyncProgress63Full(t *testing.T) { testSyncProgress(t, 63, FullSync) }
|
||||||
func TestSyncProgress63Fast(t *testing.T) { testSyncProgress(t, 63, FastSync) }
|
func TestSyncProgress63Fast(t *testing.T) { testSyncProgress(t, 63, FastSync) }
|
||||||
|
|
@ -1524,7 +1424,6 @@ func testSyncProgress(t *testing.T, protocol int, mode SyncMode) {
|
||||||
// Tests that synchronisation progress (origin block number and highest block
|
// Tests that synchronisation progress (origin block number and highest block
|
||||||
// number) is tracked and updated correctly in case of a fork (or manual head
|
// number) is tracked and updated correctly in case of a fork (or manual head
|
||||||
// revertal).
|
// revertal).
|
||||||
func TestForkedSyncProgress61(t *testing.T) { testForkedSyncProgress(t, 61, FullSync) }
|
|
||||||
func TestForkedSyncProgress62(t *testing.T) { testForkedSyncProgress(t, 62, FullSync) }
|
func TestForkedSyncProgress62(t *testing.T) { testForkedSyncProgress(t, 62, FullSync) }
|
||||||
func TestForkedSyncProgress63Full(t *testing.T) { testForkedSyncProgress(t, 63, FullSync) }
|
func TestForkedSyncProgress63Full(t *testing.T) { testForkedSyncProgress(t, 63, FullSync) }
|
||||||
func TestForkedSyncProgress63Fast(t *testing.T) { testForkedSyncProgress(t, 63, FastSync) }
|
func TestForkedSyncProgress63Fast(t *testing.T) { testForkedSyncProgress(t, 63, FastSync) }
|
||||||
|
|
@ -1601,7 +1500,6 @@ func testForkedSyncProgress(t *testing.T, protocol int, mode SyncMode) {
|
||||||
// Tests that if synchronisation is aborted due to some failure, then the progress
|
// Tests that if synchronisation is aborted due to some failure, then the progress
|
||||||
// origin is not updated in the next sync cycle, as it should be considered the
|
// origin is not updated in the next sync cycle, as it should be considered the
|
||||||
// continuation of the previous sync and not a new instance.
|
// continuation of the previous sync and not a new instance.
|
||||||
func TestFailedSyncProgress61(t *testing.T) { testFailedSyncProgress(t, 61, FullSync) }
|
|
||||||
func TestFailedSyncProgress62(t *testing.T) { testFailedSyncProgress(t, 62, FullSync) }
|
func TestFailedSyncProgress62(t *testing.T) { testFailedSyncProgress(t, 62, FullSync) }
|
||||||
func TestFailedSyncProgress63Full(t *testing.T) { testFailedSyncProgress(t, 63, FullSync) }
|
func TestFailedSyncProgress63Full(t *testing.T) { testFailedSyncProgress(t, 63, FullSync) }
|
||||||
func TestFailedSyncProgress63Fast(t *testing.T) { testFailedSyncProgress(t, 63, FastSync) }
|
func TestFailedSyncProgress63Fast(t *testing.T) { testFailedSyncProgress(t, 63, FastSync) }
|
||||||
|
|
@ -1679,7 +1577,6 @@ func testFailedSyncProgress(t *testing.T, protocol int, mode SyncMode) {
|
||||||
|
|
||||||
// Tests that if an attacker fakes a chain height, after the attack is detected,
|
// Tests that if an attacker fakes a chain height, after the attack is detected,
|
||||||
// the progress height is successfully reduced at the next sync invocation.
|
// the progress height is successfully reduced at the next sync invocation.
|
||||||
func TestFakedSyncProgress61(t *testing.T) { testFakedSyncProgress(t, 61, FullSync) }
|
|
||||||
func TestFakedSyncProgress62(t *testing.T) { testFakedSyncProgress(t, 62, FullSync) }
|
func TestFakedSyncProgress62(t *testing.T) { testFakedSyncProgress(t, 62, FullSync) }
|
||||||
func TestFakedSyncProgress63Full(t *testing.T) { testFakedSyncProgress(t, 63, FullSync) }
|
func TestFakedSyncProgress63Full(t *testing.T) { testFakedSyncProgress(t, 63, FullSync) }
|
||||||
func TestFakedSyncProgress63Fast(t *testing.T) { testFakedSyncProgress(t, 63, FastSync) }
|
func TestFakedSyncProgress63Fast(t *testing.T) { testFakedSyncProgress(t, 63, FastSync) }
|
||||||
|
|
|
||||||
|
|
@ -23,16 +23,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
hashInMeter = metrics.NewMeter("eth/downloader/hashes/in")
|
|
||||||
hashReqTimer = metrics.NewTimer("eth/downloader/hashes/req")
|
|
||||||
hashDropMeter = metrics.NewMeter("eth/downloader/hashes/drop")
|
|
||||||
hashTimeoutMeter = metrics.NewMeter("eth/downloader/hashes/timeout")
|
|
||||||
|
|
||||||
blockInMeter = metrics.NewMeter("eth/downloader/blocks/in")
|
|
||||||
blockReqTimer = metrics.NewTimer("eth/downloader/blocks/req")
|
|
||||||
blockDropMeter = metrics.NewMeter("eth/downloader/blocks/drop")
|
|
||||||
blockTimeoutMeter = metrics.NewMeter("eth/downloader/blocks/timeout")
|
|
||||||
|
|
||||||
headerInMeter = metrics.NewMeter("eth/downloader/headers/in")
|
headerInMeter = metrics.NewMeter("eth/downloader/headers/in")
|
||||||
headerReqTimer = metrics.NewTimer("eth/downloader/headers/req")
|
headerReqTimer = metrics.NewTimer("eth/downloader/headers/req")
|
||||||
headerDropMeter = metrics.NewMeter("eth/downloader/headers/drop")
|
headerDropMeter = metrics.NewMeter("eth/downloader/headers/drop")
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"math/big"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -37,10 +38,8 @@ const (
|
||||||
measurementImpact = 0.1 // The impact a single measurement has on a peer's final throughput value.
|
measurementImpact = 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
|
// Head hash and total difficulty retriever for
|
||||||
type relativeHashFetcherFn func(common.Hash) error
|
type currentHeadRetrievalFn func() (common.Hash, *big.Int)
|
||||||
type absoluteHashFetcherFn func(uint64, int) error
|
|
||||||
type blockFetcherFn func([]common.Hash) error
|
|
||||||
|
|
||||||
// Block header and body fetchers belonging to eth/62 and above
|
// Block header and body fetchers belonging to eth/62 and above
|
||||||
type relativeHeaderFetcherFn func(common.Hash, int, int, bool) error
|
type relativeHeaderFetcherFn func(common.Hash, int, int, bool) error
|
||||||
|
|
@ -57,8 +56,7 @@ var (
|
||||||
|
|
||||||
// peer represents an active peer from which hashes and blocks are retrieved.
|
// peer represents an active peer from which hashes and blocks are retrieved.
|
||||||
type peer struct {
|
type peer struct {
|
||||||
id string // Unique identifier of the peer
|
id string // Unique identifier of the peer
|
||||||
head common.Hash // Hash of the peers latest known block
|
|
||||||
|
|
||||||
headerIdle int32 // Current header activity state of the peer (idle = 0, active = 1)
|
headerIdle int32 // Current header activity state of the peer (idle = 0, active = 1)
|
||||||
blockIdle int32 // Current block activity state of the peer (idle = 0, active = 1)
|
blockIdle int32 // Current block activity state of the peer (idle = 0, active = 1)
|
||||||
|
|
@ -79,9 +77,7 @@ type peer struct {
|
||||||
|
|
||||||
lacking map[common.Hash]struct{} // 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
|
currentHead currentHeadRetrievalFn // Method to fetch the currently known head of the peer
|
||||||
getAbsHashes absoluteHashFetcherFn // [eth/61] Method to retrieve a batch of hashes from an absolute position
|
|
||||||
getBlocks blockFetcherFn // [eth/61] Method to retrieve a batch of blocks
|
|
||||||
|
|
||||||
getRelHeaders relativeHeaderFetcherFn // [eth/62] Method to retrieve a batch of headers from an origin hash
|
getRelHeaders relativeHeaderFetcherFn // [eth/62] Method to retrieve a batch of headers from an origin hash
|
||||||
getAbsHeaders absoluteHeaderFetcherFn // [eth/62] Method to retrieve a batch of headers from an absolute position
|
getAbsHeaders absoluteHeaderFetcherFn // [eth/62] Method to retrieve a batch of headers from an absolute position
|
||||||
|
|
@ -96,19 +92,14 @@ type peer struct {
|
||||||
|
|
||||||
// newPeer create a new downloader peer, with specific hash and block retrieval
|
// newPeer create a new downloader peer, with specific hash and block retrieval
|
||||||
// mechanisms.
|
// mechanisms.
|
||||||
func newPeer(id string, version int, head common.Hash,
|
func newPeer(id string, version int, currentHead currentHeadRetrievalFn,
|
||||||
getRelHashes relativeHashFetcherFn, getAbsHashes absoluteHashFetcherFn, getBlocks blockFetcherFn, // eth/61 callbacks, remove when upgrading
|
|
||||||
getRelHeaders relativeHeaderFetcherFn, getAbsHeaders absoluteHeaderFetcherFn, getBlockBodies blockBodyFetcherFn,
|
getRelHeaders relativeHeaderFetcherFn, getAbsHeaders absoluteHeaderFetcherFn, getBlockBodies blockBodyFetcherFn,
|
||||||
getReceipts receiptFetcherFn, getNodeData stateFetcherFn) *peer {
|
getReceipts receiptFetcherFn, getNodeData stateFetcherFn) *peer {
|
||||||
return &peer{
|
return &peer{
|
||||||
id: id,
|
id: id,
|
||||||
head: head,
|
|
||||||
lacking: make(map[common.Hash]struct{}),
|
lacking: make(map[common.Hash]struct{}),
|
||||||
|
|
||||||
getRelHashes: getRelHashes,
|
currentHead: currentHead,
|
||||||
getAbsHashes: getAbsHashes,
|
|
||||||
getBlocks: getBlocks,
|
|
||||||
|
|
||||||
getRelHeaders: getRelHeaders,
|
getRelHeaders: getRelHeaders,
|
||||||
getAbsHeaders: getAbsHeaders,
|
getAbsHeaders: getAbsHeaders,
|
||||||
getBlockBodies: getBlockBodies,
|
getBlockBodies: getBlockBodies,
|
||||||
|
|
@ -138,28 +129,6 @@ func (p *peer) Reset() {
|
||||||
p.lacking = make(map[common.Hash]struct{})
|
p.lacking = make(map[common.Hash]struct{})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch61 sends a block retrieval request to the remote peer.
|
|
||||||
func (p *peer) Fetch61(request *fetchRequest) error {
|
|
||||||
// Sanity check the protocol version
|
|
||||||
if p.version != 61 {
|
|
||||||
panic(fmt.Sprintf("block fetch [eth/61] requested on eth/%d", p.version))
|
|
||||||
}
|
|
||||||
// Short circuit if the peer is already fetching
|
|
||||||
if !atomic.CompareAndSwapInt32(&p.blockIdle, 0, 1) {
|
|
||||||
return errAlreadyFetching
|
|
||||||
}
|
|
||||||
p.blockStarted = time.Now()
|
|
||||||
|
|
||||||
// Convert the hash set to a retrievable slice
|
|
||||||
hashes := make([]common.Hash, 0, len(request.Hashes))
|
|
||||||
for hash, _ := range request.Hashes {
|
|
||||||
hashes = append(hashes, hash)
|
|
||||||
}
|
|
||||||
go p.getBlocks(hashes)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FetchHeaders sends a header retrieval request to the remote peer.
|
// FetchHeaders sends a header retrieval request to the remote peer.
|
||||||
func (p *peer) FetchHeaders(from uint64, count int) error {
|
func (p *peer) FetchHeaders(from uint64, count int) error {
|
||||||
// Sanity check the protocol version
|
// Sanity check the protocol version
|
||||||
|
|
@ -481,20 +450,6 @@ func (ps *peerSet) AllPeers() []*peer {
|
||||||
return list
|
return list
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockIdlePeers retrieves a flat list of all the currently idle peers within the
|
|
||||||
// active peer set, ordered by their reputation.
|
|
||||||
func (ps *peerSet) BlockIdlePeers() ([]*peer, int) {
|
|
||||||
idle := func(p *peer) bool {
|
|
||||||
return atomic.LoadInt32(&p.blockIdle) == 0
|
|
||||||
}
|
|
||||||
throughput := func(p *peer) float64 {
|
|
||||||
p.lock.RLock()
|
|
||||||
defer p.lock.RUnlock()
|
|
||||||
return p.blockThroughput
|
|
||||||
}
|
|
||||||
return ps.idlePeers(61, 61, idle, throughput)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HeaderIdlePeers retrieves a flat list of all the currently header-idle peers
|
// HeaderIdlePeers retrieves a flat list of all the currently header-idle peers
|
||||||
// within the active peer set, ordered by their reputation.
|
// within the active peer set, ordered by their reputation.
|
||||||
func (ps *peerSet) HeaderIdlePeers() ([]*peer, int) {
|
func (ps *peerSet) HeaderIdlePeers() ([]*peer, int) {
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,6 @@ var (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errNoFetchesPending = errors.New("no fetches pending")
|
errNoFetchesPending = errors.New("no fetches pending")
|
||||||
errStateSyncPending = errors.New("state trie sync already scheduled")
|
|
||||||
errStaleDelivery = errors.New("stale delivery")
|
errStaleDelivery = errors.New("stale delivery")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -74,10 +73,6 @@ type queue struct {
|
||||||
mode SyncMode // Synchronisation mode to decide on the block parts to schedule for fetching
|
mode SyncMode // Synchronisation mode to decide on the block parts to schedule for fetching
|
||||||
fastSyncPivot uint64 // Block number where the fast sync pivots into archive synchronisation mode
|
fastSyncPivot uint64 // Block number where the fast sync pivots into archive synchronisation mode
|
||||||
|
|
||||||
hashPool map[common.Hash]int // [eth/61] Pending hashes, mapping to their insertion index (priority)
|
|
||||||
hashQueue *prque.Prque // [eth/61] Priority queue of the block hashes to fetch
|
|
||||||
hashCounter int // [eth/61] Counter indexing the added hashes to ensure retrieval order
|
|
||||||
|
|
||||||
headerHead common.Hash // [eth/62] Hash of the last queued header to verify order
|
headerHead common.Hash // [eth/62] Hash of the last queued header to verify order
|
||||||
|
|
||||||
// Headers are "special", they download in batches, supported by a skeleton chain
|
// Headers are "special", they download in batches, supported by a skeleton chain
|
||||||
|
|
@ -85,7 +80,6 @@ type queue struct {
|
||||||
headerTaskQueue *prque.Prque // [eth/62] Priority queue of the skeleton indexes to fetch the filling headers for
|
headerTaskQueue *prque.Prque // [eth/62] Priority queue of the skeleton indexes to fetch the filling headers for
|
||||||
headerPeerMiss map[string]map[uint64]struct{} // [eth/62] Set of per-peer header batches known to be unavailable
|
headerPeerMiss map[string]map[uint64]struct{} // [eth/62] Set of per-peer header batches known to be unavailable
|
||||||
headerPendPool map[string]*fetchRequest // [eth/62] Currently pending header retrieval operations
|
headerPendPool map[string]*fetchRequest // [eth/62] Currently pending header retrieval operations
|
||||||
headerDonePool map[uint64]struct{} // [eth/62] Set of the completed header fetches
|
|
||||||
headerResults []*types.Header // [eth/62] Result cache accumulating the completed headers
|
headerResults []*types.Header // [eth/62] Result cache accumulating the completed headers
|
||||||
headerProced int // [eth/62] Number of headers already processed from the results
|
headerProced int // [eth/62] Number of headers already processed from the results
|
||||||
headerOffset uint64 // [eth/62] Number of the first header in the result cache
|
headerOffset uint64 // [eth/62] Number of the first header in the result cache
|
||||||
|
|
@ -124,8 +118,6 @@ type queue struct {
|
||||||
func newQueue(stateDb ethdb.Database) *queue {
|
func newQueue(stateDb ethdb.Database) *queue {
|
||||||
lock := new(sync.Mutex)
|
lock := new(sync.Mutex)
|
||||||
return &queue{
|
return &queue{
|
||||||
hashPool: make(map[common.Hash]int),
|
|
||||||
hashQueue: prque.New(),
|
|
||||||
headerPendPool: make(map[string]*fetchRequest),
|
headerPendPool: make(map[string]*fetchRequest),
|
||||||
headerContCh: make(chan bool),
|
headerContCh: make(chan bool),
|
||||||
blockTaskPool: make(map[common.Hash]*types.Header),
|
blockTaskPool: make(map[common.Hash]*types.Header),
|
||||||
|
|
@ -158,10 +150,6 @@ func (q *queue) Reset() {
|
||||||
q.mode = FullSync
|
q.mode = FullSync
|
||||||
q.fastSyncPivot = 0
|
q.fastSyncPivot = 0
|
||||||
|
|
||||||
q.hashPool = make(map[common.Hash]int)
|
|
||||||
q.hashQueue.Reset()
|
|
||||||
q.hashCounter = 0
|
|
||||||
|
|
||||||
q.headerHead = common.Hash{}
|
q.headerHead = common.Hash{}
|
||||||
|
|
||||||
q.headerPendPool = make(map[string]*fetchRequest)
|
q.headerPendPool = make(map[string]*fetchRequest)
|
||||||
|
|
@ -208,7 +196,7 @@ func (q *queue) PendingBlocks() int {
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
defer q.lock.Unlock()
|
defer q.lock.Unlock()
|
||||||
|
|
||||||
return q.hashQueue.Size() + q.blockTaskQueue.Size()
|
return q.blockTaskQueue.Size()
|
||||||
}
|
}
|
||||||
|
|
||||||
// PendingReceipts retrieves the number of block receipts pending for retrieval.
|
// PendingReceipts retrieves the number of block receipts pending for retrieval.
|
||||||
|
|
@ -272,7 +260,7 @@ func (q *queue) Idle() bool {
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
defer q.lock.Unlock()
|
defer q.lock.Unlock()
|
||||||
|
|
||||||
queued := q.hashQueue.Size() + q.blockTaskQueue.Size() + q.receiptTaskQueue.Size() + q.stateTaskQueue.Size()
|
queued := q.blockTaskQueue.Size() + q.receiptTaskQueue.Size() + q.stateTaskQueue.Size()
|
||||||
pending := len(q.blockPendPool) + len(q.receiptPendPool) + len(q.statePendPool)
|
pending := len(q.blockPendPool) + len(q.receiptPendPool) + len(q.statePendPool)
|
||||||
cached := len(q.blockDonePool) + len(q.receiptDonePool)
|
cached := len(q.blockDonePool) + len(q.receiptDonePool)
|
||||||
|
|
||||||
|
|
@ -323,34 +311,6 @@ func (q *queue) ShouldThrottleReceipts() bool {
|
||||||
return pending >= len(q.resultCache)-len(q.receiptDonePool)
|
return pending >= len(q.resultCache)-len(q.receiptDonePool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schedule61 adds a set of hashes for the download queue for scheduling, returning
|
|
||||||
// the new hashes encountered.
|
|
||||||
func (q *queue) Schedule61(hashes []common.Hash, fifo bool) []common.Hash {
|
|
||||||
q.lock.Lock()
|
|
||||||
defer q.lock.Unlock()
|
|
||||||
|
|
||||||
// Insert all the hashes prioritised in the arrival order
|
|
||||||
inserts := make([]common.Hash, 0, len(hashes))
|
|
||||||
for _, hash := range hashes {
|
|
||||||
// Skip anything we already have
|
|
||||||
if old, ok := q.hashPool[hash]; ok {
|
|
||||||
glog.V(logger.Warn).Infof("Hash %x already scheduled at index %v", hash, old)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Update the counters and insert the hash
|
|
||||||
q.hashCounter = q.hashCounter + 1
|
|
||||||
inserts = append(inserts, hash)
|
|
||||||
|
|
||||||
q.hashPool[hash] = q.hashCounter
|
|
||||||
if fifo {
|
|
||||||
q.hashQueue.Push(hash, -float32(q.hashCounter)) // Lowest gets schedules first
|
|
||||||
} else {
|
|
||||||
q.hashQueue.Push(hash, float32(q.hashCounter)) // Highest gets schedules first
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return inserts
|
|
||||||
}
|
|
||||||
|
|
||||||
// ScheduleSkeleton adds a batch of header retrieval tasks to the queue to fill
|
// ScheduleSkeleton adds a batch of header retrieval tasks to the queue to fill
|
||||||
// up an already retrieved header skeleton.
|
// up an already retrieved header skeleton.
|
||||||
func (q *queue) ScheduleSkeleton(from uint64, skeleton []*types.Header) {
|
func (q *queue) ScheduleSkeleton(from uint64, skeleton []*types.Header) {
|
||||||
|
|
@ -550,15 +510,6 @@ func (q *queue) ReserveHeaders(p *peer, count int) *fetchRequest {
|
||||||
return request
|
return request
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReserveBlocks reserves a set of block hashes for the given peer, skipping any
|
|
||||||
// previously failed download.
|
|
||||||
func (q *queue) ReserveBlocks(p *peer, count int) *fetchRequest {
|
|
||||||
q.lock.Lock()
|
|
||||||
defer q.lock.Unlock()
|
|
||||||
|
|
||||||
return q.reserveHashes(p, count, q.hashQueue, nil, q.blockPendPool, len(q.resultCache)-len(q.blockDonePool))
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReserveNodeData reserves a set of node data hashes for the given peer, skipping
|
// ReserveNodeData reserves a set of node data hashes for the given peer, skipping
|
||||||
// any previously failed download.
|
// any previously failed download.
|
||||||
func (q *queue) ReserveNodeData(p *peer, count int) *fetchRequest {
|
func (q *queue) ReserveNodeData(p *peer, count int) *fetchRequest {
|
||||||
|
|
@ -753,11 +704,6 @@ func (q *queue) CancelHeaders(request *fetchRequest) {
|
||||||
q.cancel(request, q.headerTaskQueue, q.headerPendPool)
|
q.cancel(request, q.headerTaskQueue, q.headerPendPool)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CancelBlocks aborts a fetch request, returning all pending hashes to the queue.
|
|
||||||
func (q *queue) CancelBlocks(request *fetchRequest) {
|
|
||||||
q.cancel(request, q.hashQueue, q.blockPendPool)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CancelBodies aborts a body fetch request, returning all pending headers to the
|
// CancelBodies aborts a body fetch request, returning all pending headers to the
|
||||||
// task queue.
|
// task queue.
|
||||||
func (q *queue) CancelBodies(request *fetchRequest) {
|
func (q *queue) CancelBodies(request *fetchRequest) {
|
||||||
|
|
@ -801,9 +747,6 @@ func (q *queue) Revoke(peerId string) {
|
||||||
defer q.lock.Unlock()
|
defer q.lock.Unlock()
|
||||||
|
|
||||||
if request, ok := q.blockPendPool[peerId]; ok {
|
if request, ok := q.blockPendPool[peerId]; ok {
|
||||||
for hash, index := range request.Hashes {
|
|
||||||
q.hashQueue.Push(hash, float32(index))
|
|
||||||
}
|
|
||||||
for _, header := range request.Headers {
|
for _, header := range request.Headers {
|
||||||
q.blockTaskQueue.Push(header, -float32(header.Number.Uint64()))
|
q.blockTaskQueue.Push(header, -float32(header.Number.Uint64()))
|
||||||
}
|
}
|
||||||
|
|
@ -832,15 +775,6 @@ func (q *queue) ExpireHeaders(timeout time.Duration) map[string]int {
|
||||||
return q.expire(timeout, q.headerPendPool, q.headerTaskQueue, headerTimeoutMeter)
|
return q.expire(timeout, q.headerPendPool, q.headerTaskQueue, headerTimeoutMeter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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) map[string]int {
|
|
||||||
q.lock.Lock()
|
|
||||||
defer q.lock.Unlock()
|
|
||||||
|
|
||||||
return q.expire(timeout, q.blockPendPool, q.hashQueue, blockTimeoutMeter)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExpireBodies checks for in flight block body requests that exceeded a timeout
|
// ExpireBodies checks for in flight block body requests that exceeded a timeout
|
||||||
// allowance, canceling them and returning the responsible peers for penalisation.
|
// allowance, canceling them and returning the responsible peers for penalisation.
|
||||||
func (q *queue) ExpireBodies(timeout time.Duration) map[string]int {
|
func (q *queue) ExpireBodies(timeout time.Duration) map[string]int {
|
||||||
|
|
@ -907,74 +841,6 @@ func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest,
|
||||||
return expiries
|
return expiries
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 0, errNoFetchesPending
|
|
||||||
}
|
|
||||||
blockReqTimer.UpdateSince(request.Time)
|
|
||||||
delete(q.blockPendPool, id)
|
|
||||||
|
|
||||||
// If no blocks were retrieved, mark them as unavailable for the origin peer
|
|
||||||
if len(blocks) == 0 {
|
|
||||||
for hash, _ := range request.Hashes {
|
|
||||||
request.Peer.MarkLacking(hash)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Iterate over the downloaded blocks and add each of them
|
|
||||||
accepted, errs := 0, make([]error, 0)
|
|
||||||
for _, block := range blocks {
|
|
||||||
// Skip any blocks that were not requested
|
|
||||||
hash := block.Hash()
|
|
||||||
if _, ok := request.Hashes[hash]; !ok {
|
|
||||||
errs = append(errs, fmt.Errorf("non-requested block %x", hash))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Reconstruct the next result if contents match up
|
|
||||||
index := int(block.Number().Int64() - int64(q.resultOffset))
|
|
||||||
if index >= len(q.resultCache) || index < 0 {
|
|
||||||
errs = []error{errInvalidChain}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
q.resultCache[index] = &fetchResult{
|
|
||||||
Header: block.Header(),
|
|
||||||
Transactions: block.Transactions(),
|
|
||||||
Uncles: block.Uncles(),
|
|
||||||
}
|
|
||||||
q.blockDonePool[block.Hash()] = struct{}{}
|
|
||||||
|
|
||||||
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 accepted, nil
|
|
||||||
case len(errs) == 1 && (errs[0] == errInvalidChain || errs[0] == errInvalidBlock):
|
|
||||||
return accepted, errs[0]
|
|
||||||
case len(errs) == len(blocks):
|
|
||||||
return accepted, errStaleDelivery
|
|
||||||
default:
|
|
||||||
return accepted, fmt.Errorf("multiple failures: %v", errs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeliverHeaders injects a header retrieval response into the header results
|
// DeliverHeaders injects a header retrieval response into the header results
|
||||||
// cache. This method either accepts all headers it received, or none of them
|
// cache. This method either accepts all headers it received, or none of them
|
||||||
// if they do not map correctly to the skeleton.
|
// if they do not map correctly to the skeleton.
|
||||||
|
|
|
||||||
|
|
@ -73,26 +73,6 @@ type dataPack interface {
|
||||||
Stats() string
|
Stats() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// hashPack is a batch of block hashes returned by a peer (eth/61).
|
|
||||||
type hashPack struct {
|
|
||||||
peerId string
|
|
||||||
hashes []common.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *hashPack) PeerId() string { return p.peerId }
|
|
||||||
func (p *hashPack) Items() int { return len(p.hashes) }
|
|
||||||
func (p *hashPack) Stats() string { return fmt.Sprintf("%d", len(p.hashes)) }
|
|
||||||
|
|
||||||
// blockPack is a batch of blocks returned by a peer (eth/61).
|
|
||||||
type blockPack struct {
|
|
||||||
peerId string
|
|
||||||
blocks []*types.Block
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *blockPack) PeerId() string { return p.peerId }
|
|
||||||
func (p *blockPack) Items() int { return len(p.blocks) }
|
|
||||||
func (p *blockPack) Stats() string { return fmt.Sprintf("%d", len(p.blocks)) }
|
|
||||||
|
|
||||||
// headerPack is a batch of block headers returned by a peer.
|
// headerPack is a batch of block headers returned by a peer.
|
||||||
type headerPack struct {
|
type headerPack struct {
|
||||||
peerId string
|
peerId string
|
||||||
|
|
|
||||||
|
|
@ -48,9 +48,6 @@ var (
|
||||||
// blockRetrievalFn is a callback type for retrieving a block from the local chain.
|
// blockRetrievalFn is a callback type for retrieving a block from the local chain.
|
||||||
type blockRetrievalFn func(common.Hash) *types.Block
|
type blockRetrievalFn func(common.Hash) *types.Block
|
||||||
|
|
||||||
// blockRequesterFn is a callback type for sending a block retrieval request.
|
|
||||||
type blockRequesterFn func([]common.Hash) error
|
|
||||||
|
|
||||||
// headerRequesterFn is a callback type for sending a header retrieval request.
|
// headerRequesterFn is a callback type for sending a header retrieval request.
|
||||||
type headerRequesterFn func(common.Hash) error
|
type headerRequesterFn func(common.Hash) error
|
||||||
|
|
||||||
|
|
@ -82,7 +79,6 @@ type announce struct {
|
||||||
|
|
||||||
origin string // Identifier of the peer originating the notification
|
origin string // Identifier of the peer originating the notification
|
||||||
|
|
||||||
fetch61 blockRequesterFn // [eth/61] Fetcher function to retrieve an announced block
|
|
||||||
fetchHeader headerRequesterFn // [eth/62] Fetcher function to retrieve the header of an announced block
|
fetchHeader headerRequesterFn // [eth/62] Fetcher function to retrieve the header of an announced block
|
||||||
fetchBodies bodyRequesterFn // [eth/62] Fetcher function to retrieve the body of an announced block
|
fetchBodies bodyRequesterFn // [eth/62] Fetcher function to retrieve the body of an announced block
|
||||||
}
|
}
|
||||||
|
|
@ -191,14 +187,12 @@ func (f *Fetcher) Stop() {
|
||||||
// Notify announces the fetcher of the potential availability of a new block in
|
// Notify announces the fetcher of the potential availability of a new block in
|
||||||
// the network.
|
// the network.
|
||||||
func (f *Fetcher) Notify(peer string, hash common.Hash, number uint64, time time.Time,
|
func (f *Fetcher) Notify(peer string, hash common.Hash, number uint64, time time.Time,
|
||||||
blockFetcher blockRequesterFn, // eth/61 specific whole block fetcher
|
|
||||||
headerFetcher headerRequesterFn, bodyFetcher bodyRequesterFn) error {
|
headerFetcher headerRequesterFn, bodyFetcher bodyRequesterFn) error {
|
||||||
block := &announce{
|
block := &announce{
|
||||||
hash: hash,
|
hash: hash,
|
||||||
number: number,
|
number: number,
|
||||||
time: time,
|
time: time,
|
||||||
origin: peer,
|
origin: peer,
|
||||||
fetch61: blockFetcher,
|
|
||||||
fetchHeader: headerFetcher,
|
fetchHeader: headerFetcher,
|
||||||
fetchBodies: bodyFetcher,
|
fetchBodies: bodyFetcher,
|
||||||
}
|
}
|
||||||
|
|
@ -224,34 +218,6 @@ func (f *Fetcher) Enqueue(peer string, block *types.Block) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilterBlocks extracts all the blocks that were explicitly requested by the fetcher,
|
|
||||||
// returning those that should be handled differently.
|
|
||||||
func (f *Fetcher) FilterBlocks(blocks types.Blocks) types.Blocks {
|
|
||||||
glog.V(logger.Detail).Infof("[eth/61] filtering %d blocks", len(blocks))
|
|
||||||
|
|
||||||
// Send the filter channel to the fetcher
|
|
||||||
filter := make(chan []*types.Block)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case f.blockFilter <- filter:
|
|
||||||
case <-f.quit:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Request the filtering of the block list
|
|
||||||
select {
|
|
||||||
case filter <- blocks:
|
|
||||||
case <-f.quit:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Retrieve the blocks remaining after filtering
|
|
||||||
select {
|
|
||||||
case blocks := <-filter:
|
|
||||||
return blocks
|
|
||||||
case <-f.quit:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterHeaders extracts all the headers that were explicitly requested by the fetcher,
|
// FilterHeaders extracts all the headers that were explicitly requested by the fetcher,
|
||||||
// returning those that should be handled differently.
|
// returning those that should be handled differently.
|
||||||
func (f *Fetcher) FilterHeaders(headers []*types.Header, time time.Time) []*types.Header {
|
func (f *Fetcher) FilterHeaders(headers []*types.Header, time time.Time) []*types.Header {
|
||||||
|
|
@ -413,7 +379,7 @@ func (f *Fetcher) loop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Send out all block (eth/61) or header (eth/62) requests
|
// Send out all block header requests
|
||||||
for peer, hashes := range request {
|
for peer, hashes := range request {
|
||||||
if glog.V(logger.Detail) && len(hashes) > 0 {
|
if glog.V(logger.Detail) && len(hashes) > 0 {
|
||||||
list := "["
|
list := "["
|
||||||
|
|
@ -421,29 +387,17 @@ func (f *Fetcher) loop() {
|
||||||
list += fmt.Sprintf("%x…, ", hash[:4])
|
list += fmt.Sprintf("%x…, ", hash[:4])
|
||||||
}
|
}
|
||||||
list = list[:len(list)-2] + "]"
|
list = list[:len(list)-2] + "]"
|
||||||
|
glog.V(logger.Detail).Infof("[eth/62] Peer %s: fetching headers %s", peer, list)
|
||||||
if f.fetching[hashes[0]].fetch61 != nil {
|
|
||||||
glog.V(logger.Detail).Infof("[eth/61] Peer %s: fetching blocks %s", peer, list)
|
|
||||||
} else {
|
|
||||||
glog.V(logger.Detail).Infof("[eth/62] Peer %s: fetching headers %s", peer, list)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Create a closure of the fetch and schedule in on a new thread
|
// Create a closure of the fetch and schedule in on a new thread
|
||||||
fetchBlocks, fetchHeader, hashes := f.fetching[hashes[0]].fetch61, f.fetching[hashes[0]].fetchHeader, hashes
|
fetchHeader, hashes := f.fetching[hashes[0]].fetchHeader, hashes
|
||||||
go func() {
|
go func() {
|
||||||
if f.fetchingHook != nil {
|
if f.fetchingHook != nil {
|
||||||
f.fetchingHook(hashes)
|
f.fetchingHook(hashes)
|
||||||
}
|
}
|
||||||
if fetchBlocks != nil {
|
for _, hash := range hashes {
|
||||||
// Use old eth/61 protocol to retrieve whole blocks
|
headerFetchMeter.Mark(1)
|
||||||
blockFetchMeter.Mark(int64(len(hashes)))
|
fetchHeader(hash) // Suboptimal, but protocol doesn't allow batch header retrievals
|
||||||
fetchBlocks(hashes)
|
|
||||||
} else {
|
|
||||||
// Use new eth/62 protocol to retrieve headers first
|
|
||||||
for _, hash := range hashes {
|
|
||||||
headerFetchMeter.Mark(1)
|
|
||||||
fetchHeader(hash) // Suboptimal, but protocol doesn't allow batch header retrievals
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
@ -486,46 +440,6 @@ func (f *Fetcher) loop() {
|
||||||
// Schedule the next fetch if blocks are still pending
|
// Schedule the next fetch if blocks are still pending
|
||||||
f.rescheduleComplete(completeTimer)
|
f.rescheduleComplete(completeTimer)
|
||||||
|
|
||||||
case filter := <-f.blockFilter:
|
|
||||||
// Blocks arrived, extract any explicit fetches, return all else
|
|
||||||
var blocks types.Blocks
|
|
||||||
select {
|
|
||||||
case blocks = <-filter:
|
|
||||||
case <-f.quit:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
blockFilterInMeter.Mark(int64(len(blocks)))
|
|
||||||
|
|
||||||
explicit, download := []*types.Block{}, []*types.Block{}
|
|
||||||
for _, block := range blocks {
|
|
||||||
hash := block.Hash()
|
|
||||||
|
|
||||||
// Filter explicitly requested blocks from hash announcements
|
|
||||||
if f.fetching[hash] != nil && f.queued[hash] == nil {
|
|
||||||
// Discard if already imported by other means
|
|
||||||
if f.getBlock(hash) == nil {
|
|
||||||
explicit = append(explicit, block)
|
|
||||||
} else {
|
|
||||||
f.forgetHash(hash)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
download = append(download, block)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
blockFilterOutMeter.Mark(int64(len(download)))
|
|
||||||
select {
|
|
||||||
case filter <- download:
|
|
||||||
case <-f.quit:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Schedule the retrieved blocks for ordered import
|
|
||||||
for _, block := range explicit {
|
|
||||||
if announce := f.fetching[block.Hash()]; announce != nil {
|
|
||||||
f.enqueue(announce.origin, block)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case filter := <-f.headerFilter:
|
case filter := <-f.headerFilter:
|
||||||
// Headers arrived from a remote peer. Extract those that were explicitly
|
// Headers arrived from a remote peer. Extract those that were explicitly
|
||||||
// requested by the fetcher, and return everything else so it's delivered
|
// requested by the fetcher, and return everything else so it's delivered
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ var (
|
||||||
// contains a transaction and every 5th an uncle to allow testing correct block
|
// contains a transaction and every 5th an uncle to allow testing correct block
|
||||||
// reassembly.
|
// reassembly.
|
||||||
func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block) {
|
func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block) {
|
||||||
blocks, _ := core.GenerateChain(parent, testdb, n, func(i int, block *core.BlockGen) {
|
blocks, _ := core.GenerateChain(nil, parent, testdb, n, func(i int, block *core.BlockGen) {
|
||||||
block.SetCoinbase(common.Address{seed})
|
block.SetCoinbase(common.Address{seed})
|
||||||
|
|
||||||
// If the block number is multiple of 3, send a bonus transaction to the miner
|
// If the block number is multiple of 3, send a bonus transaction to the miner
|
||||||
|
|
@ -152,28 +152,6 @@ func (f *fetcherTester) dropPeer(peer string) {
|
||||||
f.drops[peer] = true
|
f.drops[peer] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeBlockFetcher retrieves a block fetcher associated with a simulated peer.
|
|
||||||
func (f *fetcherTester) makeBlockFetcher(blocks map[common.Hash]*types.Block) blockRequesterFn {
|
|
||||||
closure := make(map[common.Hash]*types.Block)
|
|
||||||
for hash, block := range blocks {
|
|
||||||
closure[hash] = block
|
|
||||||
}
|
|
||||||
// Create a function that returns blocks from the closure
|
|
||||||
return func(hashes []common.Hash) error {
|
|
||||||
// Gather the blocks to return
|
|
||||||
blocks := make([]*types.Block, 0, len(hashes))
|
|
||||||
for _, hash := range hashes {
|
|
||||||
if block, ok := closure[hash]; ok {
|
|
||||||
blocks = append(blocks, block)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Return on a new thread
|
|
||||||
go f.fetcher.FilterBlocks(blocks)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeHeaderFetcher retrieves a block header fetcher associated with a simulated peer.
|
// makeHeaderFetcher retrieves a block header fetcher associated with a simulated peer.
|
||||||
func (f *fetcherTester) makeHeaderFetcher(blocks map[common.Hash]*types.Block, drift time.Duration) headerRequesterFn {
|
func (f *fetcherTester) makeHeaderFetcher(blocks map[common.Hash]*types.Block, drift time.Duration) headerRequesterFn {
|
||||||
closure := make(map[common.Hash]*types.Block)
|
closure := make(map[common.Hash]*types.Block)
|
||||||
|
|
@ -294,7 +272,6 @@ func verifyImportDone(t *testing.T, imported chan *types.Block) {
|
||||||
|
|
||||||
// Tests that a fetcher accepts block announcements and initiates retrievals for
|
// Tests that a fetcher accepts block announcements and initiates retrievals for
|
||||||
// them, successfully importing into the local chain.
|
// them, successfully importing into the local chain.
|
||||||
func TestSequentialAnnouncements61(t *testing.T) { testSequentialAnnouncements(t, 61) }
|
|
||||||
func TestSequentialAnnouncements62(t *testing.T) { testSequentialAnnouncements(t, 62) }
|
func TestSequentialAnnouncements62(t *testing.T) { testSequentialAnnouncements(t, 62) }
|
||||||
func TestSequentialAnnouncements63(t *testing.T) { testSequentialAnnouncements(t, 63) }
|
func TestSequentialAnnouncements63(t *testing.T) { testSequentialAnnouncements(t, 63) }
|
||||||
func TestSequentialAnnouncements64(t *testing.T) { testSequentialAnnouncements(t, 64) }
|
func TestSequentialAnnouncements64(t *testing.T) { testSequentialAnnouncements(t, 64) }
|
||||||
|
|
@ -305,7 +282,6 @@ func testSequentialAnnouncements(t *testing.T, protocol int) {
|
||||||
hashes, blocks := makeChain(targetBlocks, 0, genesis)
|
hashes, blocks := makeChain(targetBlocks, 0, genesis)
|
||||||
|
|
||||||
tester := newTester()
|
tester := newTester()
|
||||||
blockFetcher := tester.makeBlockFetcher(blocks)
|
|
||||||
headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack)
|
headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack)
|
||||||
bodyFetcher := tester.makeBodyFetcher(blocks, 0)
|
bodyFetcher := tester.makeBodyFetcher(blocks, 0)
|
||||||
|
|
||||||
|
|
@ -314,11 +290,7 @@ func testSequentialAnnouncements(t *testing.T, protocol int) {
|
||||||
tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
|
tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
|
||||||
|
|
||||||
for i := len(hashes) - 2; i >= 0; i-- {
|
for i := len(hashes) - 2; i >= 0; i-- {
|
||||||
if protocol < 62 {
|
tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
|
||||||
tester.fetcher.Notify("valid", hashes[i], 0, time.Now().Add(-arriveTimeout), blockFetcher, nil, nil)
|
|
||||||
} else {
|
|
||||||
tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), nil, headerFetcher, bodyFetcher)
|
|
||||||
}
|
|
||||||
verifyImportEvent(t, imported, true)
|
verifyImportEvent(t, imported, true)
|
||||||
}
|
}
|
||||||
verifyImportDone(t, imported)
|
verifyImportDone(t, imported)
|
||||||
|
|
@ -326,7 +298,6 @@ func testSequentialAnnouncements(t *testing.T, protocol int) {
|
||||||
|
|
||||||
// Tests that if blocks are announced by multiple peers (or even the same buggy
|
// Tests that if blocks are announced by multiple peers (or even the same buggy
|
||||||
// peer), they will only get downloaded at most once.
|
// peer), they will only get downloaded at most once.
|
||||||
func TestConcurrentAnnouncements61(t *testing.T) { testConcurrentAnnouncements(t, 61) }
|
|
||||||
func TestConcurrentAnnouncements62(t *testing.T) { testConcurrentAnnouncements(t, 62) }
|
func TestConcurrentAnnouncements62(t *testing.T) { testConcurrentAnnouncements(t, 62) }
|
||||||
func TestConcurrentAnnouncements63(t *testing.T) { testConcurrentAnnouncements(t, 63) }
|
func TestConcurrentAnnouncements63(t *testing.T) { testConcurrentAnnouncements(t, 63) }
|
||||||
func TestConcurrentAnnouncements64(t *testing.T) { testConcurrentAnnouncements(t, 64) }
|
func TestConcurrentAnnouncements64(t *testing.T) { testConcurrentAnnouncements(t, 64) }
|
||||||
|
|
@ -338,15 +309,10 @@ func testConcurrentAnnouncements(t *testing.T, protocol int) {
|
||||||
|
|
||||||
// Assemble a tester with a built in counter for the requests
|
// Assemble a tester with a built in counter for the requests
|
||||||
tester := newTester()
|
tester := newTester()
|
||||||
blockFetcher := tester.makeBlockFetcher(blocks)
|
|
||||||
headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack)
|
headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack)
|
||||||
bodyFetcher := tester.makeBodyFetcher(blocks, 0)
|
bodyFetcher := tester.makeBodyFetcher(blocks, 0)
|
||||||
|
|
||||||
counter := uint32(0)
|
counter := uint32(0)
|
||||||
blockWrapper := func(hashes []common.Hash) error {
|
|
||||||
atomic.AddUint32(&counter, uint32(len(hashes)))
|
|
||||||
return blockFetcher(hashes)
|
|
||||||
}
|
|
||||||
headerWrapper := func(hash common.Hash) error {
|
headerWrapper := func(hash common.Hash) error {
|
||||||
atomic.AddUint32(&counter, 1)
|
atomic.AddUint32(&counter, 1)
|
||||||
return headerFetcher(hash)
|
return headerFetcher(hash)
|
||||||
|
|
@ -356,15 +322,9 @@ func testConcurrentAnnouncements(t *testing.T, protocol int) {
|
||||||
tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
|
tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
|
||||||
|
|
||||||
for i := len(hashes) - 2; i >= 0; i-- {
|
for i := len(hashes) - 2; i >= 0; i-- {
|
||||||
if protocol < 62 {
|
tester.fetcher.Notify("first", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerWrapper, bodyFetcher)
|
||||||
tester.fetcher.Notify("first", hashes[i], 0, time.Now().Add(-arriveTimeout), blockWrapper, nil, nil)
|
tester.fetcher.Notify("second", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout+time.Millisecond), headerWrapper, bodyFetcher)
|
||||||
tester.fetcher.Notify("second", hashes[i], 0, time.Now().Add(-arriveTimeout+time.Millisecond), blockWrapper, nil, nil)
|
tester.fetcher.Notify("second", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout-time.Millisecond), headerWrapper, bodyFetcher)
|
||||||
tester.fetcher.Notify("second", hashes[i], 0, time.Now().Add(-arriveTimeout-time.Millisecond), blockWrapper, nil, nil)
|
|
||||||
} else {
|
|
||||||
tester.fetcher.Notify("first", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), nil, headerWrapper, bodyFetcher)
|
|
||||||
tester.fetcher.Notify("second", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout+time.Millisecond), nil, headerWrapper, bodyFetcher)
|
|
||||||
tester.fetcher.Notify("second", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout-time.Millisecond), nil, headerWrapper, bodyFetcher)
|
|
||||||
}
|
|
||||||
verifyImportEvent(t, imported, true)
|
verifyImportEvent(t, imported, true)
|
||||||
}
|
}
|
||||||
verifyImportDone(t, imported)
|
verifyImportDone(t, imported)
|
||||||
|
|
@ -377,7 +337,6 @@ func testConcurrentAnnouncements(t *testing.T, protocol int) {
|
||||||
|
|
||||||
// Tests that announcements arriving while a previous is being fetched still
|
// Tests that announcements arriving while a previous is being fetched still
|
||||||
// results in a valid import.
|
// results in a valid import.
|
||||||
func TestOverlappingAnnouncements61(t *testing.T) { testOverlappingAnnouncements(t, 61) }
|
|
||||||
func TestOverlappingAnnouncements62(t *testing.T) { testOverlappingAnnouncements(t, 62) }
|
func TestOverlappingAnnouncements62(t *testing.T) { testOverlappingAnnouncements(t, 62) }
|
||||||
func TestOverlappingAnnouncements63(t *testing.T) { testOverlappingAnnouncements(t, 63) }
|
func TestOverlappingAnnouncements63(t *testing.T) { testOverlappingAnnouncements(t, 63) }
|
||||||
func TestOverlappingAnnouncements64(t *testing.T) { testOverlappingAnnouncements(t, 64) }
|
func TestOverlappingAnnouncements64(t *testing.T) { testOverlappingAnnouncements(t, 64) }
|
||||||
|
|
@ -388,7 +347,6 @@ func testOverlappingAnnouncements(t *testing.T, protocol int) {
|
||||||
hashes, blocks := makeChain(targetBlocks, 0, genesis)
|
hashes, blocks := makeChain(targetBlocks, 0, genesis)
|
||||||
|
|
||||||
tester := newTester()
|
tester := newTester()
|
||||||
blockFetcher := tester.makeBlockFetcher(blocks)
|
|
||||||
headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack)
|
headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack)
|
||||||
bodyFetcher := tester.makeBodyFetcher(blocks, 0)
|
bodyFetcher := tester.makeBodyFetcher(blocks, 0)
|
||||||
|
|
||||||
|
|
@ -401,11 +359,7 @@ func testOverlappingAnnouncements(t *testing.T, protocol int) {
|
||||||
tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
|
tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
|
||||||
|
|
||||||
for i := len(hashes) - 2; i >= 0; i-- {
|
for i := len(hashes) - 2; i >= 0; i-- {
|
||||||
if protocol < 62 {
|
tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
|
||||||
tester.fetcher.Notify("valid", hashes[i], 0, time.Now().Add(-arriveTimeout), blockFetcher, nil, nil)
|
|
||||||
} else {
|
|
||||||
tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), nil, headerFetcher, bodyFetcher)
|
|
||||||
}
|
|
||||||
select {
|
select {
|
||||||
case <-imported:
|
case <-imported:
|
||||||
case <-time.After(time.Second):
|
case <-time.After(time.Second):
|
||||||
|
|
@ -417,7 +371,6 @@ func testOverlappingAnnouncements(t *testing.T, protocol int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that announces already being retrieved will not be duplicated.
|
// Tests that announces already being retrieved will not be duplicated.
|
||||||
func TestPendingDeduplication61(t *testing.T) { testPendingDeduplication(t, 61) }
|
|
||||||
func TestPendingDeduplication62(t *testing.T) { testPendingDeduplication(t, 62) }
|
func TestPendingDeduplication62(t *testing.T) { testPendingDeduplication(t, 62) }
|
||||||
func TestPendingDeduplication63(t *testing.T) { testPendingDeduplication(t, 63) }
|
func TestPendingDeduplication63(t *testing.T) { testPendingDeduplication(t, 63) }
|
||||||
func TestPendingDeduplication64(t *testing.T) { testPendingDeduplication(t, 64) }
|
func TestPendingDeduplication64(t *testing.T) { testPendingDeduplication(t, 64) }
|
||||||
|
|
@ -428,22 +381,11 @@ func testPendingDeduplication(t *testing.T, protocol int) {
|
||||||
|
|
||||||
// Assemble a tester with a built in counter and delayed fetcher
|
// Assemble a tester with a built in counter and delayed fetcher
|
||||||
tester := newTester()
|
tester := newTester()
|
||||||
blockFetcher := tester.makeBlockFetcher(blocks)
|
|
||||||
headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack)
|
headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack)
|
||||||
bodyFetcher := tester.makeBodyFetcher(blocks, 0)
|
bodyFetcher := tester.makeBodyFetcher(blocks, 0)
|
||||||
|
|
||||||
delay := 50 * time.Millisecond
|
delay := 50 * time.Millisecond
|
||||||
counter := uint32(0)
|
counter := uint32(0)
|
||||||
blockWrapper := func(hashes []common.Hash) error {
|
|
||||||
atomic.AddUint32(&counter, uint32(len(hashes)))
|
|
||||||
|
|
||||||
// Simulate a long running fetch
|
|
||||||
go func() {
|
|
||||||
time.Sleep(delay)
|
|
||||||
blockFetcher(hashes)
|
|
||||||
}()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
headerWrapper := func(hash common.Hash) error {
|
headerWrapper := func(hash common.Hash) error {
|
||||||
atomic.AddUint32(&counter, 1)
|
atomic.AddUint32(&counter, 1)
|
||||||
|
|
||||||
|
|
@ -456,11 +398,7 @@ func testPendingDeduplication(t *testing.T, protocol int) {
|
||||||
}
|
}
|
||||||
// Announce the same block many times until it's fetched (wait for any pending ops)
|
// Announce the same block many times until it's fetched (wait for any pending ops)
|
||||||
for tester.getBlock(hashes[0]) == nil {
|
for tester.getBlock(hashes[0]) == nil {
|
||||||
if protocol < 62 {
|
tester.fetcher.Notify("repeater", hashes[0], 1, time.Now().Add(-arriveTimeout), headerWrapper, bodyFetcher)
|
||||||
tester.fetcher.Notify("repeater", hashes[0], 0, time.Now().Add(-arriveTimeout), blockWrapper, nil, nil)
|
|
||||||
} else {
|
|
||||||
tester.fetcher.Notify("repeater", hashes[0], 1, time.Now().Add(-arriveTimeout), nil, headerWrapper, bodyFetcher)
|
|
||||||
}
|
|
||||||
time.Sleep(time.Millisecond)
|
time.Sleep(time.Millisecond)
|
||||||
}
|
}
|
||||||
time.Sleep(delay)
|
time.Sleep(delay)
|
||||||
|
|
@ -476,7 +414,6 @@ func testPendingDeduplication(t *testing.T, protocol int) {
|
||||||
|
|
||||||
// Tests that announcements retrieved in a random order are cached and eventually
|
// Tests that announcements retrieved in a random order are cached and eventually
|
||||||
// imported when all the gaps are filled in.
|
// imported when all the gaps are filled in.
|
||||||
func TestRandomArrivalImport61(t *testing.T) { testRandomArrivalImport(t, 61) }
|
|
||||||
func TestRandomArrivalImport62(t *testing.T) { testRandomArrivalImport(t, 62) }
|
func TestRandomArrivalImport62(t *testing.T) { testRandomArrivalImport(t, 62) }
|
||||||
func TestRandomArrivalImport63(t *testing.T) { testRandomArrivalImport(t, 63) }
|
func TestRandomArrivalImport63(t *testing.T) { testRandomArrivalImport(t, 63) }
|
||||||
func TestRandomArrivalImport64(t *testing.T) { testRandomArrivalImport(t, 64) }
|
func TestRandomArrivalImport64(t *testing.T) { testRandomArrivalImport(t, 64) }
|
||||||
|
|
@ -488,7 +425,6 @@ func testRandomArrivalImport(t *testing.T, protocol int) {
|
||||||
skip := targetBlocks / 2
|
skip := targetBlocks / 2
|
||||||
|
|
||||||
tester := newTester()
|
tester := newTester()
|
||||||
blockFetcher := tester.makeBlockFetcher(blocks)
|
|
||||||
headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack)
|
headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack)
|
||||||
bodyFetcher := tester.makeBodyFetcher(blocks, 0)
|
bodyFetcher := tester.makeBodyFetcher(blocks, 0)
|
||||||
|
|
||||||
|
|
@ -498,26 +434,17 @@ func testRandomArrivalImport(t *testing.T, protocol int) {
|
||||||
|
|
||||||
for i := len(hashes) - 1; i >= 0; i-- {
|
for i := len(hashes) - 1; i >= 0; i-- {
|
||||||
if i != skip {
|
if i != skip {
|
||||||
if protocol < 62 {
|
tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
|
||||||
tester.fetcher.Notify("valid", hashes[i], 0, time.Now().Add(-arriveTimeout), blockFetcher, nil, nil)
|
|
||||||
} else {
|
|
||||||
tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), nil, headerFetcher, bodyFetcher)
|
|
||||||
}
|
|
||||||
time.Sleep(time.Millisecond)
|
time.Sleep(time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Finally announce the skipped entry and check full import
|
// Finally announce the skipped entry and check full import
|
||||||
if protocol < 62 {
|
tester.fetcher.Notify("valid", hashes[skip], uint64(len(hashes)-skip-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
|
||||||
tester.fetcher.Notify("valid", hashes[skip], 0, time.Now().Add(-arriveTimeout), blockFetcher, nil, nil)
|
|
||||||
} else {
|
|
||||||
tester.fetcher.Notify("valid", hashes[skip], uint64(len(hashes)-skip-1), time.Now().Add(-arriveTimeout), nil, headerFetcher, bodyFetcher)
|
|
||||||
}
|
|
||||||
verifyImportCount(t, imported, len(hashes)-1)
|
verifyImportCount(t, imported, len(hashes)-1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that direct block enqueues (due to block propagation vs. hash announce)
|
// Tests that direct block enqueues (due to block propagation vs. hash announce)
|
||||||
// are correctly schedule, filling and import queue gaps.
|
// are correctly schedule, filling and import queue gaps.
|
||||||
func TestQueueGapFill61(t *testing.T) { testQueueGapFill(t, 61) }
|
|
||||||
func TestQueueGapFill62(t *testing.T) { testQueueGapFill(t, 62) }
|
func TestQueueGapFill62(t *testing.T) { testQueueGapFill(t, 62) }
|
||||||
func TestQueueGapFill63(t *testing.T) { testQueueGapFill(t, 63) }
|
func TestQueueGapFill63(t *testing.T) { testQueueGapFill(t, 63) }
|
||||||
func TestQueueGapFill64(t *testing.T) { testQueueGapFill(t, 64) }
|
func TestQueueGapFill64(t *testing.T) { testQueueGapFill(t, 64) }
|
||||||
|
|
@ -529,7 +456,6 @@ func testQueueGapFill(t *testing.T, protocol int) {
|
||||||
skip := targetBlocks / 2
|
skip := targetBlocks / 2
|
||||||
|
|
||||||
tester := newTester()
|
tester := newTester()
|
||||||
blockFetcher := tester.makeBlockFetcher(blocks)
|
|
||||||
headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack)
|
headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack)
|
||||||
bodyFetcher := tester.makeBodyFetcher(blocks, 0)
|
bodyFetcher := tester.makeBodyFetcher(blocks, 0)
|
||||||
|
|
||||||
|
|
@ -539,11 +465,7 @@ func testQueueGapFill(t *testing.T, protocol int) {
|
||||||
|
|
||||||
for i := len(hashes) - 1; i >= 0; i-- {
|
for i := len(hashes) - 1; i >= 0; i-- {
|
||||||
if i != skip {
|
if i != skip {
|
||||||
if protocol < 62 {
|
tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
|
||||||
tester.fetcher.Notify("valid", hashes[i], 0, time.Now().Add(-arriveTimeout), blockFetcher, nil, nil)
|
|
||||||
} else {
|
|
||||||
tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), nil, headerFetcher, bodyFetcher)
|
|
||||||
}
|
|
||||||
time.Sleep(time.Millisecond)
|
time.Sleep(time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -554,7 +476,6 @@ func testQueueGapFill(t *testing.T, protocol int) {
|
||||||
|
|
||||||
// Tests that blocks arriving from various sources (multiple propagations, hash
|
// Tests that blocks arriving from various sources (multiple propagations, hash
|
||||||
// announces, etc) do not get scheduled for import multiple times.
|
// announces, etc) do not get scheduled for import multiple times.
|
||||||
func TestImportDeduplication61(t *testing.T) { testImportDeduplication(t, 61) }
|
|
||||||
func TestImportDeduplication62(t *testing.T) { testImportDeduplication(t, 62) }
|
func TestImportDeduplication62(t *testing.T) { testImportDeduplication(t, 62) }
|
||||||
func TestImportDeduplication63(t *testing.T) { testImportDeduplication(t, 63) }
|
func TestImportDeduplication63(t *testing.T) { testImportDeduplication(t, 63) }
|
||||||
func TestImportDeduplication64(t *testing.T) { testImportDeduplication(t, 64) }
|
func TestImportDeduplication64(t *testing.T) { testImportDeduplication(t, 64) }
|
||||||
|
|
@ -565,7 +486,6 @@ func testImportDeduplication(t *testing.T, protocol int) {
|
||||||
|
|
||||||
// Create the tester and wrap the importer with a counter
|
// Create the tester and wrap the importer with a counter
|
||||||
tester := newTester()
|
tester := newTester()
|
||||||
blockFetcher := tester.makeBlockFetcher(blocks)
|
|
||||||
headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack)
|
headerFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack)
|
||||||
bodyFetcher := tester.makeBodyFetcher(blocks, 0)
|
bodyFetcher := tester.makeBodyFetcher(blocks, 0)
|
||||||
|
|
||||||
|
|
@ -581,11 +501,7 @@ func testImportDeduplication(t *testing.T, protocol int) {
|
||||||
tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
|
tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
|
||||||
|
|
||||||
// Announce the duplicating block, wait for retrieval, and also propagate directly
|
// Announce the duplicating block, wait for retrieval, and also propagate directly
|
||||||
if protocol < 62 {
|
tester.fetcher.Notify("valid", hashes[0], 1, time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
|
||||||
tester.fetcher.Notify("valid", hashes[0], 0, time.Now().Add(-arriveTimeout), blockFetcher, nil, nil)
|
|
||||||
} else {
|
|
||||||
tester.fetcher.Notify("valid", hashes[0], 1, time.Now().Add(-arriveTimeout), nil, headerFetcher, bodyFetcher)
|
|
||||||
}
|
|
||||||
<-fetching
|
<-fetching
|
||||||
|
|
||||||
tester.fetcher.Enqueue("valid", blocks[hashes[0]])
|
tester.fetcher.Enqueue("valid", blocks[hashes[0]])
|
||||||
|
|
@ -661,14 +577,14 @@ func testDistantAnnouncementDiscarding(t *testing.T, protocol int) {
|
||||||
tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- struct{}{} }
|
tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- struct{}{} }
|
||||||
|
|
||||||
// Ensure that a block with a lower number than the threshold is discarded
|
// Ensure that a block with a lower number than the threshold is discarded
|
||||||
tester.fetcher.Notify("lower", hashes[low], blocks[hashes[low]].NumberU64(), time.Now().Add(-arriveTimeout), nil, headerFetcher, bodyFetcher)
|
tester.fetcher.Notify("lower", hashes[low], blocks[hashes[low]].NumberU64(), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
|
||||||
select {
|
select {
|
||||||
case <-time.After(50 * time.Millisecond):
|
case <-time.After(50 * time.Millisecond):
|
||||||
case <-fetching:
|
case <-fetching:
|
||||||
t.Fatalf("fetcher requested stale header")
|
t.Fatalf("fetcher requested stale header")
|
||||||
}
|
}
|
||||||
// Ensure that a block with a higher number than the threshold is discarded
|
// Ensure that a block with a higher number than the threshold is discarded
|
||||||
tester.fetcher.Notify("higher", hashes[high], blocks[hashes[high]].NumberU64(), time.Now().Add(-arriveTimeout), nil, headerFetcher, bodyFetcher)
|
tester.fetcher.Notify("higher", hashes[high], blocks[hashes[high]].NumberU64(), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
|
||||||
select {
|
select {
|
||||||
case <-time.After(50 * time.Millisecond):
|
case <-time.After(50 * time.Millisecond):
|
||||||
case <-fetching:
|
case <-fetching:
|
||||||
|
|
@ -694,7 +610,7 @@ func testInvalidNumberAnnouncement(t *testing.T, protocol int) {
|
||||||
tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
|
tester.fetcher.importedHook = func(block *types.Block) { imported <- block }
|
||||||
|
|
||||||
// Announce a block with a bad number, check for immediate drop
|
// Announce a block with a bad number, check for immediate drop
|
||||||
tester.fetcher.Notify("bad", hashes[0], 2, time.Now().Add(-arriveTimeout), nil, headerFetcher, bodyFetcher)
|
tester.fetcher.Notify("bad", hashes[0], 2, time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
|
||||||
verifyImportEvent(t, imported, false)
|
verifyImportEvent(t, imported, false)
|
||||||
|
|
||||||
tester.lock.RLock()
|
tester.lock.RLock()
|
||||||
|
|
@ -705,7 +621,7 @@ func testInvalidNumberAnnouncement(t *testing.T, protocol int) {
|
||||||
t.Fatalf("peer with invalid numbered announcement not dropped")
|
t.Fatalf("peer with invalid numbered announcement not dropped")
|
||||||
}
|
}
|
||||||
// Make sure a good announcement passes without a drop
|
// Make sure a good announcement passes without a drop
|
||||||
tester.fetcher.Notify("good", hashes[0], 1, time.Now().Add(-arriveTimeout), nil, headerFetcher, bodyFetcher)
|
tester.fetcher.Notify("good", hashes[0], 1, time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
|
||||||
verifyImportEvent(t, imported, true)
|
verifyImportEvent(t, imported, true)
|
||||||
|
|
||||||
tester.lock.RLock()
|
tester.lock.RLock()
|
||||||
|
|
@ -744,7 +660,7 @@ func testEmptyBlockShortCircuit(t *testing.T, protocol int) {
|
||||||
|
|
||||||
// Iteratively announce blocks until all are imported
|
// Iteratively announce blocks until all are imported
|
||||||
for i := len(hashes) - 2; i >= 0; i-- {
|
for i := len(hashes) - 2; i >= 0; i-- {
|
||||||
tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), nil, headerFetcher, bodyFetcher)
|
tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
|
||||||
|
|
||||||
// All announces should fetch the header
|
// All announces should fetch the header
|
||||||
verifyFetchingEvent(t, fetching, true)
|
verifyFetchingEvent(t, fetching, true)
|
||||||
|
|
@ -761,7 +677,6 @@ func testEmptyBlockShortCircuit(t *testing.T, protocol int) {
|
||||||
// Tests that a peer is unable to use unbounded memory with sending infinite
|
// Tests that a peer is unable to use unbounded memory with sending infinite
|
||||||
// block announcements to a node, but that even in the face of such an attack,
|
// block announcements to a node, but that even in the face of such an attack,
|
||||||
// the fetcher remains operational.
|
// the fetcher remains operational.
|
||||||
func TestHashMemoryExhaustionAttack61(t *testing.T) { testHashMemoryExhaustionAttack(t, 61) }
|
|
||||||
func TestHashMemoryExhaustionAttack62(t *testing.T) { testHashMemoryExhaustionAttack(t, 62) }
|
func TestHashMemoryExhaustionAttack62(t *testing.T) { testHashMemoryExhaustionAttack(t, 62) }
|
||||||
func TestHashMemoryExhaustionAttack63(t *testing.T) { testHashMemoryExhaustionAttack(t, 63) }
|
func TestHashMemoryExhaustionAttack63(t *testing.T) { testHashMemoryExhaustionAttack(t, 63) }
|
||||||
func TestHashMemoryExhaustionAttack64(t *testing.T) { testHashMemoryExhaustionAttack(t, 64) }
|
func TestHashMemoryExhaustionAttack64(t *testing.T) { testHashMemoryExhaustionAttack(t, 64) }
|
||||||
|
|
@ -782,29 +697,19 @@ func testHashMemoryExhaustionAttack(t *testing.T, protocol int) {
|
||||||
// Create a valid chain and an infinite junk chain
|
// Create a valid chain and an infinite junk chain
|
||||||
targetBlocks := hashLimit + 2*maxQueueDist
|
targetBlocks := hashLimit + 2*maxQueueDist
|
||||||
hashes, blocks := makeChain(targetBlocks, 0, genesis)
|
hashes, blocks := makeChain(targetBlocks, 0, genesis)
|
||||||
validBlockFetcher := tester.makeBlockFetcher(blocks)
|
|
||||||
validHeaderFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack)
|
validHeaderFetcher := tester.makeHeaderFetcher(blocks, -gatherSlack)
|
||||||
validBodyFetcher := tester.makeBodyFetcher(blocks, 0)
|
validBodyFetcher := tester.makeBodyFetcher(blocks, 0)
|
||||||
|
|
||||||
attack, _ := makeChain(targetBlocks, 0, unknownBlock)
|
attack, _ := makeChain(targetBlocks, 0, unknownBlock)
|
||||||
attackerBlockFetcher := tester.makeBlockFetcher(nil)
|
|
||||||
attackerHeaderFetcher := tester.makeHeaderFetcher(nil, -gatherSlack)
|
attackerHeaderFetcher := tester.makeHeaderFetcher(nil, -gatherSlack)
|
||||||
attackerBodyFetcher := tester.makeBodyFetcher(nil, 0)
|
attackerBodyFetcher := tester.makeBodyFetcher(nil, 0)
|
||||||
|
|
||||||
// Feed the tester a huge hashset from the attacker, and a limited from the valid peer
|
// Feed the tester a huge hashset from the attacker, and a limited from the valid peer
|
||||||
for i := 0; i < len(attack); i++ {
|
for i := 0; i < len(attack); i++ {
|
||||||
if i < maxQueueDist {
|
if i < maxQueueDist {
|
||||||
if protocol < 62 {
|
tester.fetcher.Notify("valid", hashes[len(hashes)-2-i], uint64(i+1), time.Now(), validHeaderFetcher, validBodyFetcher)
|
||||||
tester.fetcher.Notify("valid", hashes[len(hashes)-2-i], 0, time.Now(), validBlockFetcher, nil, nil)
|
|
||||||
} else {
|
|
||||||
tester.fetcher.Notify("valid", hashes[len(hashes)-2-i], uint64(i+1), time.Now(), nil, validHeaderFetcher, validBodyFetcher)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if protocol < 62 {
|
|
||||||
tester.fetcher.Notify("attacker", attack[i], 0, time.Now(), attackerBlockFetcher, nil, nil)
|
|
||||||
} else {
|
|
||||||
tester.fetcher.Notify("attacker", attack[i], 1 /* don't distance drop */, time.Now(), nil, attackerHeaderFetcher, attackerBodyFetcher)
|
|
||||||
}
|
}
|
||||||
|
tester.fetcher.Notify("attacker", attack[i], 1 /* don't distance drop */, time.Now(), attackerHeaderFetcher, attackerBodyFetcher)
|
||||||
}
|
}
|
||||||
if count := atomic.LoadInt32(&announces); count != hashLimit+maxQueueDist {
|
if count := atomic.LoadInt32(&announces); count != hashLimit+maxQueueDist {
|
||||||
t.Fatalf("queued announce count mismatch: have %d, want %d", count, hashLimit+maxQueueDist)
|
t.Fatalf("queued announce count mismatch: have %d, want %d", count, hashLimit+maxQueueDist)
|
||||||
|
|
@ -814,11 +719,7 @@ func testHashMemoryExhaustionAttack(t *testing.T, protocol int) {
|
||||||
|
|
||||||
// Feed the remaining valid hashes to ensure DOS protection state remains clean
|
// Feed the remaining valid hashes to ensure DOS protection state remains clean
|
||||||
for i := len(hashes) - maxQueueDist - 2; i >= 0; i-- {
|
for i := len(hashes) - maxQueueDist - 2; i >= 0; i-- {
|
||||||
if protocol < 62 {
|
tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), validHeaderFetcher, validBodyFetcher)
|
||||||
tester.fetcher.Notify("valid", hashes[i], 0, time.Now().Add(-arriveTimeout), validBlockFetcher, nil, nil)
|
|
||||||
} else {
|
|
||||||
tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), nil, validHeaderFetcher, validBodyFetcher)
|
|
||||||
}
|
|
||||||
verifyImportEvent(t, imported, true)
|
verifyImportEvent(t, imported, true)
|
||||||
}
|
}
|
||||||
verifyImportDone(t, imported)
|
verifyImportDone(t, imported)
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,9 @@ var (
|
||||||
propBroadcastDropMeter = metrics.NewMeter("eth/fetcher/prop/broadcasts/drop")
|
propBroadcastDropMeter = metrics.NewMeter("eth/fetcher/prop/broadcasts/drop")
|
||||||
propBroadcastDOSMeter = metrics.NewMeter("eth/fetcher/prop/broadcasts/dos")
|
propBroadcastDOSMeter = metrics.NewMeter("eth/fetcher/prop/broadcasts/dos")
|
||||||
|
|
||||||
blockFetchMeter = metrics.NewMeter("eth/fetcher/fetch/blocks")
|
|
||||||
headerFetchMeter = metrics.NewMeter("eth/fetcher/fetch/headers")
|
headerFetchMeter = metrics.NewMeter("eth/fetcher/fetch/headers")
|
||||||
bodyFetchMeter = metrics.NewMeter("eth/fetcher/fetch/bodies")
|
bodyFetchMeter = metrics.NewMeter("eth/fetcher/fetch/bodies")
|
||||||
|
|
||||||
blockFilterInMeter = metrics.NewMeter("eth/fetcher/filter/blocks/in")
|
|
||||||
blockFilterOutMeter = metrics.NewMeter("eth/fetcher/filter/blocks/out")
|
|
||||||
headerFilterInMeter = metrics.NewMeter("eth/fetcher/filter/headers/in")
|
headerFilterInMeter = metrics.NewMeter("eth/fetcher/filter/headers/in")
|
||||||
headerFilterOutMeter = metrics.NewMeter("eth/fetcher/filter/headers/out")
|
headerFilterOutMeter = metrics.NewMeter("eth/fetcher/filter/headers/out")
|
||||||
bodyFilterInMeter = metrics.NewMeter("eth/fetcher/filter/bodies/in")
|
bodyFilterInMeter = metrics.NewMeter("eth/fetcher/filter/bodies/in")
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,6 @@ type PublicFilterAPI struct {
|
||||||
|
|
||||||
transactionMu sync.RWMutex
|
transactionMu sync.RWMutex
|
||||||
transactionQueue map[int]*hashQueue
|
transactionQueue map[int]*hashQueue
|
||||||
|
|
||||||
transactMu sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPublicFilterAPI returns a new PublicFilterAPI instance.
|
// NewPublicFilterAPI returns a new PublicFilterAPI instance.
|
||||||
|
|
@ -100,6 +98,7 @@ done:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
|
s.filterManager.Lock() // lock order like filterLoop()
|
||||||
s.logMu.Lock()
|
s.logMu.Lock()
|
||||||
for id, filter := range s.logQueue {
|
for id, filter := range s.logQueue {
|
||||||
if time.Since(filter.timeout) > filterTickerTime {
|
if time.Since(filter.timeout) > filterTickerTime {
|
||||||
|
|
@ -126,6 +125,7 @@ done:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.transactionMu.Unlock()
|
s.transactionMu.Unlock()
|
||||||
|
s.filterManager.Unlock()
|
||||||
case <-s.quit:
|
case <-s.quit:
|
||||||
break done
|
break done
|
||||||
}
|
}
|
||||||
|
|
@ -135,19 +135,24 @@ done:
|
||||||
|
|
||||||
// NewBlockFilter create a new filter that returns blocks that are included into the canonical chain.
|
// NewBlockFilter create a new filter that returns blocks that are included into the canonical chain.
|
||||||
func (s *PublicFilterAPI) NewBlockFilter() (string, error) {
|
func (s *PublicFilterAPI) NewBlockFilter() (string, error) {
|
||||||
|
// protect filterManager.Add() and setting of filter fields
|
||||||
|
s.filterManager.Lock()
|
||||||
|
defer s.filterManager.Unlock()
|
||||||
|
|
||||||
externalId, err := newFilterId()
|
externalId, err := newFilterId()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.blockMu.Lock()
|
|
||||||
filter := New(s.chainDb)
|
filter := New(s.chainDb)
|
||||||
id, err := s.filterManager.Add(filter, ChainFilter)
|
id, err := s.filterManager.Add(filter, ChainFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.blockMu.Lock()
|
||||||
s.blockQueue[id] = &hashQueue{timeout: time.Now()}
|
s.blockQueue[id] = &hashQueue{timeout: time.Now()}
|
||||||
|
s.blockMu.Unlock()
|
||||||
|
|
||||||
filter.BlockCallback = func(block *types.Block, logs vm.Logs) {
|
filter.BlockCallback = func(block *types.Block, logs vm.Logs) {
|
||||||
s.blockMu.Lock()
|
s.blockMu.Lock()
|
||||||
|
|
@ -158,8 +163,6 @@ func (s *PublicFilterAPI) NewBlockFilter() (string, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defer s.blockMu.Unlock()
|
|
||||||
|
|
||||||
s.filterMapMu.Lock()
|
s.filterMapMu.Lock()
|
||||||
s.filterMapping[externalId] = id
|
s.filterMapping[externalId] = id
|
||||||
s.filterMapMu.Unlock()
|
s.filterMapMu.Unlock()
|
||||||
|
|
@ -169,21 +172,24 @@ func (s *PublicFilterAPI) NewBlockFilter() (string, error) {
|
||||||
|
|
||||||
// NewPendingTransactionFilter creates a filter that returns new pending transactions.
|
// NewPendingTransactionFilter creates a filter that returns new pending transactions.
|
||||||
func (s *PublicFilterAPI) NewPendingTransactionFilter() (string, error) {
|
func (s *PublicFilterAPI) NewPendingTransactionFilter() (string, error) {
|
||||||
|
// protect filterManager.Add() and setting of filter fields
|
||||||
|
s.filterManager.Lock()
|
||||||
|
defer s.filterManager.Unlock()
|
||||||
|
|
||||||
externalId, err := newFilterId()
|
externalId, err := newFilterId()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.transactionMu.Lock()
|
|
||||||
defer s.transactionMu.Unlock()
|
|
||||||
|
|
||||||
filter := New(s.chainDb)
|
filter := New(s.chainDb)
|
||||||
id, err := s.filterManager.Add(filter, PendingTxFilter)
|
id, err := s.filterManager.Add(filter, PendingTxFilter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.transactionMu.Lock()
|
||||||
s.transactionQueue[id] = &hashQueue{timeout: time.Now()}
|
s.transactionQueue[id] = &hashQueue{timeout: time.Now()}
|
||||||
|
s.transactionMu.Unlock()
|
||||||
|
|
||||||
filter.TransactionCallback = func(tx *types.Transaction) {
|
filter.TransactionCallback = func(tx *types.Transaction) {
|
||||||
s.transactionMu.Lock()
|
s.transactionMu.Lock()
|
||||||
|
|
@ -203,8 +209,9 @@ func (s *PublicFilterAPI) NewPendingTransactionFilter() (string, error) {
|
||||||
|
|
||||||
// newLogFilter creates a new log filter.
|
// newLogFilter creates a new log filter.
|
||||||
func (s *PublicFilterAPI) newLogFilter(earliest, latest int64, addresses []common.Address, topics [][]common.Hash, callback func(log *vm.Log, removed bool)) (int, error) {
|
func (s *PublicFilterAPI) newLogFilter(earliest, latest int64, addresses []common.Address, topics [][]common.Hash, callback func(log *vm.Log, removed bool)) (int, error) {
|
||||||
s.logMu.Lock()
|
// protect filterManager.Add() and setting of filter fields
|
||||||
defer s.logMu.Unlock()
|
s.filterManager.Lock()
|
||||||
|
defer s.filterManager.Unlock()
|
||||||
|
|
||||||
filter := New(s.chainDb)
|
filter := New(s.chainDb)
|
||||||
id, err := s.filterManager.Add(filter, LogFilter)
|
id, err := s.filterManager.Add(filter, LogFilter)
|
||||||
|
|
@ -212,7 +219,9 @@ func (s *PublicFilterAPI) newLogFilter(earliest, latest int64, addresses []commo
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.logMu.Lock()
|
||||||
s.logQueue[id] = &logQueue{timeout: time.Now()}
|
s.logQueue[id] = &logQueue{timeout: time.Now()}
|
||||||
|
s.logMu.Unlock()
|
||||||
|
|
||||||
filter.SetBeginBlock(earliest)
|
filter.SetBeginBlock(earliest)
|
||||||
filter.SetEndBlock(latest)
|
filter.SetEndBlock(latest)
|
||||||
|
|
@ -443,35 +452,43 @@ func (s *PublicFilterAPI) GetLogs(args NewFilterArgs) []vmlog {
|
||||||
|
|
||||||
// UninstallFilter removes the filter with the given filter id.
|
// UninstallFilter removes the filter with the given filter id.
|
||||||
func (s *PublicFilterAPI) UninstallFilter(filterId string) bool {
|
func (s *PublicFilterAPI) UninstallFilter(filterId string) bool {
|
||||||
s.filterMapMu.Lock()
|
s.filterManager.Lock()
|
||||||
defer s.filterMapMu.Unlock()
|
defer s.filterManager.Unlock()
|
||||||
|
|
||||||
|
s.filterMapMu.Lock()
|
||||||
id, ok := s.filterMapping[filterId]
|
id, ok := s.filterMapping[filterId]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
s.filterMapMu.Unlock()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
defer s.filterManager.Remove(id)
|
|
||||||
delete(s.filterMapping, filterId)
|
delete(s.filterMapping, filterId)
|
||||||
|
s.filterMapMu.Unlock()
|
||||||
|
|
||||||
|
s.filterManager.Remove(id)
|
||||||
|
|
||||||
|
s.logMu.Lock()
|
||||||
if _, ok := s.logQueue[id]; ok {
|
if _, ok := s.logQueue[id]; ok {
|
||||||
s.logMu.Lock()
|
|
||||||
defer s.logMu.Unlock()
|
|
||||||
delete(s.logQueue, id)
|
delete(s.logQueue, id)
|
||||||
|
s.logMu.Unlock()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
s.logMu.Unlock()
|
||||||
|
|
||||||
|
s.blockMu.Lock()
|
||||||
if _, ok := s.blockQueue[id]; ok {
|
if _, ok := s.blockQueue[id]; ok {
|
||||||
s.blockMu.Lock()
|
|
||||||
defer s.blockMu.Unlock()
|
|
||||||
delete(s.blockQueue, id)
|
delete(s.blockQueue, id)
|
||||||
|
s.blockMu.Unlock()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
s.blockMu.Unlock()
|
||||||
|
|
||||||
|
s.transactionMu.Lock()
|
||||||
if _, ok := s.transactionQueue[id]; ok {
|
if _, ok := s.transactionQueue[id]; ok {
|
||||||
s.transactionMu.Lock()
|
|
||||||
defer s.transactionMu.Unlock()
|
|
||||||
delete(s.transactionQueue, id)
|
delete(s.transactionQueue, id)
|
||||||
|
s.transactionMu.Unlock()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
s.transactionMu.Unlock()
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
@ -525,7 +542,9 @@ func (s *PublicFilterAPI) logFilterChanged(id int) []vmlog {
|
||||||
|
|
||||||
// GetFilterLogs returns the logs for the filter with the given id.
|
// GetFilterLogs returns the logs for the filter with the given id.
|
||||||
func (s *PublicFilterAPI) GetFilterLogs(filterId string) []vmlog {
|
func (s *PublicFilterAPI) GetFilterLogs(filterId string) []vmlog {
|
||||||
|
s.filterMapMu.RLock()
|
||||||
id, ok := s.filterMapping[filterId]
|
id, ok := s.filterMapping[filterId]
|
||||||
|
s.filterMapMu.RUnlock()
|
||||||
if !ok {
|
if !ok {
|
||||||
return toRPCLogs(nil, false)
|
return toRPCLogs(nil, false)
|
||||||
}
|
}
|
||||||
|
|
@ -540,9 +559,9 @@ func (s *PublicFilterAPI) GetFilterLogs(filterId string) []vmlog {
|
||||||
// GetFilterChanges returns the logs for the filter with the given id since last time is was called.
|
// GetFilterChanges returns the logs for the filter with the given id since last time is was called.
|
||||||
// This can be used for polling.
|
// This can be used for polling.
|
||||||
func (s *PublicFilterAPI) GetFilterChanges(filterId string) interface{} {
|
func (s *PublicFilterAPI) GetFilterChanges(filterId string) interface{} {
|
||||||
s.filterMapMu.Lock()
|
s.filterMapMu.RLock()
|
||||||
id, ok := s.filterMapping[filterId]
|
id, ok := s.filterMapping[filterId]
|
||||||
s.filterMapMu.Unlock()
|
s.filterMapMu.RUnlock()
|
||||||
|
|
||||||
if !ok { // filter not found
|
if !ok { // filter not found
|
||||||
return []interface{}{}
|
return []interface{}{}
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,11 @@ func (self *Filter) SetTopics(topics [][]common.Hash) {
|
||||||
|
|
||||||
// Run filters logs with the current parameters set
|
// Run filters logs with the current parameters set
|
||||||
func (self *Filter) Find() vm.Logs {
|
func (self *Filter) Find() vm.Logs {
|
||||||
latestBlock := core.GetBlock(self.db, core.GetHeadBlockHash(self.db))
|
latestHash := core.GetHeadBlockHash(self.db)
|
||||||
|
latestBlock := core.GetBlock(self.db, latestHash)
|
||||||
|
if latestBlock == nil {
|
||||||
|
return vm.Logs{}
|
||||||
|
}
|
||||||
var beginBlockNo uint64 = uint64(self.begin)
|
var beginBlockNo uint64 = uint64(self.begin)
|
||||||
if self.begin == -1 {
|
if self.begin == -1 {
|
||||||
beginBlockNo = latestBlock.NumberU64()
|
beginBlockNo = latestBlock.NumberU64()
|
||||||
|
|
@ -122,13 +126,13 @@ func (self *Filter) mipFind(start, end uint64, depth int) (logs vm.Logs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Filter) getLogs(start, end uint64) (logs vm.Logs) {
|
func (self *Filter) getLogs(start, end uint64) (logs vm.Logs) {
|
||||||
var block *types.Block
|
|
||||||
|
|
||||||
for i := start; i <= end; i++ {
|
for i := start; i <= end; i++ {
|
||||||
|
var block *types.Block
|
||||||
hash := core.GetCanonicalHash(self.db, i)
|
hash := core.GetCanonicalHash(self.db, i)
|
||||||
if hash != (common.Hash{}) {
|
if hash != (common.Hash{}) {
|
||||||
block = core.GetBlock(self.db, hash)
|
block = core.GetBlock(self.db, hash)
|
||||||
} else { // block not found
|
}
|
||||||
|
if block == nil { // block not found/written
|
||||||
return logs
|
return logs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,11 +82,20 @@ func (fs *FilterSystem) Stop() {
|
||||||
fs.sub.Unsubscribe()
|
fs.sub.Unsubscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add adds a filter to the filter manager
|
// Acquire filter system maps lock, required to force lock acquisition
|
||||||
func (fs *FilterSystem) Add(filter *Filter, filterType FilterType) (int, error) {
|
// sequence with filterMu acquired first to avoid deadlocks by callbacks
|
||||||
|
func (fs *FilterSystem) Lock() {
|
||||||
fs.filterMu.Lock()
|
fs.filterMu.Lock()
|
||||||
defer fs.filterMu.Unlock()
|
}
|
||||||
|
|
||||||
|
// Release filter system maps lock
|
||||||
|
func (fs *FilterSystem) Unlock() {
|
||||||
|
fs.filterMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds a filter to the filter manager
|
||||||
|
// Expects filterMu to be locked.
|
||||||
|
func (fs *FilterSystem) Add(filter *Filter, filterType FilterType) (int, error) {
|
||||||
id := fs.filterId
|
id := fs.filterId
|
||||||
filter.created = time.Now()
|
filter.created = time.Now()
|
||||||
|
|
||||||
|
|
@ -110,10 +119,8 @@ func (fs *FilterSystem) Add(filter *Filter, filterType FilterType) (int, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes a filter by filter id
|
// Remove removes a filter by filter id
|
||||||
|
// Expects filterMu to be locked.
|
||||||
func (fs *FilterSystem) Remove(id int) {
|
func (fs *FilterSystem) Remove(id int) {
|
||||||
fs.filterMu.Lock()
|
|
||||||
defer fs.filterMu.Unlock()
|
|
||||||
|
|
||||||
delete(fs.chainFilters, id)
|
delete(fs.chainFilters, id)
|
||||||
delete(fs.pendingTxFilters, id)
|
delete(fs.pendingTxFilters, id)
|
||||||
delete(fs.logFilters, id)
|
delete(fs.logFilters, id)
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ func BenchmarkMipmaps(b *testing.B) {
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
genesis := core.WriteGenesisBlockForTesting(db, core.GenesisAccount{Address: addr1, Balance: big.NewInt(1000000)})
|
genesis := core.WriteGenesisBlockForTesting(db, core.GenesisAccount{Address: addr1, Balance: big.NewInt(1000000)})
|
||||||
chain, receipts := core.GenerateChain(genesis, db, 100010, func(i int, gen *core.BlockGen) {
|
chain, receipts := core.GenerateChain(nil, genesis, db, 100010, func(i int, gen *core.BlockGen) {
|
||||||
var receipts types.Receipts
|
var receipts types.Receipts
|
||||||
switch i {
|
switch i {
|
||||||
case 2403:
|
case 2403:
|
||||||
|
|
@ -133,7 +133,7 @@ func TestFilters(t *testing.T) {
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
genesis := core.WriteGenesisBlockForTesting(db, core.GenesisAccount{Address: addr, Balance: big.NewInt(1000000)})
|
genesis := core.WriteGenesisBlockForTesting(db, core.GenesisAccount{Address: addr, Balance: big.NewInt(1000000)})
|
||||||
chain, receipts := core.GenerateChain(genesis, db, 1000, func(i int, gen *core.BlockGen) {
|
chain, receipts := core.GenerateChain(nil, genesis, db, 1000, func(i int, gen *core.BlockGen) {
|
||||||
var receipts types.Receipts
|
var receipts types.Receipts
|
||||||
switch i {
|
switch i {
|
||||||
case 1:
|
case 1:
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue