mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 07:06:42 +00:00
Merge 1018bf6a00 into a07539fb88
This commit is contained in:
commit
be16a2abdc
733 changed files with 35402 additions and 20138 deletions
94
.travis.yml
94
.travis.yml
|
|
@ -5,23 +5,44 @@ matrix:
|
||||||
include:
|
include:
|
||||||
- os: linux
|
- os: linux
|
||||||
dist: trusty
|
dist: trusty
|
||||||
go: 1.5.4
|
sudo: required
|
||||||
env:
|
go: 1.7.5
|
||||||
- GO15VENDOREXPERIMENT=1
|
script:
|
||||||
|
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install fuse
|
||||||
|
- sudo modprobe fuse
|
||||||
|
- sudo chmod 666 /dev/fuse
|
||||||
|
- sudo chown root:$USER /etc/fuse.conf
|
||||||
|
- go run build/ci.go install
|
||||||
|
- go run build/ci.go test -coverage
|
||||||
|
|
||||||
|
# These are the latest Go versions.
|
||||||
- os: linux
|
- os: linux
|
||||||
dist: trusty
|
dist: trusty
|
||||||
go: 1.6.2
|
sudo: required
|
||||||
- os: linux
|
go: 1.8
|
||||||
dist: trusty
|
script:
|
||||||
go: 1.7.4
|
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install fuse
|
||||||
|
- sudo modprobe fuse
|
||||||
|
- sudo chmod 666 /dev/fuse
|
||||||
|
- sudo chown root:$USER /etc/fuse.conf
|
||||||
|
- go run build/ci.go install
|
||||||
|
- go run build/ci.go test -coverage -misspell
|
||||||
|
|
||||||
- os: osx
|
- os: osx
|
||||||
go: 1.7.4
|
go: 1.8
|
||||||
|
sudo: required
|
||||||
|
script:
|
||||||
|
- brew update
|
||||||
|
- brew install caskroom/cask/brew-cask
|
||||||
|
- brew cask install osxfuse
|
||||||
|
- go run build/ci.go install
|
||||||
|
- go run build/ci.go test -coverage -misspell
|
||||||
|
|
||||||
# This builder does the Ubuntu PPA and Linux Azure uploads
|
# This builder does the Ubuntu PPA and Linux Azure uploads
|
||||||
- os: linux
|
- os: linux
|
||||||
dist: trusty
|
dist: trusty
|
||||||
sudo: required
|
sudo: required
|
||||||
go: 1.7.4
|
go: 1.8
|
||||||
env:
|
env:
|
||||||
- ubuntu-ppa
|
- ubuntu-ppa
|
||||||
- azure-linux
|
- azure-linux
|
||||||
|
|
@ -50,6 +71,51 @@ matrix:
|
||||||
- GOARM=6 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
- GOARM=6 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
- GOARM=7 CC=arm-linux-gnueabihf-gcc go run build/ci.go install -arch arm
|
- GOARM=7 CC=arm-linux-gnueabihf-gcc go run build/ci.go install -arch arm
|
||||||
- GOARM=7 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
- GOARM=7 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
# ARM64 linux builds are broken in Go 1.8 (https://github.com/golang/go/issues/19137), reenable in Go 1.8.1
|
||||||
|
# - CC=aarch64-linux-gnu-gcc go run build/ci.go install -arch arm64
|
||||||
|
# - go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
|
||||||
|
# This builder does the Linux Azure MIPS xgo uploads
|
||||||
|
- os: linux
|
||||||
|
dist: trusty
|
||||||
|
sudo: required
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
go: 1.8
|
||||||
|
env:
|
||||||
|
- azure-linux-mips
|
||||||
|
script:
|
||||||
|
- go run build/ci.go xgo --alltools -- --targets=linux/mips --ldflags '-extldflags "-static"' -v
|
||||||
|
- for bin in build/bin/*-linux-mips; do mv -f "${bin}" "${bin/-linux-mips/}"; done
|
||||||
|
- go run build/ci.go archive -arch mips -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
|
||||||
|
- go run build/ci.go xgo --alltools -- --targets=linux/mipsle --ldflags '-extldflags "-static"' -v
|
||||||
|
- for bin in build/bin/*-linux-mipsle; do mv -f "${bin}" "${bin/-linux-mipsle/}"; done
|
||||||
|
- go run build/ci.go archive -arch mipsle -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
|
||||||
|
- go run build/ci.go xgo --alltools -- --targets=linux/mips64 --ldflags '-extldflags "-static"' -v
|
||||||
|
- for bin in build/bin/*-linux-mips64; do mv -f "${bin}" "${bin/-linux-mips64/}"; done
|
||||||
|
- go run build/ci.go archive -arch mips64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
|
||||||
|
- go run build/ci.go xgo --alltools -- --targets=linux/mips64le --ldflags '-extldflags "-static"' -v
|
||||||
|
- for bin in build/bin/*-linux-mips64le; do mv -f "${bin}" "${bin/-linux-mips64le/}"; done
|
||||||
|
- go run build/ci.go archive -arch mips64le -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
|
||||||
|
# This builder is a temporary fallback for building ARM64 while Go 1.8 is fixed
|
||||||
|
- os: linux
|
||||||
|
dist: trusty
|
||||||
|
sudo: required
|
||||||
|
go: 1.7.5
|
||||||
|
env:
|
||||||
|
- azure-linux-arm64-fallback
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- gcc-multilib
|
||||||
|
script:
|
||||||
|
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install gcc-aarch64-linux-gnu libc6-dev-arm64-cross
|
||||||
|
- sudo ln -s /usr/include/asm-generic /usr/include/asm
|
||||||
|
|
||||||
- CC=aarch64-linux-gnu-gcc go run build/ci.go install -arch arm64
|
- CC=aarch64-linux-gnu-gcc go run build/ci.go install -arch arm64
|
||||||
- go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
- go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
|
||||||
|
|
@ -73,10 +139,10 @@ matrix:
|
||||||
- azure-android
|
- azure-android
|
||||||
- maven-android
|
- maven-android
|
||||||
before_install:
|
before_install:
|
||||||
- curl https://storage.googleapis.com/golang/go1.8rc3.linux-amd64.tar.gz | tar -xz
|
- curl https://storage.googleapis.com/golang/go1.8.linux-amd64.tar.gz | tar -xz
|
||||||
- export PATH=`pwd`/go/bin:$PATH
|
- export PATH=`pwd`/go/bin:$PATH
|
||||||
- export GOROOT=`pwd`/go
|
- export GOROOT=`pwd`/go
|
||||||
- export GOPATH=$HOME/go # Drop post Go 1.8
|
- export GOPATH=$HOME/go
|
||||||
script:
|
script:
|
||||||
# Build the Android archive and upload it to Maven Central and Azure
|
# Build the Android archive and upload it to Maven Central and Azure
|
||||||
- curl https://dl.google.com/android/repository/android-ndk-r13b-linux-x86_64.zip -o android-ndk-r13b.zip
|
- curl https://dl.google.com/android/repository/android-ndk-r13b-linux-x86_64.zip -o android-ndk-r13b.zip
|
||||||
|
|
@ -90,7 +156,7 @@ matrix:
|
||||||
|
|
||||||
# This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads
|
# This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads
|
||||||
- os: osx
|
- os: osx
|
||||||
go: 1.7.4
|
go: 1.8
|
||||||
env:
|
env:
|
||||||
- azure-osx
|
- azure-osx
|
||||||
- azure-ios
|
- azure-ios
|
||||||
|
|
@ -101,7 +167,7 @@ matrix:
|
||||||
|
|
||||||
# Build the iOS framework and upload it to CocoaPods and Azure
|
# Build the iOS framework and upload it to CocoaPods and Azure
|
||||||
- gem uninstall cocoapods -a
|
- gem uninstall cocoapods -a
|
||||||
- gem install cocoapods --pre
|
- gem install cocoapods
|
||||||
|
|
||||||
- mv ~/.cocoapods/repos/master ~/.cocoapods/repos/master.bak
|
- mv ~/.cocoapods/repos/master ~/.cocoapods/repos/master.bak
|
||||||
- sed -i '.bak' 's/repo.join/!repo.join/g' $(dirname `gem which cocoapods`)/cocoapods/sources_manager.rb
|
- sed -i '.bak' 's/repo.join/!repo.join/g' $(dirname `gem which cocoapods`)/cocoapods/sources_manager.rb
|
||||||
|
|
@ -116,7 +182,7 @@ install:
|
||||||
- go get golang.org/x/tools/cmd/cover
|
- go get golang.org/x/tools/cmd/cover
|
||||||
script:
|
script:
|
||||||
- go run build/ci.go install
|
- go run build/ci.go install
|
||||||
- go run build/ci.go test -coverage -vet -misspell
|
- go run build/ci.go test -coverage
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
webhooks:
|
webhooks:
|
||||||
|
|
|
||||||
43
Makefile
43
Makefile
|
|
@ -40,6 +40,15 @@ test: all
|
||||||
clean:
|
clean:
|
||||||
rm -fr build/_workspace/pkg/ $(GOBIN)/*
|
rm -fr build/_workspace/pkg/ $(GOBIN)/*
|
||||||
|
|
||||||
|
# The devtools target installs tools required for 'go generate'.
|
||||||
|
# You need to put $GOBIN (or $GOPATH/bin) in your PATH to use 'go generate'.
|
||||||
|
|
||||||
|
devtools:
|
||||||
|
go get -u golang.org/x/tools/cmd/stringer
|
||||||
|
go get -u github.com/jteeuwen/go-bindata/go-bindata
|
||||||
|
go get -u github.com/fjl/gencodec
|
||||||
|
go install ./cmd/abigen
|
||||||
|
|
||||||
# Cross Compilation Targets (xgo)
|
# Cross Compilation Targets (xgo)
|
||||||
|
|
||||||
geth-cross: geth-linux geth-darwin geth-windows geth-android geth-ios
|
geth-cross: geth-linux geth-darwin geth-windows geth-android geth-ios
|
||||||
|
|
@ -51,12 +60,12 @@ geth-linux: geth-linux-386 geth-linux-amd64 geth-linux-arm geth-linux-mips64 get
|
||||||
@ls -ld $(GOBIN)/geth-linux-*
|
@ls -ld $(GOBIN)/geth-linux-*
|
||||||
|
|
||||||
geth-linux-386:
|
geth-linux-386:
|
||||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/386 -v ./cmd/geth
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/386 -v ./cmd/geth
|
||||||
@echo "Linux 386 cross compilation done:"
|
@echo "Linux 386 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/geth-linux-* | grep 386
|
@ls -ld $(GOBIN)/geth-linux-* | grep 386
|
||||||
|
|
||||||
geth-linux-amd64:
|
geth-linux-amd64:
|
||||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/amd64 -v ./cmd/geth
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/amd64 -v ./cmd/geth
|
||||||
@echo "Linux amd64 cross compilation done:"
|
@echo "Linux amd64 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/geth-linux-* | grep amd64
|
@ls -ld $(GOBIN)/geth-linux-* | grep amd64
|
||||||
|
|
||||||
|
|
@ -65,32 +74,42 @@ geth-linux-arm: geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-ar
|
||||||
@ls -ld $(GOBIN)/geth-linux-* | grep arm
|
@ls -ld $(GOBIN)/geth-linux-* | grep arm
|
||||||
|
|
||||||
geth-linux-arm-5:
|
geth-linux-arm-5:
|
||||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-5 -v ./cmd/geth
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/arm-5 -v ./cmd/geth
|
||||||
@echo "Linux ARMv5 cross compilation done:"
|
@echo "Linux ARMv5 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/geth-linux-* | grep arm-5
|
@ls -ld $(GOBIN)/geth-linux-* | grep arm-5
|
||||||
|
|
||||||
geth-linux-arm-6:
|
geth-linux-arm-6:
|
||||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-6 -v ./cmd/geth
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/arm-6 -v ./cmd/geth
|
||||||
@echo "Linux ARMv6 cross compilation done:"
|
@echo "Linux ARMv6 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/geth-linux-* | grep arm-6
|
@ls -ld $(GOBIN)/geth-linux-* | grep arm-6
|
||||||
|
|
||||||
geth-linux-arm-7:
|
geth-linux-arm-7:
|
||||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-7 -v ./cmd/geth
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/arm-7 -v ./cmd/geth
|
||||||
@echo "Linux ARMv7 cross compilation done:"
|
@echo "Linux ARMv7 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/geth-linux-* | grep arm-7
|
@ls -ld $(GOBIN)/geth-linux-* | grep arm-7
|
||||||
|
|
||||||
geth-linux-arm64:
|
geth-linux-arm64:
|
||||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm64 -v ./cmd/geth
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/arm64 -v ./cmd/geth
|
||||||
@echo "Linux ARM64 cross compilation done:"
|
@echo "Linux ARM64 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/geth-linux-* | grep arm64
|
@ls -ld $(GOBIN)/geth-linux-* | grep arm64
|
||||||
|
|
||||||
|
geth-linux-mips:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/mips --ldflags '-extldflags "-static"' -v ./cmd/geth
|
||||||
|
@echo "Linux MIPS cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-linux-* | grep mips
|
||||||
|
|
||||||
|
geth-linux-mipsle:
|
||||||
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/mipsle --ldflags '-extldflags "-static"' -v ./cmd/geth
|
||||||
|
@echo "Linux MIPSle cross compilation done:"
|
||||||
|
@ls -ld $(GOBIN)/geth-linux-* | grep mipsle
|
||||||
|
|
||||||
geth-linux-mips64:
|
geth-linux-mips64:
|
||||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64 -v ./cmd/geth
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/mips64 --ldflags '-extldflags "-static"' -v ./cmd/geth
|
||||||
@echo "Linux MIPS64 cross compilation done:"
|
@echo "Linux MIPS64 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/geth-linux-* | grep mips64
|
@ls -ld $(GOBIN)/geth-linux-* | grep mips64
|
||||||
|
|
||||||
geth-linux-mips64le:
|
geth-linux-mips64le:
|
||||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64le -v ./cmd/geth
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/mips64le --ldflags '-extldflags "-static"' -v ./cmd/geth
|
||||||
@echo "Linux MIPS64le cross compilation done:"
|
@echo "Linux MIPS64le cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/geth-linux-* | grep mips64le
|
@ls -ld $(GOBIN)/geth-linux-* | grep mips64le
|
||||||
|
|
||||||
|
|
@ -99,12 +118,12 @@ geth-darwin: geth-darwin-386 geth-darwin-amd64
|
||||||
@ls -ld $(GOBIN)/geth-darwin-*
|
@ls -ld $(GOBIN)/geth-darwin-*
|
||||||
|
|
||||||
geth-darwin-386:
|
geth-darwin-386:
|
||||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=darwin/386 -v ./cmd/geth
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=darwin/386 -v ./cmd/geth
|
||||||
@echo "Darwin 386 cross compilation done:"
|
@echo "Darwin 386 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/geth-darwin-* | grep 386
|
@ls -ld $(GOBIN)/geth-darwin-* | grep 386
|
||||||
|
|
||||||
geth-darwin-amd64:
|
geth-darwin-amd64:
|
||||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=darwin/amd64 -v ./cmd/geth
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=darwin/amd64 -v ./cmd/geth
|
||||||
@echo "Darwin amd64 cross compilation done:"
|
@echo "Darwin amd64 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/geth-darwin-* | grep amd64
|
@ls -ld $(GOBIN)/geth-darwin-* | grep amd64
|
||||||
|
|
||||||
|
|
@ -113,11 +132,11 @@ geth-windows: geth-windows-386 geth-windows-amd64
|
||||||
@ls -ld $(GOBIN)/geth-windows-*
|
@ls -ld $(GOBIN)/geth-windows-*
|
||||||
|
|
||||||
geth-windows-386:
|
geth-windows-386:
|
||||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=windows/386 -v ./cmd/geth
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=windows/386 -v ./cmd/geth
|
||||||
@echo "Windows 386 cross compilation done:"
|
@echo "Windows 386 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/geth-windows-* | grep 386
|
@ls -ld $(GOBIN)/geth-windows-* | grep 386
|
||||||
|
|
||||||
geth-windows-amd64:
|
geth-windows-amd64:
|
||||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=windows/amd64 -v ./cmd/geth
|
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=windows/amd64 -v ./cmd/geth
|
||||||
@echo "Windows amd64 cross compilation done:"
|
@echo "Windows amd64 cross compilation done:"
|
||||||
@ls -ld $(GOBIN)/geth-windows-* | grep amd64
|
@ls -ld $(GOBIN)/geth-windows-* | grep amd64
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ For prerequisites and detailed build instructions please read the
|
||||||
[Installation Instructions](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum)
|
[Installation Instructions](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum)
|
||||||
on the wiki.
|
on the wiki.
|
||||||
|
|
||||||
Building geth requires both a Go and a C compiler.
|
Building geth requires both a Go (version 1.7 or later) and a C compiler.
|
||||||
You can install them using your favourite package manager.
|
You can install them using your favourite package manager.
|
||||||
Once the dependencies are installed, run
|
Once the dependencies are installed, run
|
||||||
|
|
||||||
|
|
@ -118,7 +118,7 @@ As a developer, sooner rather than later you'll want to start interacting with G
|
||||||
network via your own programs and not manually through the console. To aid this, Geth has built in
|
network via your own programs and not manually through the console. To aid this, Geth has built in
|
||||||
support for a JSON-RPC based APIs ([standard APIs](https://github.com/ethereum/wiki/wiki/JSON-RPC) and
|
support for a JSON-RPC based APIs ([standard APIs](https://github.com/ethereum/wiki/wiki/JSON-RPC) and
|
||||||
[Geth specific APIs](https://github.com/ethereum/go-ethereum/wiki/Management-APIs)). These can be
|
[Geth specific APIs](https://github.com/ethereum/go-ethereum/wiki/Management-APIs)). These can be
|
||||||
exposed via HTTP, WebSockets and IPC (unix sockets on unix based platroms, and named pipes on Windows).
|
exposed via HTTP, WebSockets and IPC (unix sockets on unix based platforms, and named pipes on Windows).
|
||||||
|
|
||||||
The IPC interface is enabled by default and exposes all the APIs supported by Geth, whereas the HTTP
|
The IPC interface is enabled by default and exposes all the APIs supported by Geth, whereas the HTTP
|
||||||
and WS interfaces need to manually be enabled and only expose a subset of APIs due to security reasons.
|
and WS interfaces need to manually be enabled and only expose a subset of APIs due to security reasons.
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
1.5.9
|
1.6.0
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
@ -129,16 +130,15 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) {
|
||||||
var size int
|
var size int
|
||||||
var offset int
|
var offset int
|
||||||
if t.Type.IsSlice {
|
if t.Type.IsSlice {
|
||||||
|
|
||||||
// get the offset which determines the start of this array ...
|
// get the offset which determines the start of this array ...
|
||||||
offset = int(common.BytesToBig(output[index : index+32]).Uint64())
|
offset = int(binary.BigEndian.Uint64(output[index+24 : index+32]))
|
||||||
if offset+32 > len(output) {
|
if offset+32 > len(output) {
|
||||||
return nil, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32)
|
return nil, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32)
|
||||||
}
|
}
|
||||||
|
|
||||||
slice = output[offset:]
|
slice = output[offset:]
|
||||||
// ... starting with the size of the array in elements ...
|
// ... starting with the size of the array in elements ...
|
||||||
size = int(common.BytesToBig(slice[:32]).Uint64())
|
size = int(binary.BigEndian.Uint64(slice[24:32]))
|
||||||
slice = slice[32:]
|
slice = slice[32:]
|
||||||
// ... and make sure that we've at the very least the amount of bytes
|
// ... and make sure that we've at the very least the amount of bytes
|
||||||
// available in the buffer.
|
// available in the buffer.
|
||||||
|
|
@ -147,7 +147,7 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// reslice to match the required size
|
// reslice to match the required size
|
||||||
slice = slice[:(size * 32)]
|
slice = slice[:size*32]
|
||||||
} else if t.Type.IsArray {
|
} else if t.Type.IsArray {
|
||||||
//get the number of elements in the array
|
//get the number of elements in the array
|
||||||
size = t.Type.SliceSize
|
size = t.Type.SliceSize
|
||||||
|
|
@ -165,33 +165,12 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) {
|
||||||
inter interface{} // interface type
|
inter interface{} // interface type
|
||||||
returnOutput = slice[i*32 : i*32+32] // the return output
|
returnOutput = slice[i*32 : i*32+32] // the return output
|
||||||
)
|
)
|
||||||
|
|
||||||
// set inter to the correct type (cast)
|
// set inter to the correct type (cast)
|
||||||
switch elem.T {
|
switch elem.T {
|
||||||
case IntTy, UintTy:
|
case IntTy, UintTy:
|
||||||
bigNum := common.BytesToBig(returnOutput)
|
inter = readInteger(t.Type.Kind, returnOutput)
|
||||||
switch t.Type.Kind {
|
|
||||||
case reflect.Uint8:
|
|
||||||
inter = uint8(bigNum.Uint64())
|
|
||||||
case reflect.Uint16:
|
|
||||||
inter = uint16(bigNum.Uint64())
|
|
||||||
case reflect.Uint32:
|
|
||||||
inter = uint32(bigNum.Uint64())
|
|
||||||
case reflect.Uint64:
|
|
||||||
inter = bigNum.Uint64()
|
|
||||||
case reflect.Int8:
|
|
||||||
inter = int8(bigNum.Int64())
|
|
||||||
case reflect.Int16:
|
|
||||||
inter = int16(bigNum.Int64())
|
|
||||||
case reflect.Int32:
|
|
||||||
inter = int32(bigNum.Int64())
|
|
||||||
case reflect.Int64:
|
|
||||||
inter = bigNum.Int64()
|
|
||||||
default:
|
|
||||||
inter = common.BytesToBig(returnOutput)
|
|
||||||
}
|
|
||||||
case BoolTy:
|
case BoolTy:
|
||||||
inter = common.BytesToBig(returnOutput).Uint64() > 0
|
inter = !allZero(returnOutput)
|
||||||
case AddressTy:
|
case AddressTy:
|
||||||
inter = common.BytesToAddress(returnOutput)
|
inter = common.BytesToAddress(returnOutput)
|
||||||
case HashTy:
|
case HashTy:
|
||||||
|
|
@ -207,6 +186,38 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) {
|
||||||
return refSlice.Interface(), nil
|
return refSlice.Interface(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func readInteger(kind reflect.Kind, b []byte) interface{} {
|
||||||
|
switch kind {
|
||||||
|
case reflect.Uint8:
|
||||||
|
return uint8(b[len(b)-1])
|
||||||
|
case reflect.Uint16:
|
||||||
|
return binary.BigEndian.Uint16(b[len(b)-2:])
|
||||||
|
case reflect.Uint32:
|
||||||
|
return binary.BigEndian.Uint32(b[len(b)-4:])
|
||||||
|
case reflect.Uint64:
|
||||||
|
return binary.BigEndian.Uint64(b[len(b)-8:])
|
||||||
|
case reflect.Int8:
|
||||||
|
return int8(b[len(b)-1])
|
||||||
|
case reflect.Int16:
|
||||||
|
return int16(binary.BigEndian.Uint16(b[len(b)-2:]))
|
||||||
|
case reflect.Int32:
|
||||||
|
return int32(binary.BigEndian.Uint32(b[len(b)-4:]))
|
||||||
|
case reflect.Int64:
|
||||||
|
return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
|
||||||
|
default:
|
||||||
|
return new(big.Int).SetBytes(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func allZero(b []byte) bool {
|
||||||
|
for _, byte := range b {
|
||||||
|
if byte != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// toGoType parses the input and casts it to the proper type defined by the ABI
|
// toGoType parses the input and casts it to the proper type defined by the ABI
|
||||||
// argument in T.
|
// argument in T.
|
||||||
func toGoType(i int, t Argument, output []byte) (interface{}, error) {
|
func toGoType(i int, t Argument, output []byte) (interface{}, error) {
|
||||||
|
|
@ -226,12 +237,12 @@ func toGoType(i int, t Argument, output []byte) (interface{}, error) {
|
||||||
switch t.Type.T {
|
switch t.Type.T {
|
||||||
case StringTy, BytesTy: // variable arrays are written at the end of the return bytes
|
case StringTy, BytesTy: // variable arrays are written at the end of the return bytes
|
||||||
// parse offset from which we should start reading
|
// parse offset from which we should start reading
|
||||||
offset := int(common.BytesToBig(output[index : index+32]).Uint64())
|
offset := int(binary.BigEndian.Uint64(output[index+24 : index+32]))
|
||||||
if offset+32 > len(output) {
|
if offset+32 > len(output) {
|
||||||
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32)
|
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32)
|
||||||
}
|
}
|
||||||
// parse the size up until we should be reading
|
// parse the size up until we should be reading
|
||||||
size := int(common.BytesToBig(output[offset : offset+32]).Uint64())
|
size := int(binary.BigEndian.Uint64(output[offset+24 : offset+32]))
|
||||||
if offset+32+size > len(output) {
|
if offset+32+size > len(output) {
|
||||||
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32+size)
|
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32+size)
|
||||||
}
|
}
|
||||||
|
|
@ -245,32 +256,9 @@ func toGoType(i int, t Argument, output []byte) (interface{}, error) {
|
||||||
// convert the bytes to whatever is specified by the ABI.
|
// convert the bytes to whatever is specified by the ABI.
|
||||||
switch t.Type.T {
|
switch t.Type.T {
|
||||||
case IntTy, UintTy:
|
case IntTy, UintTy:
|
||||||
bigNum := common.BytesToBig(returnOutput)
|
return readInteger(t.Type.Kind, returnOutput), nil
|
||||||
|
|
||||||
// If the type is a integer convert to the integer type
|
|
||||||
// specified by the ABI.
|
|
||||||
switch t.Type.Kind {
|
|
||||||
case reflect.Uint8:
|
|
||||||
return uint8(bigNum.Uint64()), nil
|
|
||||||
case reflect.Uint16:
|
|
||||||
return uint16(bigNum.Uint64()), nil
|
|
||||||
case reflect.Uint32:
|
|
||||||
return uint32(bigNum.Uint64()), nil
|
|
||||||
case reflect.Uint64:
|
|
||||||
return uint64(bigNum.Uint64()), nil
|
|
||||||
case reflect.Int8:
|
|
||||||
return int8(bigNum.Int64()), nil
|
|
||||||
case reflect.Int16:
|
|
||||||
return int16(bigNum.Int64()), nil
|
|
||||||
case reflect.Int32:
|
|
||||||
return int32(bigNum.Int64()), nil
|
|
||||||
case reflect.Int64:
|
|
||||||
return int64(bigNum.Int64()), nil
|
|
||||||
case reflect.Ptr:
|
|
||||||
return bigNum, nil
|
|
||||||
}
|
|
||||||
case BoolTy:
|
case BoolTy:
|
||||||
return common.BytesToBig(returnOutput).Uint64() > 0, nil
|
return !allZero(returnOutput), nil
|
||||||
case AddressTy:
|
case AddressTy:
|
||||||
return common.BytesToAddress(returnOutput), nil
|
return common.BytesToAddress(returnOutput), nil
|
||||||
case HashTy:
|
case HashTy:
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,13 @@
|
||||||
package bind
|
package bind
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package backends
|
package backends
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -25,6 +26,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum"
|
"github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
@ -32,12 +34,9 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"golang.org/x/net/context"
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Default chain configuration which sets homestead phase at block 0 (i.e. no frontier)
|
|
||||||
var chainConfig = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0), EIP150Block: new(big.Int), EIP158Block: new(big.Int)}
|
|
||||||
|
|
||||||
// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
|
// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
|
||||||
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
|
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
|
||||||
|
|
||||||
|
|
@ -58,11 +57,12 @@ type SimulatedBackend struct {
|
||||||
|
|
||||||
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
|
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
|
||||||
// for testing purposes.
|
// for testing purposes.
|
||||||
func NewSimulatedBackend(accounts ...core.GenesisAccount) *SimulatedBackend {
|
func NewSimulatedBackend(alloc core.GenesisAlloc) *SimulatedBackend {
|
||||||
database, _ := ethdb.NewMemDatabase()
|
database, _ := ethdb.NewMemDatabase()
|
||||||
core.WriteGenesisBlockForTesting(database, accounts...)
|
genesis := core.Genesis{Config: params.AllProtocolChanges, Alloc: alloc}
|
||||||
blockchain, _ := core.NewBlockChain(database, chainConfig, new(core.FakePow), new(event.TypeMux), vm.Config{})
|
genesis.MustCommit(database)
|
||||||
backend := &SimulatedBackend{database: database, blockchain: blockchain}
|
blockchain, _ := core.NewBlockChain(database, genesis.Config, new(pow.FakePow), new(event.TypeMux), vm.Config{})
|
||||||
|
backend := &SimulatedBackend{database: database, blockchain: blockchain, config: genesis.Config}
|
||||||
backend.rollback()
|
backend.rollback()
|
||||||
return backend
|
return backend
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +88,7 @@ func (b *SimulatedBackend) Rollback() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *SimulatedBackend) rollback() {
|
func (b *SimulatedBackend) rollback() {
|
||||||
blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {})
|
blocks, _ := core.GenerateChain(b.config, 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)
|
||||||
}
|
}
|
||||||
|
|
@ -236,7 +236,7 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
|
||||||
if call.GasPrice == nil {
|
if call.GasPrice == nil {
|
||||||
call.GasPrice = big.NewInt(1)
|
call.GasPrice = big.NewInt(1)
|
||||||
}
|
}
|
||||||
if call.Gas == nil || call.Gas.BitLen() == 0 {
|
if call.Gas == nil || call.Gas.Sign() == 0 {
|
||||||
call.Gas = big.NewInt(50000000)
|
call.Gas = big.NewInt(50000000)
|
||||||
}
|
}
|
||||||
if call.Value == nil {
|
if call.Value == nil {
|
||||||
|
|
@ -244,15 +244,15 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
|
||||||
}
|
}
|
||||||
// Set infinite balance to the fake caller account.
|
// Set infinite balance to the fake caller account.
|
||||||
from := statedb.GetOrNewStateObject(call.From)
|
from := statedb.GetOrNewStateObject(call.From)
|
||||||
from.SetBalance(common.MaxBig)
|
from.SetBalance(math.MaxBig256)
|
||||||
// Execute the call.
|
// Execute the call.
|
||||||
msg := callmsg{call}
|
msg := callmsg{call}
|
||||||
|
|
||||||
evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain)
|
evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain)
|
||||||
// Create a new environment which holds all relevant information
|
// Create a new environment which holds all relevant information
|
||||||
// about the transaction and calling mechanisms.
|
// about the transaction and calling mechanisms.
|
||||||
vmenv := vm.NewEVM(evmContext, statedb, chainConfig, vm.Config{})
|
vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{})
|
||||||
gaspool := new(core.GasPool).AddGas(common.MaxBig)
|
gaspool := new(core.GasPool).AddGas(math.MaxBig256)
|
||||||
ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
|
ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
|
||||||
return ret, gasUsed, err
|
return ret, gasUsed, err
|
||||||
}
|
}
|
||||||
|
|
@ -272,7 +272,7 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
|
||||||
panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce))
|
panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce))
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) {
|
blocks, _ := core.GenerateChain(b.config, 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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package bind
|
package bind
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -26,7 +27,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SignerFn is a signer function callback when a contract requires a method to
|
// SignerFn is a signer function callback when a contract requires a method to
|
||||||
|
|
@ -36,6 +36,7 @@ type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Tra
|
||||||
// CallOpts is the collection of options to fine tune a contract call request.
|
// CallOpts is the collection of options to fine tune a contract call request.
|
||||||
type CallOpts struct {
|
type CallOpts struct {
|
||||||
Pending bool // Whether to operate on the pending state or the last known one
|
Pending bool // Whether to operate on the pending state or the last known one
|
||||||
|
From common.Address // Optional the sender address, otherwise the first account is used
|
||||||
|
|
||||||
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
||||||
}
|
}
|
||||||
|
|
@ -108,7 +109,7 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string,
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
msg = ethereum.CallMsg{To: &c.address, Data: input}
|
msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input}
|
||||||
ctx = ensureContext(opts.Context)
|
ctx = ensureContext(opts.Context)
|
||||||
code []byte
|
code []byte
|
||||||
output []byte
|
output []byte
|
||||||
|
|
|
||||||
|
|
@ -169,7 +169,7 @@ var bindTests = []struct {
|
||||||
// Generate a new random account and a funded simulator
|
// Generate a new random account and a funded simulator
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth := bind.NewKeyedTransactor(key)
|
auth := bind.NewKeyedTransactor(key)
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)})
|
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
|
||||||
|
|
||||||
// Deploy an interaction tester contract and call a transaction on it
|
// Deploy an interaction tester contract and call a transaction on it
|
||||||
_, _, interactor, err := DeployInteractor(auth, sim, "Deploy string")
|
_, _, interactor, err := DeployInteractor(auth, sim, "Deploy string")
|
||||||
|
|
@ -210,7 +210,7 @@ var bindTests = []struct {
|
||||||
// Generate a new random account and a funded simulator
|
// Generate a new random account and a funded simulator
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth := bind.NewKeyedTransactor(key)
|
auth := bind.NewKeyedTransactor(key)
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)})
|
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
|
||||||
|
|
||||||
// Deploy a tuple tester contract and execute a structured call on it
|
// Deploy a tuple tester contract and execute a structured call on it
|
||||||
_, _, getter, err := DeployGetter(auth, sim)
|
_, _, getter, err := DeployGetter(auth, sim)
|
||||||
|
|
@ -242,7 +242,7 @@ var bindTests = []struct {
|
||||||
// Generate a new random account and a funded simulator
|
// Generate a new random account and a funded simulator
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth := bind.NewKeyedTransactor(key)
|
auth := bind.NewKeyedTransactor(key)
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)})
|
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
|
||||||
|
|
||||||
// Deploy a tuple tester contract and execute a structured call on it
|
// Deploy a tuple tester contract and execute a structured call on it
|
||||||
_, _, tupler, err := DeployTupler(auth, sim)
|
_, _, tupler, err := DeployTupler(auth, sim)
|
||||||
|
|
@ -284,7 +284,7 @@ var bindTests = []struct {
|
||||||
// Generate a new random account and a funded simulator
|
// Generate a new random account and a funded simulator
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth := bind.NewKeyedTransactor(key)
|
auth := bind.NewKeyedTransactor(key)
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)})
|
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
|
||||||
|
|
||||||
// Deploy a slice tester contract and execute a n array call on it
|
// Deploy a slice tester contract and execute a n array call on it
|
||||||
_, _, slicer, err := DeploySlicer(auth, sim)
|
_, _, slicer, err := DeploySlicer(auth, sim)
|
||||||
|
|
@ -318,7 +318,7 @@ var bindTests = []struct {
|
||||||
// Generate a new random account and a funded simulator
|
// Generate a new random account and a funded simulator
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth := bind.NewKeyedTransactor(key)
|
auth := bind.NewKeyedTransactor(key)
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)})
|
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
|
||||||
|
|
||||||
// Deploy a default method invoker contract and execute its default method
|
// Deploy a default method invoker contract and execute its default method
|
||||||
_, _, defaulter, err := DeployDefaulter(auth, sim)
|
_, _, defaulter, err := DeployDefaulter(auth, sim)
|
||||||
|
|
@ -351,7 +351,7 @@ var bindTests = []struct {
|
||||||
`[{"constant":true,"inputs":[],"name":"String","outputs":[{"name":"","type":"string"}],"type":"function"}]`,
|
`[{"constant":true,"inputs":[],"name":"String","outputs":[{"name":"","type":"string"}],"type":"function"}]`,
|
||||||
`
|
`
|
||||||
// Create a simulator and wrap a non-deployed contract
|
// Create a simulator and wrap a non-deployed contract
|
||||||
sim := backends.NewSimulatedBackend()
|
sim := backends.NewSimulatedBackend(nil)
|
||||||
|
|
||||||
nonexistent, err := NewNonExistent(common.Address{}, sim)
|
nonexistent, err := NewNonExistent(common.Address{}, sim)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -387,7 +387,7 @@ var bindTests = []struct {
|
||||||
// Generate a new random account and a funded simulator
|
// Generate a new random account and a funded simulator
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth := bind.NewKeyedTransactor(key)
|
auth := bind.NewKeyedTransactor(key)
|
||||||
sim := backends.NewSimulatedBackend(core.GenesisAccount{Address: auth.From, Balance: big.NewInt(10000000000)})
|
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
|
||||||
|
|
||||||
// Deploy a funky gas pattern contract
|
// Deploy a funky gas pattern contract
|
||||||
_, _, limiter, err := DeployFunkyGasPattern(auth, sim)
|
_, _, limiter, err := DeployFunkyGasPattern(auth, sim)
|
||||||
|
|
@ -408,6 +408,45 @@ var bindTests = []struct {
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
|
// Test that constant functions can be called from an (optional) specified address
|
||||||
|
{
|
||||||
|
`CallFrom`,
|
||||||
|
`
|
||||||
|
contract CallFrom {
|
||||||
|
function callFrom() constant returns(address) {
|
||||||
|
return msg.sender;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, `6060604052346000575b6086806100176000396000f300606060405263ffffffff60e060020a60003504166349f8e98281146022575b6000565b34600057602c6055565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b335b905600a165627a7a72305820aef6b7685c0fa24ba6027e4870404a57df701473fe4107741805c19f5138417c0029`,
|
||||||
|
`[{"constant":true,"inputs":[],"name":"callFrom","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"}]`,
|
||||||
|
`
|
||||||
|
// Generate a new random account and a funded simulator
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
auth := bind.NewKeyedTransactor(key)
|
||||||
|
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
|
||||||
|
|
||||||
|
// Deploy a sender tester contract and execute a structured call on it
|
||||||
|
_, _, callfrom, err := DeployCallFrom(auth, sim)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to deploy sender contract: %v", err)
|
||||||
|
}
|
||||||
|
sim.Commit()
|
||||||
|
|
||||||
|
if res, err := callfrom.CallFrom(nil); err != nil {
|
||||||
|
t.Errorf("Failed to call constant function: %v", err)
|
||||||
|
} else if res != (common.Address{}) {
|
||||||
|
t.Errorf("Invalid address returned, want: %x, got: %x", (common.Address{}), res)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range []common.Address{common.Address{}, common.Address{1}, common.Address{2}} {
|
||||||
|
if res, err := callfrom.CallFrom(&bind.CallOpts{From: addr}); err != nil {
|
||||||
|
t.Fatalf("Failed to call constant function: %v", err)
|
||||||
|
} else if res != addr {
|
||||||
|
t.Fatalf("Invalid address returned, want: %x, got: %x", addr, res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that packages generated by the binder can be successfully compiled and
|
// Tests that packages generated by the binder can be successfully compiled and
|
||||||
|
|
@ -419,7 +458,7 @@ func TestBindings(t *testing.T) {
|
||||||
t.Skip("go sdk not found for testing")
|
t.Skip("go sdk not found for testing")
|
||||||
}
|
}
|
||||||
// Skip the test if the go-ethereum sources are symlinked (https://github.com/golang/go/issues/14845)
|
// Skip the test if the go-ethereum sources are symlinked (https://github.com/golang/go/issues/14845)
|
||||||
linkTestCode := fmt.Sprintf("package linktest\nfunc CheckSymlinks(){\nfmt.Println(backends.NewSimulatedBackend())\n}")
|
linkTestCode := fmt.Sprintf("package linktest\nfunc CheckSymlinks(){\nfmt.Println(backends.NewSimulatedBackend(nil))\n}")
|
||||||
linkTestDeps, err := imports.Process("", []byte(linkTestCode), nil)
|
linkTestDeps, err := imports.Process("", []byte(linkTestCode), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed check for goimports symlink bug: %v", err)
|
t.Fatalf("failed check for goimports symlink bug: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -17,31 +17,31 @@
|
||||||
package bind
|
package bind
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// WaitMined waits for tx to be mined on the blockchain.
|
// WaitMined waits for tx to be mined on the blockchain.
|
||||||
// It stops waiting when the context is canceled.
|
// It stops waiting when the context is canceled.
|
||||||
func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*types.Receipt, error) {
|
func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*types.Receipt, error) {
|
||||||
queryTicker := time.NewTicker(1 * time.Second)
|
queryTicker := time.NewTicker(time.Second)
|
||||||
defer queryTicker.Stop()
|
defer queryTicker.Stop()
|
||||||
loghash := tx.Hash().Hex()[:8]
|
|
||||||
|
logger := log.New("hash", tx.Hash())
|
||||||
for {
|
for {
|
||||||
receipt, err := b.TransactionReceipt(ctx, tx.Hash())
|
receipt, err := b.TransactionReceipt(ctx, tx.Hash())
|
||||||
if receipt != nil {
|
if receipt != nil {
|
||||||
return receipt, nil
|
return receipt, nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Detail).Infof("tx %x error: %v", loghash, err)
|
logger.Trace("Receipt retrieval failed", "err", err)
|
||||||
} else {
|
} else {
|
||||||
glog.V(logger.Detail).Infof("tx %x not yet mined...", loghash)
|
logger.Trace("Transaction not yet mined")
|
||||||
}
|
}
|
||||||
// Wait for the next round.
|
// Wait for the next round.
|
||||||
select {
|
select {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package bind_test
|
package bind_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -27,7 +28,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
|
@ -53,9 +53,8 @@ var waitDeployedTests = map[string]struct {
|
||||||
|
|
||||||
func TestWaitDeployed(t *testing.T) {
|
func TestWaitDeployed(t *testing.T) {
|
||||||
for name, test := range waitDeployedTests {
|
for name, test := range waitDeployedTests {
|
||||||
backend := backends.NewSimulatedBackend(core.GenesisAccount{
|
backend := backends.NewSimulatedBackend(core.GenesisAlloc{
|
||||||
Address: crypto.PubkeyToAddress(testKey.PublicKey),
|
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000)},
|
||||||
Balance: big.NewInt(10000000000),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Create the transaction.
|
// Create the transaction.
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -58,7 +59,7 @@ var (
|
||||||
|
|
||||||
// U256 converts a big Int into a 256bit EVM number.
|
// U256 converts a big Int into a 256bit EVM number.
|
||||||
func U256(n *big.Int) []byte {
|
func U256(n *big.Int) []byte {
|
||||||
return common.LeftPadBytes(common.U256(n).Bytes(), 32)
|
return math.PaddedBigBytes(math.U256(n), 32)
|
||||||
}
|
}
|
||||||
|
|
||||||
// packNum packs the given number (using the reflect value) and will cast it to appropriate number representation
|
// packNum packs the given number (using the reflect value) and will cast it to appropriate number representation
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
)
|
)
|
||||||
|
|
||||||
// packBytesSlice packs the given bytes as [L, V] as the canonical representation
|
// packBytesSlice packs the given bytes as [L, V] as the canonical representation
|
||||||
|
|
@ -45,9 +46,9 @@ func packElement(t Type, reflectValue reflect.Value) []byte {
|
||||||
return common.LeftPadBytes(reflectValue.Bytes(), 32)
|
return common.LeftPadBytes(reflectValue.Bytes(), 32)
|
||||||
case BoolTy:
|
case BoolTy:
|
||||||
if reflectValue.Bool() {
|
if reflectValue.Bool() {
|
||||||
return common.LeftPadBytes(common.Big1.Bytes(), 32)
|
return math.PaddedBigBytes(common.Big1, 32)
|
||||||
} else {
|
} else {
|
||||||
return common.LeftPadBytes(common.Big0.Bytes(), 32)
|
return math.PaddedBigBytes(common.Big0, 32)
|
||||||
}
|
}
|
||||||
case BytesTy:
|
case BytesTy:
|
||||||
if reflectValue.Kind() == reflect.Array {
|
if reflectValue.Kind() == reflect.Array {
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Minimum amount of time between cache reloads. This limit applies if the platform does
|
// Minimum amount of time between cache reloads. This limit applies if the platform does
|
||||||
|
|
@ -210,8 +209,8 @@ func (ac *accountCache) close() {
|
||||||
// Callers must hold ac.mu.
|
// Callers must hold ac.mu.
|
||||||
func (ac *accountCache) reload() {
|
func (ac *accountCache) reload() {
|
||||||
accounts, err := ac.scan()
|
accounts, err := ac.scan()
|
||||||
if err != nil && glog.V(logger.Debug) {
|
if err != nil {
|
||||||
glog.Errorf("can't load keys: %v", err)
|
log.Debug("Failed to reload keystore contents", "err", err)
|
||||||
}
|
}
|
||||||
ac.all = accounts
|
ac.all = accounts
|
||||||
sort.Sort(ac.all)
|
sort.Sort(ac.all)
|
||||||
|
|
@ -225,7 +224,7 @@ func (ac *accountCache) reload() {
|
||||||
case ac.notify <- struct{}{}:
|
case ac.notify <- struct{}{}:
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
glog.V(logger.Debug).Infof("reloaded keys, cache has %d accounts", len(ac.all))
|
log.Debug("Reloaded keystore contents", "accounts", len(ac.all))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ac *accountCache) scan() ([]accounts.Account, error) {
|
func (ac *accountCache) scan() ([]accounts.Account, error) {
|
||||||
|
|
@ -244,12 +243,14 @@ func (ac *accountCache) scan() ([]accounts.Account, error) {
|
||||||
for _, fi := range files {
|
for _, fi := range files {
|
||||||
path := filepath.Join(ac.keydir, fi.Name())
|
path := filepath.Join(ac.keydir, fi.Name())
|
||||||
if skipKeyFile(fi) {
|
if skipKeyFile(fi) {
|
||||||
glog.V(logger.Detail).Infof("ignoring file %s", path)
|
log.Trace("Ignoring file on account scan", "path", path)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
logger := log.New("path", path)
|
||||||
|
|
||||||
fd, err := os.Open(path)
|
fd, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Detail).Infoln(err)
|
logger.Trace("Failed to open keystore file", "err", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
buf.Reset(fd)
|
buf.Reset(fd)
|
||||||
|
|
@ -259,9 +260,9 @@ func (ac *accountCache) scan() ([]accounts.Account, error) {
|
||||||
addr := common.HexToAddress(keyJSON.Address)
|
addr := common.HexToAddress(keyJSON.Address)
|
||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
glog.V(logger.Debug).Infof("can't decode key %s: %v", path, err)
|
logger.Debug("Failed to decode keystore key", "err", err)
|
||||||
case (addr == common.Address{}):
|
case (addr == common.Address{}):
|
||||||
glog.V(logger.Debug).Infof("can't decode key %s: missing or zero address", path)
|
logger.Debug("Failed to decode keystore key", "err", "missing or zero address")
|
||||||
default:
|
default:
|
||||||
addrs = append(addrs, accounts.Account{Address: addr, URL: accounts.URL{Scheme: KeyStoreScheme, Path: path}})
|
addrs = append(addrs, accounts.Account{Address: addr, URL: accounts.URL{Scheme: KeyStoreScheme, Path: path}})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/crypto/secp256k1"
|
|
||||||
"github.com/pborman/uuid"
|
"github.com/pborman/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -157,7 +156,7 @@ func NewKeyForDirectICAP(rand io.Reader) *Key {
|
||||||
panic("key generation: could not read from random source: " + err.Error())
|
panic("key generation: could not read from random source: " + err.Error())
|
||||||
}
|
}
|
||||||
reader := bytes.NewReader(randBytes)
|
reader := bytes.NewReader(randBytes)
|
||||||
privateKeyECDSA, err := ecdsa.GenerateKey(secp256k1.S256(), reader)
|
privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
|
panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -169,7 +168,7 @@ func NewKeyForDirectICAP(rand io.Reader) *Key {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newKey(rand io.Reader) (*Key, error) {
|
func newKey(rand io.Reader) (*Key, error) {
|
||||||
privateKeyECDSA, err := ecdsa.GenerateKey(secp256k1.S256(), rand)
|
privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), rand)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/crypto/randentropy"
|
"github.com/ethereum/go-ethereum/crypto/randentropy"
|
||||||
"github.com/pborman/uuid"
|
"github.com/pborman/uuid"
|
||||||
|
|
@ -115,8 +116,7 @@ func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
encryptKey := derivedKey[:16]
|
encryptKey := derivedKey[:16]
|
||||||
keyBytes0 := crypto.FromECDSA(key.PrivateKey)
|
keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32)
|
||||||
keyBytes := common.LeftPadBytes(keyBytes0, 32)
|
|
||||||
|
|
||||||
iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16
|
iv := randentropy.GetEntropyCSPRNG(aes.BlockSize) // 16
|
||||||
cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv)
|
cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv)
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,7 @@ package keystore
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/rjeczalik/notify"
|
"github.com/rjeczalik/notify"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -64,15 +63,16 @@ func (w *watcher) loop() {
|
||||||
w.starting = false
|
w.starting = false
|
||||||
w.ac.mu.Unlock()
|
w.ac.mu.Unlock()
|
||||||
}()
|
}()
|
||||||
|
logger := log.New("path", w.ac.keydir)
|
||||||
|
|
||||||
err := notify.Watch(w.ac.keydir, w.ev, notify.All)
|
if err := notify.Watch(w.ac.keydir, w.ev, notify.All); err != nil {
|
||||||
if err != nil {
|
logger.Trace("Failed to watch keystore folder", "err", err)
|
||||||
glog.V(logger.Detail).Infof("can't watch %s: %v", w.ac.keydir, err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer notify.Stop(w.ev)
|
defer notify.Stop(w.ev)
|
||||||
glog.V(logger.Detail).Infof("now watching %s", w.ac.keydir)
|
|
||||||
defer glog.V(logger.Detail).Infof("no longer watching %s", w.ac.keydir)
|
logger.Trace("Started watching keystore folder")
|
||||||
|
defer logger.Trace("Stopped watching keystore folder")
|
||||||
|
|
||||||
w.ac.mu.Lock()
|
w.ac.mu.Lock()
|
||||||
w.running = true
|
w.running = true
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,15 @@ func (u URL) String() string {
|
||||||
return u.Path
|
return u.Path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TerminalString implements the log.TerminalStringer interface.
|
||||||
|
func (u URL) TerminalString() string {
|
||||||
|
url := u.String()
|
||||||
|
if len(url) > 32 {
|
||||||
|
return url[:31] + "…"
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalJSON implements the json.Marshaller interface.
|
// MarshalJSON implements the json.Marshaller interface.
|
||||||
func (u URL) MarshalJSON() ([]byte, error) {
|
func (u URL) MarshalJSON() ([]byte, error) {
|
||||||
return json.Marshal(u.String())
|
return json.Marshal(u.String())
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,17 @@
|
||||||
// wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo:
|
// wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo:
|
||||||
// https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc
|
// https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc
|
||||||
|
|
||||||
// +build !ios
|
|
||||||
|
|
||||||
package usbwallet
|
package usbwallet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"errors"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/karalabe/gousb/usb"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/karalabe/hid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LedgerScheme is the protocol scheme prefixing account and wallet URLs.
|
// LedgerScheme is the protocol scheme prefixing account and wallet URLs.
|
||||||
|
|
@ -49,8 +48,6 @@ const ledgerRefreshThrottling = 500 * time.Millisecond
|
||||||
|
|
||||||
// LedgerHub is a accounts.Backend that can find and handle Ledger hardware wallets.
|
// LedgerHub is a accounts.Backend that can find and handle Ledger hardware wallets.
|
||||||
type LedgerHub struct {
|
type LedgerHub struct {
|
||||||
ctx *usb.Context // Context interfacing with a libusb instance
|
|
||||||
|
|
||||||
refreshed time.Time // Time instance when the list of wallets was last refreshed
|
refreshed time.Time // Time instance when the list of wallets was last refreshed
|
||||||
wallets []accounts.Wallet // List of Ledger devices currently tracking
|
wallets []accounts.Wallet // List of Ledger devices currently tracking
|
||||||
updateFeed event.Feed // Event feed to notify wallet additions/removals
|
updateFeed event.Feed // Event feed to notify wallet additions/removals
|
||||||
|
|
@ -63,18 +60,13 @@ type LedgerHub struct {
|
||||||
|
|
||||||
// NewLedgerHub creates a new hardware wallet manager for Ledger devices.
|
// NewLedgerHub creates a new hardware wallet manager for Ledger devices.
|
||||||
func NewLedgerHub() (*LedgerHub, error) {
|
func NewLedgerHub() (*LedgerHub, error) {
|
||||||
// Initialize the USB library to access Ledgers through
|
if !hid.Supported() {
|
||||||
ctx, err := usb.NewContext()
|
return nil, errors.New("unsupported platform")
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
// Create the USB hub, start and return it
|
|
||||||
hub := &LedgerHub{
|
hub := &LedgerHub{
|
||||||
ctx: ctx,
|
|
||||||
quit: make(chan chan error),
|
quit: make(chan chan error),
|
||||||
}
|
}
|
||||||
hub.refreshWallets()
|
hub.refreshWallets()
|
||||||
|
|
||||||
return hub, nil
|
return hub, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,31 +96,23 @@ func (hub *LedgerHub) refreshWallets() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Retrieve the current list of Ledger devices
|
// Retrieve the current list of Ledger devices
|
||||||
var devIDs []deviceID
|
var ledgers []hid.DeviceInfo
|
||||||
var busIDs []uint16
|
for _, info := range hid.Enumerate(0, 0) { // Can't enumerate directly, one valid ID is the 0 wildcard
|
||||||
|
|
||||||
hub.ctx.ListDevices(func(desc *usb.Descriptor) bool {
|
|
||||||
// Gather Ledger devices, don't connect any just yet
|
|
||||||
for _, id := range ledgerDeviceIDs {
|
for _, id := range ledgerDeviceIDs {
|
||||||
if desc.Vendor == id.Vendor && desc.Product == id.Product {
|
if info.VendorID == id.Vendor && info.ProductID == id.Product {
|
||||||
devIDs = append(devIDs, deviceID{Vendor: desc.Vendor, Product: desc.Product})
|
ledgers = append(ledgers, info)
|
||||||
busIDs = append(busIDs, uint16(desc.Bus)<<8+uint16(desc.Address))
|
break
|
||||||
return false
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Not ledger, ignore and don't connect either
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
// Transform the current list of wallets into the new one
|
// Transform the current list of wallets into the new one
|
||||||
hub.lock.Lock()
|
hub.lock.Lock()
|
||||||
|
|
||||||
wallets := make([]accounts.Wallet, 0, len(devIDs))
|
wallets := make([]accounts.Wallet, 0, len(ledgers))
|
||||||
events := []accounts.WalletEvent{}
|
events := []accounts.WalletEvent{}
|
||||||
|
|
||||||
for i := 0; i < len(devIDs); i++ {
|
for _, ledger := range ledgers {
|
||||||
devID, busID := devIDs[i], busIDs[i]
|
url := accounts.URL{Scheme: LedgerScheme, Path: ledger.Path}
|
||||||
|
|
||||||
url := accounts.URL{Scheme: LedgerScheme, Path: fmt.Sprintf("%03d:%03d", busID>>8, busID&0xff)}
|
|
||||||
|
|
||||||
// Drop wallets in front of the next device or those that failed for some reason
|
// Drop wallets in front of the next device or those that failed for some reason
|
||||||
for len(hub.wallets) > 0 && (hub.wallets[0].URL().Cmp(url) < 0 || hub.wallets[0].(*ledgerWallet).failed()) {
|
for len(hub.wallets) > 0 && (hub.wallets[0].URL().Cmp(url) < 0 || hub.wallets[0].(*ledgerWallet).failed()) {
|
||||||
|
|
@ -137,7 +121,7 @@ func (hub *LedgerHub) refreshWallets() {
|
||||||
}
|
}
|
||||||
// If there are no more wallets or the device is before the next, wrap new wallet
|
// If there are no more wallets or the device is before the next, wrap new wallet
|
||||||
if len(hub.wallets) == 0 || hub.wallets[0].URL().Cmp(url) > 0 {
|
if len(hub.wallets) == 0 || hub.wallets[0].URL().Cmp(url) > 0 {
|
||||||
wallet := &ledgerWallet{context: hub.ctx, hardwareID: devID, locationID: busID, url: &url}
|
wallet := &ledgerWallet{url: &url, info: ledger, log: log.New("url", url)}
|
||||||
|
|
||||||
events = append(events, accounts.WalletEvent{Wallet: wallet, Arrive: true})
|
events = append(events, accounts.WalletEvent{Wallet: wallet, Arrive: true})
|
||||||
wallets = append(wallets, wallet)
|
wallets = append(wallets, wallet)
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,10 @@
|
||||||
// wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo:
|
// wallets. The wire protocol spec can be found in the Ledger Blue GitHub repo:
|
||||||
// https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc
|
// https://raw.githubusercontent.com/LedgerHQ/blue-app-eth/master/doc/ethapp.asc
|
||||||
|
|
||||||
// +build !ios
|
|
||||||
|
|
||||||
package usbwallet
|
package usbwallet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
@ -35,12 +34,11 @@ import (
|
||||||
ethereum "github.com/ethereum/go-ethereum"
|
ethereum "github.com/ethereum/go-ethereum"
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/karalabe/gousb/usb"
|
"github.com/karalabe/hid"
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Maximum time between wallet health checks to detect USB unplugs.
|
// Maximum time between wallet health checks to detect USB unplugs.
|
||||||
|
|
@ -74,21 +72,21 @@ const (
|
||||||
ledgerP2ReturnAddressChainCode ledgerParam2 = 0x01 // Require a user confirmation before returning the address
|
ledgerP2ReturnAddressChainCode ledgerParam2 = 0x01 // Require a user confirmation before returning the address
|
||||||
)
|
)
|
||||||
|
|
||||||
// errReplyInvalidHeader is the error message returned by a Ledfer data exchange
|
// errReplyInvalidHeader is the error message returned by a Ledger data exchange
|
||||||
// if the device replies with a mismatching header. This usually means the device
|
// if the device replies with a mismatching header. This usually means the device
|
||||||
// is in browser mode.
|
// is in browser mode.
|
||||||
var errReplyInvalidHeader = errors.New("invalid reply header")
|
var errReplyInvalidHeader = errors.New("invalid reply header")
|
||||||
|
|
||||||
|
// errInvalidVersionReply is the error message returned by a Ledger version retrieval
|
||||||
|
// when a response does arrive, but it does not contain the expected data.
|
||||||
|
var errInvalidVersionReply = errors.New("invalid version reply")
|
||||||
|
|
||||||
// ledgerWallet represents a live USB Ledger hardware wallet.
|
// ledgerWallet represents a live USB Ledger hardware wallet.
|
||||||
type ledgerWallet struct {
|
type ledgerWallet struct {
|
||||||
context *usb.Context // USB context to interface libusb through
|
|
||||||
hardwareID deviceID // USB identifiers to identify this device type
|
|
||||||
locationID uint16 // USB bus and address to identify this device instance
|
|
||||||
url *accounts.URL // Textual URL uniquely identifying this wallet
|
url *accounts.URL // Textual URL uniquely identifying this wallet
|
||||||
|
|
||||||
device *usb.Device // USB device advertising itself as a Ledger wallet
|
info hid.DeviceInfo // Known USB device infos about the wallet
|
||||||
input usb.Endpoint // Input endpoint to send data to this device
|
device *hid.Device // USB device advertising itself as a Ledger wallet
|
||||||
output usb.Endpoint // Output endpoint to receive data from this device
|
|
||||||
failure error // Any failure that would make the device unusable
|
failure error // Any failure that would make the device unusable
|
||||||
|
|
||||||
version [3]byte // Current version of the Ledger Ethereum app (zero if app is offline)
|
version [3]byte // Current version of the Ledger Ethereum app (zero if app is offline)
|
||||||
|
|
@ -125,6 +123,8 @@ type ledgerWallet struct {
|
||||||
// must only ever hold a *read* lock to stateLock.
|
// must only ever hold a *read* lock to stateLock.
|
||||||
commsLock chan struct{} // Mutex (buf=1) for the USB comms without keeping the state locked
|
commsLock chan struct{} // Mutex (buf=1) for the USB comms without keeping the state locked
|
||||||
stateLock sync.RWMutex // Protects read and write access to the wallet struct fields
|
stateLock sync.RWMutex // Protects read and write access to the wallet struct fields
|
||||||
|
|
||||||
|
log log.Logger // Contextual logger to tag the ledger with its id
|
||||||
}
|
}
|
||||||
|
|
||||||
// URL implements accounts.Wallet, returning the URL of the Ledger device.
|
// URL implements accounts.Wallet, returning the URL of the Ledger device.
|
||||||
|
|
@ -183,59 +183,12 @@ func (w *ledgerWallet) Open(passphrase string) error {
|
||||||
return accounts.ErrWalletAlreadyOpen
|
return accounts.ErrWalletAlreadyOpen
|
||||||
}
|
}
|
||||||
// Otherwise iterate over all USB devices and find this again (no way to directly do this)
|
// Otherwise iterate over all USB devices and find this again (no way to directly do this)
|
||||||
// Iterate over all attached devices and fetch those seemingly Ledger
|
device, err := w.info.Open()
|
||||||
devices, err := w.context.ListDevices(func(desc *usb.Descriptor) bool {
|
|
||||||
// Only open this single specific device
|
|
||||||
return desc.Vendor == w.hardwareID.Vendor && desc.Product == w.hardwareID.Product &&
|
|
||||||
uint16(desc.Bus)<<8+uint16(desc.Address) == w.locationID
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(devices) == 0 {
|
|
||||||
return accounts.ErrUnknownWallet
|
|
||||||
}
|
|
||||||
// Device opened, attach to the input and output endpoints
|
|
||||||
device := devices[0]
|
|
||||||
|
|
||||||
var invalid string
|
|
||||||
switch {
|
|
||||||
case len(device.Descriptor.Configs) == 0:
|
|
||||||
invalid = "no endpoint config available"
|
|
||||||
case len(device.Descriptor.Configs[0].Interfaces) == 0:
|
|
||||||
invalid = "no endpoint interface available"
|
|
||||||
case len(device.Descriptor.Configs[0].Interfaces[0].Setups) == 0:
|
|
||||||
invalid = "no endpoint setup available"
|
|
||||||
case len(device.Descriptor.Configs[0].Interfaces[0].Setups[0].Endpoints) < 2:
|
|
||||||
invalid = "not enough IO endpoints available"
|
|
||||||
}
|
|
||||||
if invalid != "" {
|
|
||||||
device.Close()
|
|
||||||
return fmt.Errorf("ledger wallet [%s] invalid: %s", w.url, invalid)
|
|
||||||
}
|
|
||||||
// Open the input and output endpoints to the device
|
|
||||||
input, err := device.OpenEndpoint(
|
|
||||||
device.Descriptor.Configs[0].Config,
|
|
||||||
device.Descriptor.Configs[0].Interfaces[0].Number,
|
|
||||||
device.Descriptor.Configs[0].Interfaces[0].Setups[0].Number,
|
|
||||||
device.Descriptor.Configs[0].Interfaces[0].Setups[0].Endpoints[1].Address,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
device.Close()
|
|
||||||
return fmt.Errorf("ledger wallet [%s] input open failed: %v", w.url, err)
|
|
||||||
}
|
|
||||||
output, err := device.OpenEndpoint(
|
|
||||||
device.Descriptor.Configs[0].Config,
|
|
||||||
device.Descriptor.Configs[0].Interfaces[0].Number,
|
|
||||||
device.Descriptor.Configs[0].Interfaces[0].Setups[0].Number,
|
|
||||||
device.Descriptor.Configs[0].Interfaces[0].Setups[0].Endpoints[0].Address,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
device.Close()
|
|
||||||
return fmt.Errorf("ledger wallet [%s] output open failed: %v", w.url, err)
|
|
||||||
}
|
|
||||||
// Wallet seems to be successfully opened, guess if the Ethereum app is running
|
// Wallet seems to be successfully opened, guess if the Ethereum app is running
|
||||||
w.device, w.input, w.output = device, input, output
|
w.device = device
|
||||||
w.commsLock = make(chan struct{}, 1)
|
w.commsLock = make(chan struct{}, 1)
|
||||||
w.commsLock <- struct{}{} // Enable lock
|
w.commsLock <- struct{}{} // Enable lock
|
||||||
|
|
||||||
|
|
@ -269,8 +222,8 @@ func (w *ledgerWallet) Open(passphrase string) error {
|
||||||
// - libusb on Windows doesn't support hotplug, so we can't detect USB unplugs
|
// - libusb on Windows doesn't support hotplug, so we can't detect USB unplugs
|
||||||
// - communication timeout on the Ledger requires a device power cycle to fix
|
// - communication timeout on the Ledger requires a device power cycle to fix
|
||||||
func (w *ledgerWallet) heartbeat() {
|
func (w *ledgerWallet) heartbeat() {
|
||||||
glog.V(logger.Debug).Infof("%s health-check started", w.url.String())
|
w.log.Debug("Ledger health-check started")
|
||||||
defer glog.V(logger.Debug).Infof("%s health-check stopped", w.url.String())
|
defer w.log.Debug("Ledger health-check stopped")
|
||||||
|
|
||||||
// Execute heartbeat checks until termination or error
|
// Execute heartbeat checks until termination or error
|
||||||
var (
|
var (
|
||||||
|
|
@ -298,18 +251,18 @@ func (w *ledgerWallet) heartbeat() {
|
||||||
w.commsLock <- struct{}{}
|
w.commsLock <- struct{}{}
|
||||||
w.stateLock.RUnlock()
|
w.stateLock.RUnlock()
|
||||||
|
|
||||||
if err == usb.ERROR_IO || err == usb.ERROR_NO_DEVICE {
|
if err != nil && err != errInvalidVersionReply {
|
||||||
w.stateLock.Lock() // Lock state to tear the wallet down
|
w.stateLock.Lock() // Lock state to tear the wallet down
|
||||||
w.failure = err
|
w.failure = err
|
||||||
w.close()
|
w.close()
|
||||||
w.stateLock.Unlock()
|
w.stateLock.Unlock()
|
||||||
}
|
}
|
||||||
// Ignore uninteresting errors
|
// Ignore non hardware related errors
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
// In case of error, wait for termination
|
// In case of error, wait for termination
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Debug).Infof("%s health-check failed: %v", w.url.String(), err)
|
w.log.Debug("Ledger health-check failed", "err", err)
|
||||||
errc = <-w.healthQuit
|
errc = <-w.healthQuit
|
||||||
}
|
}
|
||||||
errc <- err
|
errc <- err
|
||||||
|
|
@ -363,13 +316,13 @@ func (w *ledgerWallet) close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Close the device, clear everything, then return
|
// Close the device, clear everything, then return
|
||||||
err := w.device.Close()
|
w.device.Close()
|
||||||
|
w.device = nil
|
||||||
|
|
||||||
w.device, w.input, w.output = nil, nil, nil
|
|
||||||
w.browser, w.version = false, [3]byte{}
|
w.browser, w.version = false, [3]byte{}
|
||||||
w.accounts, w.paths = nil, nil
|
w.accounts, w.paths = nil, nil
|
||||||
|
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accounts implements accounts.Wallet, returning the list of accounts pinned to
|
// Accounts implements accounts.Wallet, returning the list of accounts pinned to
|
||||||
|
|
@ -397,8 +350,8 @@ func (w *ledgerWallet) Accounts() []accounts.Account {
|
||||||
// selfDerive is an account derivation loop that upon request attempts to find
|
// selfDerive is an account derivation loop that upon request attempts to find
|
||||||
// new non-zero accounts.
|
// new non-zero accounts.
|
||||||
func (w *ledgerWallet) selfDerive() {
|
func (w *ledgerWallet) selfDerive() {
|
||||||
glog.V(logger.Debug).Infof("%s self-derivation started", w.url.String())
|
w.log.Debug("Ledger self-derivation started")
|
||||||
defer glog.V(logger.Debug).Infof("%s self-derivation stopped", w.url.String())
|
defer w.log.Debug("Ledger self-derivation stopped")
|
||||||
|
|
||||||
// Execute self-derivations until termination or error
|
// Execute self-derivations until termination or error
|
||||||
var (
|
var (
|
||||||
|
|
@ -443,7 +396,7 @@ func (w *ledgerWallet) selfDerive() {
|
||||||
// Retrieve the next derived Ethereum account
|
// Retrieve the next derived Ethereum account
|
||||||
if nextAddr == (common.Address{}) {
|
if nextAddr == (common.Address{}) {
|
||||||
if nextAddr, err = w.ledgerDerive(nextPath); err != nil {
|
if nextAddr, err = w.ledgerDerive(nextPath); err != nil {
|
||||||
glog.V(logger.Warn).Infof("%s self-derivation failed: %v", w.url.String(), err)
|
w.log.Warn("Ledger account derivation failed", "err", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -454,16 +407,16 @@ func (w *ledgerWallet) selfDerive() {
|
||||||
)
|
)
|
||||||
balance, err = w.deriveChain.BalanceAt(context, nextAddr, nil)
|
balance, err = w.deriveChain.BalanceAt(context, nextAddr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Warn).Infof("%s self-derivation balance retrieval failed: %v", w.url.String(), err)
|
w.log.Warn("Ledger balance retrieval failed", "err", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
nonce, err = w.deriveChain.NonceAt(context, nextAddr, nil)
|
nonce, err = w.deriveChain.NonceAt(context, nextAddr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Warn).Infof("%s self-derivation nonce retrieval failed: %v", w.url.String(), err)
|
w.log.Warn("Ledger nonce retrieval failed", "err", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// If the next account is empty, stop self-derivation, but add it nonetheless
|
// If the next account is empty, stop self-derivation, but add it nonetheless
|
||||||
if balance.BitLen() == 0 && nonce == 0 {
|
if balance.Sign() == 0 && nonce == 0 {
|
||||||
empty = true
|
empty = true
|
||||||
}
|
}
|
||||||
// We've just self-derived a new account, start tracking it locally
|
// We've just self-derived a new account, start tracking it locally
|
||||||
|
|
@ -479,7 +432,7 @@ func (w *ledgerWallet) selfDerive() {
|
||||||
|
|
||||||
// Display a log message to the user for new (or previously empty accounts)
|
// Display a log message to the user for new (or previously empty accounts)
|
||||||
if _, known := w.paths[nextAddr]; !known || (!empty && nextAddr == w.deriveNextAddr) {
|
if _, known := w.paths[nextAddr]; !known || (!empty && nextAddr == w.deriveNextAddr) {
|
||||||
glog.V(logger.Info).Infof("%s discovered %s (balance %22v, nonce %4d) at %s", w.url.String(), nextAddr.Hex(), balance, nonce, path)
|
w.log.Info("Ledger discovered new account", "address", nextAddr, "path", path, "balance", balance, "nonce", nonce)
|
||||||
}
|
}
|
||||||
// Fetch the next potential account
|
// Fetch the next potential account
|
||||||
if !empty {
|
if !empty {
|
||||||
|
|
@ -518,7 +471,7 @@ func (w *ledgerWallet) selfDerive() {
|
||||||
}
|
}
|
||||||
// In case of error, wait for termination
|
// In case of error, wait for termination
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Debug).Infof("%s self-derivation failed: %s", w.url.String(), err)
|
w.log.Debug("Ledger self-derivation failed", "err", err)
|
||||||
errc = <-w.deriveQuit
|
errc = <-w.deriveQuit
|
||||||
}
|
}
|
||||||
errc <- err
|
errc <- err
|
||||||
|
|
@ -664,7 +617,7 @@ func (w *ledgerWallet) ledgerVersion() ([3]byte, error) {
|
||||||
return [3]byte{}, err
|
return [3]byte{}, err
|
||||||
}
|
}
|
||||||
if len(reply) != 4 {
|
if len(reply) != 4 {
|
||||||
return [3]byte{}, errors.New("reply not of correct size")
|
return [3]byte{}, errInvalidVersionReply
|
||||||
}
|
}
|
||||||
// Cache the version for future reference
|
// Cache the version for future reference
|
||||||
var version [3]byte
|
var version [3]byte
|
||||||
|
|
@ -768,10 +721,6 @@ func (w *ledgerWallet) ledgerDerive(derivationPath []uint32) (common.Address, er
|
||||||
// signature R | 32 bytes
|
// signature R | 32 bytes
|
||||||
// signature S | 32 bytes
|
// signature S | 32 bytes
|
||||||
func (w *ledgerWallet) ledgerSign(derivationPath []uint32, address common.Address, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
func (w *ledgerWallet) ledgerSign(derivationPath []uint32, address common.Address, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||||
// We need to modify the timeouts to account for user feedback
|
|
||||||
defer func(old time.Duration) { w.device.ReadTimeout = old }(w.device.ReadTimeout)
|
|
||||||
w.device.ReadTimeout = time.Hour * 24 * 30 // Timeout requires a Ledger power cycle, only if you must
|
|
||||||
|
|
||||||
// Flatten the derivation path into the Ledger request
|
// Flatten the derivation path into the Ledger request
|
||||||
path := make([]byte, 1+4*len(derivationPath))
|
path := make([]byte, 1+4*len(derivationPath))
|
||||||
path[0] = byte(len(derivationPath))
|
path[0] = byte(len(derivationPath))
|
||||||
|
|
@ -902,10 +851,8 @@ func (w *ledgerWallet) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 l
|
||||||
apdu = nil
|
apdu = nil
|
||||||
}
|
}
|
||||||
// Send over to the device
|
// Send over to the device
|
||||||
if glog.V(logger.Detail) {
|
w.log.Trace("Data chunk sent to the Ledger", "chunk", hexutil.Bytes(chunk))
|
||||||
glog.Infof("-> %03d.%03d: %x", w.device.Bus, w.device.Address, chunk)
|
if _, err := w.device.Write(chunk); err != nil {
|
||||||
}
|
|
||||||
if _, err := w.input.Write(chunk); err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -914,12 +861,11 @@ func (w *ledgerWallet) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 l
|
||||||
chunk = chunk[:64] // Yeah, we surely have enough space
|
chunk = chunk[:64] // Yeah, we surely have enough space
|
||||||
for {
|
for {
|
||||||
// Read the next chunk from the Ledger wallet
|
// Read the next chunk from the Ledger wallet
|
||||||
if _, err := io.ReadFull(w.output, chunk); err != nil {
|
if _, err := io.ReadFull(w.device, chunk); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if glog.V(logger.Detail) {
|
w.log.Trace("Data chunk received from the Ledger", "chunk", hexutil.Bytes(chunk))
|
||||||
glog.Infof("<- %03d.%03d: %x", w.device.Bus, w.device.Address, chunk)
|
|
||||||
}
|
|
||||||
// Make sure the transport header matches
|
// Make sure the transport header matches
|
||||||
if chunk[0] != 0x01 || chunk[1] != 0x01 || chunk[2] != 0x05 {
|
if chunk[0] != 0x01 || chunk[1] != 0x01 || chunk[2] != 0x05 {
|
||||||
return nil, errReplyInvalidHeader
|
return nil, errReplyInvalidHeader
|
||||||
|
|
|
||||||
|
|
@ -14,16 +14,12 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// +build !ios
|
|
||||||
|
|
||||||
// Package usbwallet implements support for USB hardware wallets.
|
// Package usbwallet implements support for USB hardware wallets.
|
||||||
package usbwallet
|
package usbwallet
|
||||||
|
|
||||||
import "github.com/karalabe/gousb/usb"
|
|
||||||
|
|
||||||
// deviceID is a combined vendor/product identifier to uniquely identify a USB
|
// deviceID is a combined vendor/product identifier to uniquely identify a USB
|
||||||
// hardware device.
|
// hardware device.
|
||||||
type deviceID struct {
|
type deviceID struct {
|
||||||
Vendor usb.ID // The Vendor identifer
|
Vendor uint16 // The Vendor identifer
|
||||||
Product usb.ID // The Product identifier
|
Product uint16 // The Product identifier
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,8 @@ environment:
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- rmdir C:\go /s /q
|
- rmdir C:\go /s /q
|
||||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.7.4.windows-%GETH_ARCH%.zip
|
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.8.windows-%GETH_ARCH%.zip
|
||||||
- 7z x go1.7.4.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
|
- 7z x go1.8.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
|
||||||
- go version
|
- go version
|
||||||
- gcc --version
|
- gcc --version
|
||||||
|
|
||||||
|
|
@ -36,4 +36,4 @@ after_build:
|
||||||
|
|
||||||
test_script:
|
test_script:
|
||||||
- set CGO_ENABLED=1
|
- set CGO_ENABLED=1
|
||||||
- go run build\ci.go test -vet -coverage
|
- go run build\ci.go test -coverage
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are
|
|
||||||
met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following disclaimer
|
|
||||||
in the documentation and/or other materials provided with the
|
|
||||||
distribution.
|
|
||||||
* Neither the name of Google Inc. nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
@ -1,18 +1,3 @@
|
||||||
# Vendored Dependencies
|
|
||||||
|
|
||||||
Dependencies are almost all vendored in at the standard Go `/vendor` path. This allows
|
|
||||||
people to build go-ethereum using the standard toolchain without any particular package
|
|
||||||
manager. It also plays nicely with `go get`, not requiring external code to be relied on.
|
|
||||||
|
|
||||||
The one single dependent package missing from `vendor` is `golang.org/x/net/context`. As
|
|
||||||
this is a package exposed via public library APIs, it must not be vendored as dependent
|
|
||||||
code woulnd't be able to instantiate.
|
|
||||||
|
|
||||||
To allow reproducible builds of go-ethereum nonetheless that don't need network access
|
|
||||||
during build time to fetch `golang.org/x/net/context`, a version was copied into our repo
|
|
||||||
at the very specific `/build/_vendor` path, which is added automatically by all CI build
|
|
||||||
scripts and the makefile too.
|
|
||||||
|
|
||||||
# Debian Packaging
|
# Debian Packaging
|
||||||
|
|
||||||
Tagged releases and develop branch commits are available as installable Debian packages
|
Tagged releases and develop branch commits are available as installable Debian packages
|
||||||
|
|
|
||||||
81
build/ci.go
81
build/ci.go
|
|
@ -23,15 +23,15 @@ Usage: go run ci.go <command> <command flags/arguments>
|
||||||
|
|
||||||
Available commands are:
|
Available commands are:
|
||||||
|
|
||||||
install [-arch architecture] [ packages... ] -- builds packages and executables
|
install [ -arch architecture ] [ packages... ] -- builds packages and executables
|
||||||
test [ -coverage ] [ -vet ] [ -misspell ] [ packages... ] -- runs the tests
|
test [ -coverage ] [ -misspell ] [ packages... ] -- runs the tests
|
||||||
archive [-arch architecture] [ -type zip|tar ] [ -signer key-envvar ] [ -upload dest ] -- archives build artefacts
|
archive [ -arch architecture ] [ -type zip|tar ] [ -signer key-envvar ] [ -upload dest ] -- archives build artefacts
|
||||||
importkeys -- imports signing keys from env
|
importkeys -- imports signing keys from env
|
||||||
debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package
|
debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package
|
||||||
nsis -- creates a Windows NSIS installer
|
nsis -- creates a Windows NSIS installer
|
||||||
aar [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an Android archive
|
aar [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an Android archive
|
||||||
xcode [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an iOS XCode framework
|
xcode [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an iOS XCode framework
|
||||||
xgo [ options ] -- cross builds according to options
|
xgo [ -alltools ] [ options ] -- cross builds according to options
|
||||||
|
|
||||||
For all commands, -n prevents execution of external programs (dry run mode).
|
For all commands, -n prevents execution of external programs (dry run mode).
|
||||||
|
|
||||||
|
|
@ -70,6 +70,7 @@ var (
|
||||||
allToolsArchiveFiles = []string{
|
allToolsArchiveFiles = []string{
|
||||||
"COPYING",
|
"COPYING",
|
||||||
executablePath("abigen"),
|
executablePath("abigen"),
|
||||||
|
executablePath("bootnode"),
|
||||||
executablePath("evm"),
|
executablePath("evm"),
|
||||||
executablePath("geth"),
|
executablePath("geth"),
|
||||||
executablePath("swarm"),
|
executablePath("swarm"),
|
||||||
|
|
@ -82,6 +83,10 @@ var (
|
||||||
Name: "geth",
|
Name: "geth",
|
||||||
Description: "Ethereum CLI client.",
|
Description: "Ethereum CLI client.",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "bootnode",
|
||||||
|
Description: "Ethereum bootnode.",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "rlpdump",
|
Name: "rlpdump",
|
||||||
Description: "Developer utility tool that prints RLP structures.",
|
Description: "Developer utility tool that prints RLP structures.",
|
||||||
|
|
@ -157,9 +162,9 @@ func doInstall(cmdline []string) {
|
||||||
|
|
||||||
// Check Go version. People regularly open issues about compilation
|
// Check Go version. People regularly open issues about compilation
|
||||||
// failure with outdated Go. This should save them the trouble.
|
// failure with outdated Go. This should save them the trouble.
|
||||||
if runtime.Version() < "go1.4" && !strings.HasPrefix(runtime.Version(), "devel") {
|
if runtime.Version() < "go1.7" && !strings.HasPrefix(runtime.Version(), "devel") {
|
||||||
log.Println("You have Go version", runtime.Version())
|
log.Println("You have Go version", runtime.Version())
|
||||||
log.Println("go-ethereum requires at least Go version 1.4 and cannot")
|
log.Println("go-ethereum requires at least Go version 1.7 and cannot")
|
||||||
log.Println("be compiled with an earlier version. Please upgrade your Go installation.")
|
log.Println("be compiled with an earlier version. Please upgrade your Go installation.")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
@ -168,6 +173,8 @@ func doInstall(cmdline []string) {
|
||||||
if flag.NArg() > 0 {
|
if flag.NArg() > 0 {
|
||||||
packages = flag.Args()
|
packages = flag.Args()
|
||||||
}
|
}
|
||||||
|
packages = build.ExpandPackagesNoVendor(packages)
|
||||||
|
|
||||||
if *arch == "" || *arch == runtime.GOARCH {
|
if *arch == "" || *arch == runtime.GOARCH {
|
||||||
goinstall := goTool("install", buildFlags(env)...)
|
goinstall := goTool("install", buildFlags(env)...)
|
||||||
goinstall.Args = append(goinstall.Args, "-v")
|
goinstall.Args = append(goinstall.Args, "-v")
|
||||||
|
|
@ -210,20 +217,9 @@ func doInstall(cmdline []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildFlags(env build.Environment) (flags []string) {
|
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.
|
// Set gitCommit constant via link-time assignment.
|
||||||
if env.Commit != "" {
|
if env.Commit != "" {
|
||||||
flags = append(flags, "-ldflags", "-X main.gitCommit"+sep+env.Commit)
|
flags = append(flags, "-ldflags", "-X main.gitCommit="+env.Commit)
|
||||||
}
|
}
|
||||||
return flags
|
return flags
|
||||||
}
|
}
|
||||||
|
|
@ -244,10 +240,7 @@ func goToolArch(arch string, subcmd string, args ...string) *exec.Cmd {
|
||||||
cmd.Args = append(cmd.Args, []string{"-ldflags", "-extldflags -Wl,--allow-multiple-definition"}...)
|
cmd.Args = append(cmd.Args, []string{"-ldflags", "-extldflags -Wl,--allow-multiple-definition"}...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cmd.Env = []string{
|
cmd.Env = []string{"GOPATH=" + build.GOPATH()}
|
||||||
"GO15VENDOREXPERIMENT=1",
|
|
||||||
"GOPATH=" + build.GOPATH(),
|
|
||||||
}
|
|
||||||
if arch == "" || arch == runtime.GOARCH {
|
if arch == "" || arch == runtime.GOARCH {
|
||||||
cmd.Env = append(cmd.Env, "GOBIN="+GOBIN)
|
cmd.Env = append(cmd.Env, "GOBIN="+GOBIN)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -269,7 +262,6 @@ func goToolArch(arch string, subcmd string, args ...string) *exec.Cmd {
|
||||||
|
|
||||||
func doTest(cmdline []string) {
|
func doTest(cmdline []string) {
|
||||||
var (
|
var (
|
||||||
vet = flag.Bool("vet", false, "Whether to run go vet")
|
|
||||||
misspell = flag.Bool("misspell", false, "Whether to run the spell checker")
|
misspell = flag.Bool("misspell", false, "Whether to run the spell checker")
|
||||||
coverage = flag.Bool("coverage", false, "Whether to record code coverage")
|
coverage = flag.Bool("coverage", false, "Whether to record code coverage")
|
||||||
)
|
)
|
||||||
|
|
@ -279,23 +271,10 @@ func doTest(cmdline []string) {
|
||||||
if len(flag.CommandLine.Args()) > 0 {
|
if len(flag.CommandLine.Args()) > 0 {
|
||||||
packages = flag.CommandLine.Args()
|
packages = flag.CommandLine.Args()
|
||||||
}
|
}
|
||||||
if len(packages) == 1 && packages[0] == "./..." {
|
packages = build.ExpandPackagesNoVendor(packages)
|
||||||
// Resolve ./... manually since go vet will fail on vendored stuff
|
|
||||||
out, err := goTool("list", "./...").CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("package listing failed: %v\n%s", err, string(out))
|
|
||||||
}
|
|
||||||
packages = []string{}
|
|
||||||
for _, line := range strings.Split(string(out), "\n") {
|
|
||||||
if !strings.Contains(line, "vendor") {
|
|
||||||
packages = append(packages, strings.TrimSpace(line))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Run analysis tools before the tests.
|
// Run analysis tools before the tests.
|
||||||
if *vet {
|
|
||||||
build.MustRun(goTool("vet", packages...))
|
build.MustRun(goTool("vet", packages...))
|
||||||
}
|
|
||||||
if *misspell {
|
if *misspell {
|
||||||
spellcheck(packages)
|
spellcheck(packages)
|
||||||
}
|
}
|
||||||
|
|
@ -917,6 +896,9 @@ func newPodMetadata(env build.Environment, archive string) podMetadata {
|
||||||
// Cross compilation
|
// Cross compilation
|
||||||
|
|
||||||
func doXgo(cmdline []string) {
|
func doXgo(cmdline []string) {
|
||||||
|
var (
|
||||||
|
alltools = flag.Bool("alltools", false, `Flag whether we're building all known tools, or only on in particular`)
|
||||||
|
)
|
||||||
flag.CommandLine.Parse(cmdline)
|
flag.CommandLine.Parse(cmdline)
|
||||||
env := build.Env()
|
env := build.Env()
|
||||||
|
|
||||||
|
|
@ -924,8 +906,27 @@ func doXgo(cmdline []string) {
|
||||||
gogetxgo := goTool("get", "github.com/karalabe/xgo")
|
gogetxgo := goTool("get", "github.com/karalabe/xgo")
|
||||||
build.MustRun(gogetxgo)
|
build.MustRun(gogetxgo)
|
||||||
|
|
||||||
// Execute the actual cross compilation
|
// If all tools building is requested, build everything the builder wants
|
||||||
xgo := xgoTool(append(buildFlags(env), flag.Args()...))
|
args := append(buildFlags(env), flag.Args()...)
|
||||||
|
|
||||||
|
if *alltools {
|
||||||
|
args = append(args, []string{"--dest", GOBIN}...)
|
||||||
|
for _, res := range allToolsArchiveFiles {
|
||||||
|
if strings.HasPrefix(res, GOBIN) {
|
||||||
|
// Binary tool found, cross build it explicitly
|
||||||
|
args = append(args, "./"+filepath.Join("cmd", filepath.Base(res)))
|
||||||
|
xgo := xgoTool(args)
|
||||||
|
build.MustRun(xgo)
|
||||||
|
args = args[:len(args)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Otherwise xxecute the explicit cross compilation
|
||||||
|
path := args[len(args)-1]
|
||||||
|
args = append(args[:len(args)-1], []string{"--dest", GOBIN, path}...)
|
||||||
|
|
||||||
|
xgo := xgoTool(args)
|
||||||
build.MustRun(xgo)
|
build.MustRun(xgo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,7 @@ fi
|
||||||
|
|
||||||
# Set up the environment to use the workspace.
|
# Set up the environment to use the workspace.
|
||||||
GOPATH="$workspace"
|
GOPATH="$workspace"
|
||||||
GO15VENDOREXPERIMENT=1
|
export GOPATH
|
||||||
export GOPATH GO15VENDOREXPERIMENT
|
|
||||||
|
|
||||||
# Run the command inside the workspace.
|
# Run the command inside the workspace.
|
||||||
cd "$ethdir/go-ethereum"
|
cd "$ethdir/go-ethereum"
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ var (
|
||||||
// boring stuff
|
// boring stuff
|
||||||
"vendor/", "tests/files/", "build/",
|
"vendor/", "tests/files/", "build/",
|
||||||
// don't relicense vendored sources
|
// don't relicense vendored sources
|
||||||
"crypto/sha3/", "crypto/ecies/", "logger/glog/",
|
"crypto/sha3/", "crypto/ecies/", "log/",
|
||||||
"crypto/secp256k1/curve.go",
|
"crypto/secp256k1/curve.go",
|
||||||
// don't license generated files
|
// don't license generated files
|
||||||
"contracts/chequebook/contract/",
|
"contracts/chequebook/contract/",
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
|
|
@ -42,15 +42,19 @@ func main() {
|
||||||
natdesc = flag.String("nat", "none", "port mapping mechanism (any|none|upnp|pmp|extip:<IP>)")
|
natdesc = flag.String("nat", "none", "port mapping mechanism (any|none|upnp|pmp|extip:<IP>)")
|
||||||
netrestrict = flag.String("netrestrict", "", "restrict network communication to the given IP networks (CIDR masks)")
|
netrestrict = flag.String("netrestrict", "", "restrict network communication to the given IP networks (CIDR masks)")
|
||||||
runv5 = flag.Bool("v5", false, "run a v5 topic discovery bootnode")
|
runv5 = flag.Bool("v5", false, "run a v5 topic discovery bootnode")
|
||||||
|
verbosity = flag.Int("verbosity", int(log.LvlInfo), "log verbosity (0-9)")
|
||||||
|
vmodule = flag.String("vmodule", "", "log verbosity pattern")
|
||||||
|
|
||||||
nodeKey *ecdsa.PrivateKey
|
nodeKey *ecdsa.PrivateKey
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
flag.Var(glog.GetVerbosity(), "verbosity", "log verbosity (0-9)")
|
|
||||||
flag.Var(glog.GetVModule(), "vmodule", "log verbosity pattern")
|
|
||||||
glog.SetToStderr(true)
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
||||||
|
glogger.Verbosity(log.Lvl(*verbosity))
|
||||||
|
glogger.Vmodule(*vmodule)
|
||||||
|
log.Root().SetHandler(glogger)
|
||||||
|
|
||||||
natm, err := nat.Parse(*natdesc)
|
natm, err := nat.Parse(*natdesc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("-nat: %v", err)
|
utils.Fatalf("-nat: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of go-ethereum.
|
|
||||||
//
|
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// go-ethereum is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// disasm is a pretty-printer for EVM bytecode.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
code, err := ioutil.ReadAll(os.Stdin)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
code, err = hex.DecodeString(strings.TrimSpace(string(code[:])))
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Error: %v\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Printf("%x\n", code)
|
|
||||||
|
|
||||||
for pc := uint64(0); pc < uint64(len(code)); pc++ {
|
|
||||||
op := vm.OpCode(code[pc])
|
|
||||||
|
|
||||||
switch op {
|
|
||||||
case vm.PUSH1, vm.PUSH2, vm.PUSH3, vm.PUSH4, vm.PUSH5, vm.PUSH6, vm.PUSH7, vm.PUSH8, vm.PUSH9, vm.PUSH10, vm.PUSH11, vm.PUSH12, vm.PUSH13, vm.PUSH14, vm.PUSH15, vm.PUSH16, vm.PUSH17, vm.PUSH18, vm.PUSH19, vm.PUSH20, vm.PUSH21, vm.PUSH22, vm.PUSH23, vm.PUSH24, vm.PUSH25, vm.PUSH26, vm.PUSH27, vm.PUSH28, vm.PUSH29, vm.PUSH30, vm.PUSH31, vm.PUSH32:
|
|
||||||
a := uint64(op) - uint64(vm.PUSH1) + 1
|
|
||||||
u := pc + 1 + a
|
|
||||||
if uint64(len(code)) <= pc || uint64(len(code)) < u {
|
|
||||||
fmt.Printf("Error: incomplete push instruction at %v\n", pc)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fmt.Printf("%-5d %v => %x\n", pc, op, code[pc+1:u])
|
|
||||||
pc += a
|
|
||||||
default:
|
|
||||||
fmt.Printf("%-5d %v\n", pc, op)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,222 +0,0 @@
|
||||||
// Copyright 2014 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/>.
|
|
||||||
|
|
||||||
// ethtest executes Ethereum JSON tests.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
"github.com/ethereum/go-ethereum/tests"
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
continueOnError = false
|
|
||||||
testExtension = ".json"
|
|
||||||
defaultTest = "all"
|
|
||||||
defaultDir = "."
|
|
||||||
allTests = []string{"BlockTests", "StateTests", "TransactionTests", "VMTests", "RLPTests"}
|
|
||||||
testDirMapping = map[string]string{"BlockTests": "BlockchainTests"}
|
|
||||||
skipTests = []string{}
|
|
||||||
|
|
||||||
TestFlag = cli.StringFlag{
|
|
||||||
Name: "test",
|
|
||||||
Usage: "Test type (string): VMTests, TransactionTests, StateTests, BlockTests",
|
|
||||||
Value: defaultTest,
|
|
||||||
}
|
|
||||||
FileFlag = cli.StringFlag{
|
|
||||||
Name: "file",
|
|
||||||
Usage: "Test file or directory. Directories are searched for .json files 1 level deep",
|
|
||||||
Value: defaultDir,
|
|
||||||
EnvVar: "ETHEREUM_TEST_PATH",
|
|
||||||
}
|
|
||||||
ContinueOnErrorFlag = cli.BoolFlag{
|
|
||||||
Name: "continue",
|
|
||||||
Usage: "Continue running tests on error (true) or [default] exit immediately (false)",
|
|
||||||
}
|
|
||||||
ReadStdInFlag = cli.BoolFlag{
|
|
||||||
Name: "stdin",
|
|
||||||
Usage: "Accept input from stdin instead of reading from file",
|
|
||||||
}
|
|
||||||
SkipTestsFlag = cli.StringFlag{
|
|
||||||
Name: "skip",
|
|
||||||
Usage: "Tests names to skip",
|
|
||||||
}
|
|
||||||
TraceFlag = cli.BoolFlag{
|
|
||||||
Name: "trace",
|
|
||||||
Usage: "Enable VM tracing",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func runTestWithReader(test string, r io.Reader) error {
|
|
||||||
glog.Infoln("runTest", test)
|
|
||||||
var err error
|
|
||||||
switch strings.ToLower(test) {
|
|
||||||
case "bk", "block", "blocktest", "blockchaintest", "blocktests", "blockchaintests":
|
|
||||||
err = tests.RunBlockTestWithReader(params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, params.MainNetHomesteadGasRepriceBlock, r, skipTests)
|
|
||||||
case "st", "state", "statetest", "statetests":
|
|
||||||
rs := ¶ms.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock, DAOForkBlock: params.MainNetDAOForkBlock, DAOForkSupport: true, EIP150Block: params.MainNetHomesteadGasRepriceBlock}
|
|
||||||
err = tests.RunStateTestWithReader(rs, r, skipTests)
|
|
||||||
case "tx", "transactiontest", "transactiontests":
|
|
||||||
rs := ¶ms.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock, DAOForkBlock: params.MainNetDAOForkBlock, DAOForkSupport: true, EIP150Block: params.MainNetHomesteadGasRepriceBlock}
|
|
||||||
err = tests.RunTransactionTestsWithReader(rs, r, skipTests)
|
|
||||||
case "vm", "vmtest", "vmtests":
|
|
||||||
err = tests.RunVmTestWithReader(r, skipTests)
|
|
||||||
case "rlp", "rlptest", "rlptests":
|
|
||||||
err = tests.RunRLPTestWithReader(r, skipTests)
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf("Invalid test type specified: %v", test)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFiles(path string) ([]string, error) {
|
|
||||||
glog.Infoln("getFiles", path)
|
|
||||||
var files []string
|
|
||||||
f, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
fi, err := f.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch mode := fi.Mode(); {
|
|
||||||
case mode.IsDir():
|
|
||||||
fi, _ := ioutil.ReadDir(path)
|
|
||||||
files = make([]string, len(fi))
|
|
||||||
for i, v := range fi {
|
|
||||||
// only go 1 depth and leave directory entires blank
|
|
||||||
if !v.IsDir() && v.Name()[len(v.Name())-len(testExtension):len(v.Name())] == testExtension {
|
|
||||||
files[i] = filepath.Join(path, v.Name())
|
|
||||||
glog.Infoln("Found file", files[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case mode.IsRegular():
|
|
||||||
files = make([]string, 1)
|
|
||||||
files[0] = path
|
|
||||||
}
|
|
||||||
|
|
||||||
return files, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func runSuite(test, file string) {
|
|
||||||
var tests []string
|
|
||||||
|
|
||||||
if test == defaultTest {
|
|
||||||
tests = allTests
|
|
||||||
} else {
|
|
||||||
tests = []string{test}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, curTest := range tests {
|
|
||||||
glog.Infoln("runSuite", curTest, file)
|
|
||||||
var err error
|
|
||||||
var files []string
|
|
||||||
if test == defaultTest {
|
|
||||||
// check if we have an explicit directory mapping for the test
|
|
||||||
if _, ok := testDirMapping[curTest]; ok {
|
|
||||||
files, err = getFiles(filepath.Join(file, testDirMapping[curTest]))
|
|
||||||
} else {
|
|
||||||
// otherwise assume test name
|
|
||||||
files, err = getFiles(filepath.Join(file, curTest))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
files, err = getFiles(file)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
glog.Fatalln(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(files) == 0 {
|
|
||||||
glog.Warningln("No files matched path")
|
|
||||||
}
|
|
||||||
for _, curFile := range files {
|
|
||||||
// Skip blank entries
|
|
||||||
if len(curFile) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
r, err := os.Open(curFile)
|
|
||||||
if err != nil {
|
|
||||||
glog.Fatalln(err)
|
|
||||||
}
|
|
||||||
defer r.Close()
|
|
||||||
|
|
||||||
err = runTestWithReader(curTest, r)
|
|
||||||
if err != nil {
|
|
||||||
if continueOnError {
|
|
||||||
glog.Errorln(err)
|
|
||||||
} else {
|
|
||||||
glog.Fatalln(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupApp(c *cli.Context) error {
|
|
||||||
flagTest := c.GlobalString(TestFlag.Name)
|
|
||||||
flagFile := c.GlobalString(FileFlag.Name)
|
|
||||||
continueOnError = c.GlobalBool(ContinueOnErrorFlag.Name)
|
|
||||||
useStdIn := c.GlobalBool(ReadStdInFlag.Name)
|
|
||||||
skipTests = strings.Split(c.GlobalString(SkipTestsFlag.Name), " ")
|
|
||||||
|
|
||||||
if !useStdIn {
|
|
||||||
runSuite(flagTest, flagFile)
|
|
||||||
} else {
|
|
||||||
if err := runTestWithReader(flagTest, os.Stdin); err != nil {
|
|
||||||
glog.Fatalln(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
glog.SetToStderr(true)
|
|
||||||
|
|
||||||
app := cli.NewApp()
|
|
||||||
app.Name = "ethtest"
|
|
||||||
app.Usage = "go-ethereum test interface"
|
|
||||||
app.Action = setupApp
|
|
||||||
app.Version = "0.2.0"
|
|
||||||
app.Author = "go-ethereum team"
|
|
||||||
|
|
||||||
app.Flags = []cli.Flag{
|
|
||||||
TestFlag,
|
|
||||||
FileFlag,
|
|
||||||
ContinueOnErrorFlag,
|
|
||||||
ReadStdInFlag,
|
|
||||||
SkipTestsFlag,
|
|
||||||
TraceFlag,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := app.Run(os.Args); err != nil {
|
|
||||||
glog.Fatalln(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
55
cmd/evm/compiler.go
Normal file
55
cmd/evm/compiler.go
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
// Copyright 2017 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 (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/evm/internal/compiler"
|
||||||
|
|
||||||
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var compileCommand = cli.Command{
|
||||||
|
Action: compileCmd,
|
||||||
|
Name: "compile",
|
||||||
|
Usage: "compiles easm source to evm binary",
|
||||||
|
ArgsUsage: "<file>",
|
||||||
|
}
|
||||||
|
|
||||||
|
func compileCmd(ctx *cli.Context) error {
|
||||||
|
debug := ctx.GlobalBool(DebugFlag.Name)
|
||||||
|
|
||||||
|
if len(ctx.Args().First()) == 0 {
|
||||||
|
return errors.New("filename required")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn := ctx.Args().First()
|
||||||
|
src, err := ioutil.ReadFile(fn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bin, err := compiler.Compile(fn, src, debug)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(bin)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
53
cmd/evm/disasm.go
Normal file
53
cmd/evm/disasm.go
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
// Copyright 2017 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 (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/asm"
|
||||||
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var disasmCommand = cli.Command{
|
||||||
|
Action: disasmCmd,
|
||||||
|
Name: "disasm",
|
||||||
|
Usage: "disassembles evm binary",
|
||||||
|
ArgsUsage: "<file>",
|
||||||
|
}
|
||||||
|
|
||||||
|
func disasmCmd(ctx *cli.Context) error {
|
||||||
|
if len(ctx.Args().First()) == 0 {
|
||||||
|
return errors.New("filename required")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn := ctx.Args().First()
|
||||||
|
in, err := ioutil.ReadFile(fn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
code := strings.TrimSpace(string(in[:]))
|
||||||
|
fmt.Printf("%v\n", code)
|
||||||
|
if err = asm.PrintDisassembled(code); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
39
cmd/evm/internal/compiler/compiler.go
Normal file
39
cmd/evm/internal/compiler/compiler.go
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
// Copyright 2017 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 compiler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/asm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Compile(fn string, src []byte, debug bool) (string, error) {
|
||||||
|
compiler := asm.NewCompiler(debug)
|
||||||
|
compiler.Feed(asm.Lex(fn, src, debug))
|
||||||
|
|
||||||
|
bin, compileErrors := compiler.Compile()
|
||||||
|
if len(compileErrors) > 0 {
|
||||||
|
// report errors
|
||||||
|
for _, err := range compileErrors {
|
||||||
|
fmt.Printf("%s:%v\n", fn, err)
|
||||||
|
}
|
||||||
|
return "", errors.New("compiling failed")
|
||||||
|
}
|
||||||
|
return bin, nil
|
||||||
|
}
|
||||||
132
cmd/evm/main.go
132
cmd/evm/main.go
|
|
@ -19,19 +19,10 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
goruntime "runtime"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm/runtime"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -52,20 +43,20 @@ var (
|
||||||
Name: "codefile",
|
Name: "codefile",
|
||||||
Usage: "file containing EVM code",
|
Usage: "file containing EVM code",
|
||||||
}
|
}
|
||||||
GasFlag = cli.StringFlag{
|
GasFlag = cli.Uint64Flag{
|
||||||
Name: "gas",
|
Name: "gas",
|
||||||
Usage: "gas limit for the evm",
|
Usage: "gas limit for the evm",
|
||||||
Value: "10000000000",
|
Value: 10000000000,
|
||||||
}
|
}
|
||||||
PriceFlag = cli.StringFlag{
|
PriceFlag = utils.BigFlag{
|
||||||
Name: "price",
|
Name: "price",
|
||||||
Usage: "price set for the evm",
|
Usage: "price set for the evm",
|
||||||
Value: "0",
|
Value: new(big.Int),
|
||||||
}
|
}
|
||||||
ValueFlag = cli.StringFlag{
|
ValueFlag = utils.BigFlag{
|
||||||
Name: "value",
|
Name: "value",
|
||||||
Usage: "value set for the evm",
|
Usage: "value set for the evm",
|
||||||
Value: "0",
|
Value: new(big.Int),
|
||||||
}
|
}
|
||||||
DumpFlag = cli.BoolFlag{
|
DumpFlag = cli.BoolFlag{
|
||||||
Name: "dump",
|
Name: "dump",
|
||||||
|
|
@ -75,10 +66,6 @@ var (
|
||||||
Name: "input",
|
Name: "input",
|
||||||
Usage: "input for the EVM",
|
Usage: "input for the EVM",
|
||||||
}
|
}
|
||||||
SysStatFlag = cli.BoolFlag{
|
|
||||||
Name: "sysstat",
|
|
||||||
Usage: "display system stats",
|
|
||||||
}
|
|
||||||
VerbosityFlag = cli.IntFlag{
|
VerbosityFlag = cli.IntFlag{
|
||||||
Name: "verbosity",
|
Name: "verbosity",
|
||||||
Usage: "sets the verbosity level",
|
Usage: "sets the verbosity level",
|
||||||
|
|
@ -98,7 +85,6 @@ func init() {
|
||||||
CreateFlag,
|
CreateFlag,
|
||||||
DebugFlag,
|
DebugFlag,
|
||||||
VerbosityFlag,
|
VerbosityFlag,
|
||||||
SysStatFlag,
|
|
||||||
CodeFlag,
|
CodeFlag,
|
||||||
CodeFileFlag,
|
CodeFileFlag,
|
||||||
GasFlag,
|
GasFlag,
|
||||||
|
|
@ -108,107 +94,11 @@ func init() {
|
||||||
InputFlag,
|
InputFlag,
|
||||||
DisableGasMeteringFlag,
|
DisableGasMeteringFlag,
|
||||||
}
|
}
|
||||||
app.Action = run
|
app.Commands = []cli.Command{
|
||||||
}
|
compileCommand,
|
||||||
|
disasmCommand,
|
||||||
func run(ctx *cli.Context) error {
|
runCommand,
|
||||||
glog.SetToStderr(true)
|
|
||||||
glog.SetV(ctx.GlobalInt(VerbosityFlag.Name))
|
|
||||||
|
|
||||||
db, _ := ethdb.NewMemDatabase()
|
|
||||||
statedb, _ := state.New(common.Hash{}, db)
|
|
||||||
sender := statedb.CreateAccount(common.StringToAddress("sender"))
|
|
||||||
|
|
||||||
logger := vm.NewStructLogger(nil)
|
|
||||||
|
|
||||||
tstart := time.Now()
|
|
||||||
|
|
||||||
var (
|
|
||||||
code []byte
|
|
||||||
ret []byte
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
if ctx.GlobalString(CodeFlag.Name) != "" {
|
|
||||||
code = common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name))
|
|
||||||
} else {
|
|
||||||
var hexcode []byte
|
|
||||||
if ctx.GlobalString(CodeFileFlag.Name) != "" {
|
|
||||||
var err error
|
|
||||||
hexcode, err = ioutil.ReadFile(ctx.GlobalString(CodeFileFlag.Name))
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Could not load code from file: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
var err error
|
|
||||||
hexcode, err = ioutil.ReadAll(os.Stdin)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Could not load code from stdin: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
code = common.Hex2Bytes(string(hexcode[:]))
|
|
||||||
}
|
|
||||||
|
|
||||||
if ctx.GlobalBool(CreateFlag.Name) {
|
|
||||||
input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...)
|
|
||||||
ret, _, err = runtime.Create(input, &runtime.Config{
|
|
||||||
Origin: sender.Address(),
|
|
||||||
State: statedb,
|
|
||||||
GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)),
|
|
||||||
GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
|
|
||||||
Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
|
|
||||||
EVMConfig: vm.Config{
|
|
||||||
Tracer: logger,
|
|
||||||
Debug: ctx.GlobalBool(DebugFlag.Name),
|
|
||||||
DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
|
|
||||||
receiver.SetCode(crypto.Keccak256Hash(code), code)
|
|
||||||
|
|
||||||
ret, err = runtime.Call(receiver.Address(), common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtime.Config{
|
|
||||||
Origin: sender.Address(),
|
|
||||||
State: statedb,
|
|
||||||
GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)),
|
|
||||||
GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
|
|
||||||
Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
|
|
||||||
EVMConfig: vm.Config{
|
|
||||||
Tracer: logger,
|
|
||||||
Debug: ctx.GlobalBool(DebugFlag.Name),
|
|
||||||
DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
vmdone := time.Since(tstart)
|
|
||||||
|
|
||||||
if ctx.GlobalBool(DumpFlag.Name) {
|
|
||||||
statedb.Commit(true)
|
|
||||||
fmt.Println(string(statedb.Dump()))
|
|
||||||
}
|
|
||||||
vm.StdErrFormat(logger.StructLogs())
|
|
||||||
|
|
||||||
if ctx.GlobalBool(SysStatFlag.Name) {
|
|
||||||
var mem goruntime.MemStats
|
|
||||||
goruntime.ReadMemStats(&mem)
|
|
||||||
fmt.Printf("vm took %v\n", vmdone)
|
|
||||||
fmt.Printf(`alloc: %d
|
|
||||||
tot alloc: %d
|
|
||||||
no. malloc: %d
|
|
||||||
heap alloc: %d
|
|
||||||
heap objs: %d
|
|
||||||
num gc: %d
|
|
||||||
`, mem.Alloc, mem.TotalAlloc, mem.Mallocs, mem.HeapAlloc, mem.HeapObjects, mem.NumGC)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("OUT: 0x%x", ret)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf(" error: %v", err)
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
151
cmd/evm/runner.go
Normal file
151
cmd/evm/runner.go
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
// Copyright 2017 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 (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
goruntime "runtime"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/evm/internal/compiler"
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm/runtime"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var runCommand = cli.Command{
|
||||||
|
Action: runCmd,
|
||||||
|
Name: "run",
|
||||||
|
Usage: "run arbitrary evm binary",
|
||||||
|
ArgsUsage: "<code>",
|
||||||
|
Description: `The run command runs arbitrary EVM code.`,
|
||||||
|
}
|
||||||
|
|
||||||
|
func runCmd(ctx *cli.Context) error {
|
||||||
|
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
||||||
|
glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
|
||||||
|
log.Root().SetHandler(glogger)
|
||||||
|
|
||||||
|
var (
|
||||||
|
db, _ = ethdb.NewMemDatabase()
|
||||||
|
statedb, _ = state.New(common.Hash{}, db)
|
||||||
|
sender = common.StringToAddress("sender")
|
||||||
|
logger = vm.NewStructLogger(nil)
|
||||||
|
)
|
||||||
|
statedb.CreateAccount(sender)
|
||||||
|
|
||||||
|
var (
|
||||||
|
code []byte
|
||||||
|
ret []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if fn := ctx.Args().First(); len(fn) > 0 {
|
||||||
|
src, err := ioutil.ReadFile(fn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
bin, err := compiler.Compile(fn, src, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
code = common.Hex2Bytes(bin)
|
||||||
|
} else if ctx.GlobalString(CodeFlag.Name) != "" {
|
||||||
|
code = common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name))
|
||||||
|
} else {
|
||||||
|
var hexcode []byte
|
||||||
|
if ctx.GlobalString(CodeFileFlag.Name) != "" {
|
||||||
|
var err error
|
||||||
|
hexcode, err = ioutil.ReadFile(ctx.GlobalString(CodeFileFlag.Name))
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Could not load code from file: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
hexcode, err = ioutil.ReadAll(os.Stdin)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Could not load code from stdin: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
code = common.Hex2Bytes(string(bytes.TrimRight(hexcode, "\n")))
|
||||||
|
}
|
||||||
|
|
||||||
|
runtimeConfig := runtime.Config{
|
||||||
|
Origin: sender,
|
||||||
|
State: statedb,
|
||||||
|
GasLimit: ctx.GlobalUint64(GasFlag.Name),
|
||||||
|
GasPrice: utils.GlobalBig(ctx, PriceFlag.Name),
|
||||||
|
Value: utils.GlobalBig(ctx, ValueFlag.Name),
|
||||||
|
EVMConfig: vm.Config{
|
||||||
|
Tracer: logger,
|
||||||
|
Debug: ctx.GlobalBool(DebugFlag.Name),
|
||||||
|
DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tstart := time.Now()
|
||||||
|
if ctx.GlobalBool(CreateFlag.Name) {
|
||||||
|
input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...)
|
||||||
|
ret, _, err = runtime.Create(input, &runtimeConfig)
|
||||||
|
} else {
|
||||||
|
receiver := common.StringToAddress("receiver")
|
||||||
|
statedb.SetCode(receiver, code)
|
||||||
|
|
||||||
|
ret, err = runtime.Call(receiver, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtimeConfig)
|
||||||
|
}
|
||||||
|
execTime := time.Since(tstart)
|
||||||
|
|
||||||
|
if ctx.GlobalBool(DumpFlag.Name) {
|
||||||
|
statedb.Commit(true)
|
||||||
|
fmt.Println(string(statedb.Dump()))
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.GlobalBool(DebugFlag.Name) {
|
||||||
|
fmt.Fprintln(os.Stderr, "#### TRACE ####")
|
||||||
|
vm.WriteTrace(os.Stderr, logger.StructLogs())
|
||||||
|
fmt.Fprintln(os.Stderr, "#### LOGS ####")
|
||||||
|
vm.WriteLogs(os.Stderr, statedb.Logs())
|
||||||
|
|
||||||
|
var mem goruntime.MemStats
|
||||||
|
goruntime.ReadMemStats(&mem)
|
||||||
|
fmt.Fprintf(os.Stderr, `evm execution time: %v
|
||||||
|
heap objects: %d
|
||||||
|
allocations: %d
|
||||||
|
total allocations: %d
|
||||||
|
GC calls: %d
|
||||||
|
|
||||||
|
`, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("0x%x", ret)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" error: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Println()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -25,8 +25,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/console"
|
"github.com/ethereum/go-ethereum/console"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -203,11 +202,11 @@ func unlockAccount(ctx *cli.Context, ks *keystore.KeyStore, address string, i in
|
||||||
password := getPassPhrase(prompt, false, i, passwords)
|
password := getPassPhrase(prompt, false, i, passwords)
|
||||||
err = ks.Unlock(account, password)
|
err = ks.Unlock(account, password)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
|
log.Info("Unlocked account", "address", account.Address.Hex())
|
||||||
return account, password
|
return account, password
|
||||||
}
|
}
|
||||||
if err, ok := err.(*keystore.AmbiguousAddrError); ok {
|
if err, ok := err.(*keystore.AmbiguousAddrError); ok {
|
||||||
glog.V(logger.Info).Infof("Unlocked account %x", account.Address)
|
log.Info("Unlocked account", "address", account.Address.Hex())
|
||||||
return ambiguousAddrRecovery(ks, err, password), password
|
return ambiguousAddrRecovery(ks, err, password), password
|
||||||
}
|
}
|
||||||
if err != keystore.ErrDecrypt {
|
if err != keystore.ErrDecrypt {
|
||||||
|
|
@ -217,6 +216,7 @@ func unlockAccount(ctx *cli.Context, ks *keystore.KeyStore, address string, i in
|
||||||
}
|
}
|
||||||
// All trials expended to unlock account, bail out
|
// All trials expended to unlock account, bail out
|
||||||
utils.Fatalf("Failed to unlock account %s (%v)", address, err)
|
utils.Fatalf("Failed to unlock account %s (%v)", address, err)
|
||||||
|
|
||||||
return accounts.Account{}, ""
|
return accounts.Account{}, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,8 @@ Passphrase: {{.InputLine "foobar"}}
|
||||||
geth.expectExit()
|
geth.expectExit()
|
||||||
|
|
||||||
wantMessages := []string{
|
wantMessages := []string{
|
||||||
"Unlocked account f466859ead1932d743d622cb74fc058882e8648a",
|
"Unlocked account",
|
||||||
|
"=0xf466859ead1932d743d622cb74fc058882e8648a",
|
||||||
}
|
}
|
||||||
for _, m := range wantMessages {
|
for _, m := range wantMessages {
|
||||||
if !strings.Contains(geth.stderrText(), m) {
|
if !strings.Contains(geth.stderrText(), m) {
|
||||||
|
|
@ -189,8 +190,9 @@ Passphrase: {{.InputLine "foobar"}}
|
||||||
geth.expectExit()
|
geth.expectExit()
|
||||||
|
|
||||||
wantMessages := []string{
|
wantMessages := []string{
|
||||||
"Unlocked account 7ef5a6135f1fd6a02593eedc869c6d41d934aef8",
|
"Unlocked account",
|
||||||
"Unlocked account 289d485d9771714cce91d3393d764e1311907acc",
|
"=0x7ef5a6135f1fd6a02593eedc869c6d41d934aef8",
|
||||||
|
"=0x289d485d9771714cce91d3393d764e1311907acc",
|
||||||
}
|
}
|
||||||
for _, m := range wantMessages {
|
for _, m := range wantMessages {
|
||||||
if !strings.Contains(geth.stderrText(), m) {
|
if !strings.Contains(geth.stderrText(), m) {
|
||||||
|
|
@ -208,8 +210,9 @@ func TestUnlockFlagPasswordFile(t *testing.T) {
|
||||||
geth.expectExit()
|
geth.expectExit()
|
||||||
|
|
||||||
wantMessages := []string{
|
wantMessages := []string{
|
||||||
"Unlocked account 7ef5a6135f1fd6a02593eedc869c6d41d934aef8",
|
"Unlocked account",
|
||||||
"Unlocked account 289d485d9771714cce91d3393d764e1311907acc",
|
"=0x7ef5a6135f1fd6a02593eedc869c6d41d934aef8",
|
||||||
|
"=0x289d485d9771714cce91d3393d764e1311907acc",
|
||||||
}
|
}
|
||||||
for _, m := range wantMessages {
|
for _, m := range wantMessages {
|
||||||
if !strings.Contains(geth.stderrText(), m) {
|
if !strings.Contains(geth.stderrText(), m) {
|
||||||
|
|
@ -257,7 +260,8 @@ In order to avoid this warning, you need to remove the following duplicate key f
|
||||||
geth.expectExit()
|
geth.expectExit()
|
||||||
|
|
||||||
wantMessages := []string{
|
wantMessages := []string{
|
||||||
"Unlocked account f466859ead1932d743d622cb74fc058882e8648a",
|
"Unlocked account",
|
||||||
|
"=0xf466859ead1932d743d622cb74fc058882e8648a",
|
||||||
}
|
}
|
||||||
for _, m := range wantMessages {
|
for _, m := range wantMessages {
|
||||||
if !strings.Contains(geth.stderrText(), m) {
|
if !strings.Contains(geth.stderrText(), m) {
|
||||||
|
|
|
||||||
108
cmd/geth/bugcmd.go
Normal file
108
cmd/geth/bugcmd.go
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
// Copyright 2017 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 (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/url"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/internal/browser"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
||||||
|
cli "gopkg.in/urfave/cli.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bugCommand = cli.Command{
|
||||||
|
Action: reportBug,
|
||||||
|
Name: "bug",
|
||||||
|
Usage: "opens a window to report a bug on the geth repo",
|
||||||
|
ArgsUsage: " ",
|
||||||
|
Category: "MISCELLANEOUS COMMANDS",
|
||||||
|
}
|
||||||
|
|
||||||
|
const issueUrl = "https://github.com/ethereum/go-ethereum/issues/new"
|
||||||
|
|
||||||
|
// reportBug reports a bug by opening a new URL to the go-ethereum GH issue
|
||||||
|
// tracker and setting default values as the issue body.
|
||||||
|
func reportBug(ctx *cli.Context) error {
|
||||||
|
// execute template and write contents to buff
|
||||||
|
var buff bytes.Buffer
|
||||||
|
|
||||||
|
fmt.Fprintln(&buff, header)
|
||||||
|
fmt.Fprintln(&buff, "Version:", params.Version)
|
||||||
|
fmt.Fprintln(&buff, "Go Version:", runtime.Version())
|
||||||
|
fmt.Fprintln(&buff, "OS:", runtime.GOOS)
|
||||||
|
printOSDetails(&buff)
|
||||||
|
|
||||||
|
// open a new GH issue
|
||||||
|
if !browser.Open(issueUrl + "?body=" + url.QueryEscape(buff.String())) {
|
||||||
|
fmt.Printf("Please file a new issue at %s using this template:\n%s", issueUrl, buff.String())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// copied from the Go source. Copyright 2017 The Go Authors
|
||||||
|
func printOSDetails(w io.Writer) {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
printCmdOut(w, "uname -v: ", "uname", "-v")
|
||||||
|
printCmdOut(w, "", "sw_vers")
|
||||||
|
case "linux":
|
||||||
|
printCmdOut(w, "uname -sr: ", "uname", "-sr")
|
||||||
|
printCmdOut(w, "", "lsb_release", "-a")
|
||||||
|
case "openbsd", "netbsd", "freebsd", "dragonfly":
|
||||||
|
printCmdOut(w, "uname -v: ", "uname", "-v")
|
||||||
|
case "solaris":
|
||||||
|
out, err := ioutil.ReadFile("/etc/release")
|
||||||
|
if err == nil {
|
||||||
|
fmt.Fprintf(w, "/etc/release: %s\n", out)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("failed to read /etc/release: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// printCmdOut prints the output of running the given command.
|
||||||
|
// It ignores failures; 'go bug' is best effort.
|
||||||
|
//
|
||||||
|
// copied from the Go source. Copyright 2017 The Go Authors
|
||||||
|
func printCmdOut(w io.Writer, prefix, path string, args ...string) {
|
||||||
|
cmd := exec.Command(path, args...)
|
||||||
|
out, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%s %s: %v\n", path, strings.Join(args, " "), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "%s%s\n", prefix, bytes.TrimSpace(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
const header = `Please answer these questions before submitting your issue. Thanks!
|
||||||
|
|
||||||
|
#### What did you do?
|
||||||
|
|
||||||
|
#### What did you expect to see?
|
||||||
|
|
||||||
|
#### What did you see instead?
|
||||||
|
|
||||||
|
#### System details
|
||||||
|
`
|
||||||
|
|
@ -17,9 +17,9 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
@ -32,8 +32,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/syndtr/goleveldb/leveldb/util"
|
"github.com/syndtr/goleveldb/leveldb/util"
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
|
|
@ -56,10 +55,13 @@ participating.
|
||||||
Action: importChain,
|
Action: importChain,
|
||||||
Name: "import",
|
Name: "import",
|
||||||
Usage: "Import a blockchain file",
|
Usage: "Import a blockchain file",
|
||||||
ArgsUsage: "<filename>",
|
ArgsUsage: "<filename> (<filename 2> ... <filename N>) ",
|
||||||
Category: "BLOCKCHAIN COMMANDS",
|
Category: "BLOCKCHAIN COMMANDS",
|
||||||
Description: `
|
Description: `
|
||||||
TODO: Please write this
|
The import command imports blocks from an RLP-encoded form. The form can be one file
|
||||||
|
with several RLP-encoded blocks, or several files can be used.
|
||||||
|
If only one file is used, import error will result in failure. If several files are used,
|
||||||
|
processing will proceed even if an individual RLP-file import failure occurs.
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
exportCommand = cli.Command{
|
exportCommand = cli.Command{
|
||||||
|
|
@ -73,16 +75,6 @@ Requires a first argument of the file to write to.
|
||||||
Optional second and third arguments control the first and
|
Optional second and third arguments control the first and
|
||||||
last block to write. In this mode, the file will be appended
|
last block to write. In this mode, the file will be appended
|
||||||
if already existing.
|
if already existing.
|
||||||
`,
|
|
||||||
}
|
|
||||||
upgradedbCommand = cli.Command{
|
|
||||||
Action: upgradeDB,
|
|
||||||
Name: "upgradedb",
|
|
||||||
Usage: "Upgrade chainblock database",
|
|
||||||
ArgsUsage: " ",
|
|
||||||
Category: "BLOCKCHAIN COMMANDS",
|
|
||||||
Description: `
|
|
||||||
TODO: Please write this
|
|
||||||
`,
|
`,
|
||||||
}
|
}
|
||||||
removedbCommand = cli.Command{
|
removedbCommand = cli.Command{
|
||||||
|
|
@ -119,22 +111,27 @@ func initGenesis(ctx *cli.Context) error {
|
||||||
stack := makeFullNode(ctx)
|
stack := makeFullNode(ctx)
|
||||||
chaindb := utils.MakeChainDatabase(ctx, stack)
|
chaindb := utils.MakeChainDatabase(ctx, stack)
|
||||||
|
|
||||||
genesisFile, err := os.Open(genesisPath)
|
file, err := os.Open(genesisPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("failed to read genesis file: %v", err)
|
utils.Fatalf("failed to read genesis file: %v", err)
|
||||||
}
|
}
|
||||||
defer genesisFile.Close()
|
defer file.Close()
|
||||||
|
|
||||||
block, err := core.WriteGenesisBlock(chaindb, genesisFile)
|
genesis := new(core.Genesis)
|
||||||
|
if err := json.NewDecoder(file).Decode(genesis); err != nil {
|
||||||
|
utils.Fatalf("invalid genesis file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, hash, err := core.SetupGenesisBlock(chaindb, genesis)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("failed to write genesis block: %v", err)
|
utils.Fatalf("failed to write genesis block: %v", err)
|
||||||
}
|
}
|
||||||
glog.V(logger.Info).Infof("successfully wrote genesis block and/or chain rule set: %x", block.Hash())
|
log.Info("Successfully wrote genesis state", "hash", hash)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func importChain(ctx *cli.Context) error {
|
func importChain(ctx *cli.Context) error {
|
||||||
if len(ctx.Args()) != 1 {
|
if len(ctx.Args()) < 1 {
|
||||||
utils.Fatalf("This command requires an argument.")
|
utils.Fatalf("This command requires an argument.")
|
||||||
}
|
}
|
||||||
stack := makeFullNode(ctx)
|
stack := makeFullNode(ctx)
|
||||||
|
|
@ -158,9 +155,19 @@ func importChain(ctx *cli.Context) error {
|
||||||
}()
|
}()
|
||||||
// Import the chain
|
// Import the chain
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
|
if len(ctx.Args()) == 1 {
|
||||||
if err := utils.ImportChain(chain, ctx.Args().First()); err != nil {
|
if err := utils.ImportChain(chain, ctx.Args().First()); err != nil {
|
||||||
utils.Fatalf("Import error: %v", err)
|
utils.Fatalf("Import error: %v", err)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
for _, arg := range ctx.Args() {
|
||||||
|
if err := utils.ImportChain(chain, arg); err != nil {
|
||||||
|
log.Error("Import error", "file", arg, "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Printf("Import done in %v.\n\n", time.Since(start))
|
fmt.Printf("Import done in %v.\n\n", time.Since(start))
|
||||||
|
|
||||||
// Output pre-compaction stats mostly to see the import trashing
|
// Output pre-compaction stats mostly to see the import trashing
|
||||||
|
|
@ -183,6 +190,10 @@ func importChain(ctx *cli.Context) error {
|
||||||
fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000)
|
fmt.Printf("Allocations: %.3f million\n", float64(mem.Mallocs)/1000000)
|
||||||
fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs))
|
fmt.Printf("GC pause: %v\n\n", time.Duration(mem.PauseTotalNs))
|
||||||
|
|
||||||
|
if ctx.GlobalIsSet(utils.NoCompactionFlag.Name) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Compact the entire database to more accurately measure disk io and print the stats
|
// Compact the entire database to more accurately measure disk io and print the stats
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
fmt.Println("Compacting entire database...")
|
fmt.Println("Compacting entire database...")
|
||||||
|
|
@ -256,49 +267,6 @@ func removeDB(ctx *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func upgradeDB(ctx *cli.Context) error {
|
|
||||||
glog.Infoln("Upgrading blockchain database")
|
|
||||||
|
|
||||||
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
|
|
||||||
chain, chainDb := utils.MakeChain(ctx, stack)
|
|
||||||
bcVersion := core.GetBlockChainVersion(chainDb)
|
|
||||||
if bcVersion == 0 {
|
|
||||||
bcVersion = core.BlockChainVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export the current chain.
|
|
||||||
filename := fmt.Sprintf("blockchain_%d_%s.chain", bcVersion, time.Now().Format("20060102_150405"))
|
|
||||||
exportFile := filepath.Join(ctx.GlobalString(utils.DataDirFlag.Name), filename)
|
|
||||||
if err := utils.ExportChain(chain, exportFile); err != nil {
|
|
||||||
utils.Fatalf("Unable to export chain for reimport %s", err)
|
|
||||||
}
|
|
||||||
chainDb.Close()
|
|
||||||
if dir := dbDirectory(chainDb); dir != "" {
|
|
||||||
os.RemoveAll(dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Import the chain file.
|
|
||||||
chain, chainDb = utils.MakeChain(ctx, stack)
|
|
||||||
core.WriteBlockChainVersion(chainDb, core.BlockChainVersion)
|
|
||||||
err := utils.ImportChain(chain, exportFile)
|
|
||||||
chainDb.Close()
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf("Import error %v (a backup is made in %s, use the import command to import it)", err, exportFile)
|
|
||||||
} else {
|
|
||||||
os.Remove(exportFile)
|
|
||||||
glog.Infoln("Import finished")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func dbDirectory(db ethdb.Database) string {
|
|
||||||
ldb, ok := db.(*ethdb.LDBDatabase)
|
|
||||||
if !ok {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return ldb.Path()
|
|
||||||
}
|
|
||||||
|
|
||||||
func dump(ctx *cli.Context) error {
|
func dump(ctx *cli.Context) error {
|
||||||
stack := makeFullNode(ctx)
|
stack := makeFullNode(ctx)
|
||||||
chain, chainDb := utils.MakeChain(ctx, stack)
|
chain, chainDb := utils.MakeChain(ctx, stack)
|
||||||
|
|
@ -330,9 +298,3 @@ func hashish(x string) bool {
|
||||||
_, err := strconv.Atoi(x)
|
_, err := strconv.Atoi(x)
|
||||||
return err != nil
|
return err != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func closeAll(dbs ...ethdb.Database) {
|
|
||||||
for _, db := range dbs {
|
|
||||||
db.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -29,13 +29,13 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/console"
|
"github.com/ethereum/go-ethereum/console"
|
||||||
"github.com/ethereum/go-ethereum/contracts/release"
|
"github.com/ethereum/go-ethereum/contracts/release"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
"github.com/ethereum/go-ethereum/internal/debug"
|
"github.com/ethereum/go-ethereum/internal/debug"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -66,7 +66,6 @@ func init() {
|
||||||
initCommand,
|
initCommand,
|
||||||
importCommand,
|
importCommand,
|
||||||
exportCommand,
|
exportCommand,
|
||||||
upgradedbCommand,
|
|
||||||
removedbCommand,
|
removedbCommand,
|
||||||
dumpCommand,
|
dumpCommand,
|
||||||
// See monitorcmd.go:
|
// See monitorcmd.go:
|
||||||
|
|
@ -81,6 +80,7 @@ func init() {
|
||||||
// See misccmd.go:
|
// See misccmd.go:
|
||||||
makedagCommand,
|
makedagCommand,
|
||||||
versionCommand,
|
versionCommand,
|
||||||
|
bugCommand,
|
||||||
licenseCommand,
|
licenseCommand,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,6 +91,12 @@ func init() {
|
||||||
utils.BootnodesFlag,
|
utils.BootnodesFlag,
|
||||||
utils.DataDirFlag,
|
utils.DataDirFlag,
|
||||||
utils.KeyStoreDirFlag,
|
utils.KeyStoreDirFlag,
|
||||||
|
utils.EthashCacheDirFlag,
|
||||||
|
utils.EthashCachesInMemoryFlag,
|
||||||
|
utils.EthashCachesOnDiskFlag,
|
||||||
|
utils.EthashDatasetDirFlag,
|
||||||
|
utils.EthashDatasetsInMemoryFlag,
|
||||||
|
utils.EthashDatasetsOnDiskFlag,
|
||||||
utils.FastSyncFlag,
|
utils.FastSyncFlag,
|
||||||
utils.LightModeFlag,
|
utils.LightModeFlag,
|
||||||
utils.LightServFlag,
|
utils.LightServFlag,
|
||||||
|
|
@ -106,7 +112,6 @@ func init() {
|
||||||
utils.GasPriceFlag,
|
utils.GasPriceFlag,
|
||||||
utils.MinerThreadsFlag,
|
utils.MinerThreadsFlag,
|
||||||
utils.MiningEnabledFlag,
|
utils.MiningEnabledFlag,
|
||||||
utils.AutoDAGFlag,
|
|
||||||
utils.TargetGasLimitFlag,
|
utils.TargetGasLimitFlag,
|
||||||
utils.NATFlag,
|
utils.NATFlag,
|
||||||
utils.NoDiscoverFlag,
|
utils.NoDiscoverFlag,
|
||||||
|
|
@ -140,6 +145,7 @@ func init() {
|
||||||
utils.EthStatsURLFlag,
|
utils.EthStatsURLFlag,
|
||||||
utils.MetricsEnabledFlag,
|
utils.MetricsEnabledFlag,
|
||||||
utils.FakePoWFlag,
|
utils.FakePoWFlag,
|
||||||
|
utils.NoCompactionFlag,
|
||||||
utils.SolcPathFlag,
|
utils.SolcPathFlag,
|
||||||
utils.GpoMinGasPriceFlag,
|
utils.GpoMinGasPriceFlag,
|
||||||
utils.GpoMaxGasPriceFlag,
|
utils.GpoMaxGasPriceFlag,
|
||||||
|
|
@ -203,11 +209,10 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
||||||
}{uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch), clientIdentifier, runtime.Version(), runtime.GOOS}
|
}{uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch), clientIdentifier, runtime.Version(), runtime.GOOS}
|
||||||
extra, err := rlp.EncodeToBytes(clientInfo)
|
extra, err := rlp.EncodeToBytes(clientInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Warn).Infoln("error setting canonical miner information:", err)
|
log.Warn("Failed to set canonical miner information", "err", err)
|
||||||
}
|
}
|
||||||
if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() {
|
if uint64(len(extra)) > params.MaximumExtraDataSize {
|
||||||
glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize)
|
log.Warn("Miner extra data exceed limit", "extra", hexutil.Bytes(extra), "limit", params.MaximumExtraDataSize)
|
||||||
glog.V(logger.Debug).Infof("extra: %x\n", extra)
|
|
||||||
extra = nil
|
extra = nil
|
||||||
}
|
}
|
||||||
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
|
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
|
||||||
|
|
@ -272,7 +277,7 @@ func startNode(ctx *cli.Context, stack *node.Node) {
|
||||||
// Open and self derive any wallets already attached
|
// Open and self derive any wallets already attached
|
||||||
for _, wallet := range stack.AccountManager().Wallets() {
|
for _, wallet := range stack.AccountManager().Wallets() {
|
||||||
if err := wallet.Open(""); err != nil {
|
if err := wallet.Open(""); err != nil {
|
||||||
glog.V(logger.Warn).Infof("Failed to open wallet %s: %v", wallet.URL(), err)
|
log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err)
|
||||||
} else {
|
} else {
|
||||||
wallet.SelfDerive(accounts.DefaultBaseDerivationPath, stateReader)
|
wallet.SelfDerive(accounts.DefaultBaseDerivationPath, stateReader)
|
||||||
}
|
}
|
||||||
|
|
@ -281,13 +286,13 @@ func startNode(ctx *cli.Context, stack *node.Node) {
|
||||||
for event := range events {
|
for event := range events {
|
||||||
if event.Arrive {
|
if event.Arrive {
|
||||||
if err := event.Wallet.Open(""); err != nil {
|
if err := event.Wallet.Open(""); err != nil {
|
||||||
glog.V(logger.Info).Infof("New wallet appeared: %s, failed to open: %s", event.Wallet.URL(), err)
|
log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err)
|
||||||
} else {
|
} else {
|
||||||
glog.V(logger.Info).Infof("New wallet appeared: %s, %s", event.Wallet.URL(), event.Wallet.Status())
|
log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", event.Wallet.Status())
|
||||||
event.Wallet.SelfDerive(accounts.DefaultBaseDerivationPath, stateReader)
|
event.Wallet.SelfDerive(accounts.DefaultBaseDerivationPath, stateReader)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
glog.V(logger.Info).Infof("Old wallet dropped: %s", event.Wallet.URL())
|
log.Info("Old wallet dropped", "url", event.Wallet.URL())
|
||||||
event.Wallet.Close()
|
event.Wallet.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,10 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/ethash"
|
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -87,7 +87,7 @@ func makedag(ctx *cli.Context) error {
|
||||||
utils.Fatalf("Can't find dir")
|
utils.Fatalf("Can't find dir")
|
||||||
}
|
}
|
||||||
fmt.Println("making DAG, this could take awhile...")
|
fmt.Println("making DAG, this could take awhile...")
|
||||||
ethash.MakeDAG(blockNum, dir)
|
pow.MakeDataset(blockNum, dir)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
wrongArgs()
|
wrongArgs()
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,17 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
utils.LightKDFFlag,
|
utils.LightKDFFlag,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "ETHASH",
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
utils.EthashCacheDirFlag,
|
||||||
|
utils.EthashCachesInMemoryFlag,
|
||||||
|
utils.EthashCachesOnDiskFlag,
|
||||||
|
utils.EthashDatasetDirFlag,
|
||||||
|
utils.EthashDatasetsInMemoryFlag,
|
||||||
|
utils.EthashDatasetsOnDiskFlag,
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "PERFORMANCE TUNING",
|
Name: "PERFORMANCE TUNING",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
|
|
@ -131,7 +142,6 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
utils.MiningEnabledFlag,
|
utils.MiningEnabledFlag,
|
||||||
utils.MinerThreadsFlag,
|
utils.MinerThreadsFlag,
|
||||||
utils.AutoDAGFlag,
|
|
||||||
utils.EtherbaseFlag,
|
utils.EtherbaseFlag,
|
||||||
utils.TargetGasLimitFlag,
|
utils.TargetGasLimitFlag,
|
||||||
utils.GasPriceFlag,
|
utils.GasPriceFlag,
|
||||||
|
|
|
||||||
|
|
@ -1,162 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of go-ethereum.
|
|
||||||
//
|
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// go-ethereum is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// gethrpctest is a command to run the external RPC tests.
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/node"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
"github.com/ethereum/go-ethereum/tests"
|
|
||||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv2"
|
|
||||||
)
|
|
||||||
|
|
||||||
const defaultTestKey = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
|
|
||||||
|
|
||||||
var (
|
|
||||||
testFile = flag.String("json", "", "Path to the .json test file to load")
|
|
||||||
testName = flag.String("test", "", "Name of the test from the .json file to run")
|
|
||||||
testKey = flag.String("key", defaultTestKey, "Private key of a test account to inject")
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
// Enable logging errors, we really do want to see those
|
|
||||||
glog.SetV(2)
|
|
||||||
glog.SetToStderr(true)
|
|
||||||
|
|
||||||
// Load the test suite to run the RPC against
|
|
||||||
tests, err := tests.LoadBlockTests(*testFile)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to load test suite: %v", err)
|
|
||||||
}
|
|
||||||
test, found := tests[*testName]
|
|
||||||
if !found {
|
|
||||||
log.Fatalf("Requested test (%s) not found within suite", *testName)
|
|
||||||
}
|
|
||||||
|
|
||||||
stack, err := MakeSystemNode(*testKey, test)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Failed to assemble test stack: %v", err)
|
|
||||||
}
|
|
||||||
if err := stack.Start(); err != nil {
|
|
||||||
log.Fatalf("Failed to start test node: %v", err)
|
|
||||||
}
|
|
||||||
defer stack.Stop()
|
|
||||||
|
|
||||||
log.Println("Test node started...")
|
|
||||||
|
|
||||||
// Make sure the tests contained within the suite pass
|
|
||||||
if err := RunTest(stack, test); err != nil {
|
|
||||||
log.Fatalf("Failed to run the pre-configured test: %v", err)
|
|
||||||
}
|
|
||||||
log.Println("Initial test suite passed...")
|
|
||||||
|
|
||||||
quit := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(quit, os.Interrupt)
|
|
||||||
<-quit
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeSystemNode configures a protocol stack for the RPC tests based on a given
|
|
||||||
// keystore path and initial pre-state.
|
|
||||||
func MakeSystemNode(privkey string, test *tests.BlockTest) (*node.Node, error) {
|
|
||||||
// Create a networkless protocol stack
|
|
||||||
stack, err := node.New(&node.Config{
|
|
||||||
UseLightweightKDF: true,
|
|
||||||
IPCPath: node.DefaultIPCEndpoint(""),
|
|
||||||
HTTPHost: node.DefaultHTTPHost,
|
|
||||||
HTTPPort: node.DefaultHTTPPort,
|
|
||||||
HTTPModules: []string{"admin", "db", "eth", "debug", "miner", "net", "shh", "txpool", "personal", "web3"},
|
|
||||||
WSHost: node.DefaultWSHost,
|
|
||||||
WSPort: node.DefaultWSPort,
|
|
||||||
WSModules: []string{"admin", "db", "eth", "debug", "miner", "net", "shh", "txpool", "personal", "web3"},
|
|
||||||
NoDiscovery: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Create the keystore and inject an unlocked account if requested
|
|
||||||
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
|
||||||
|
|
||||||
if len(privkey) > 0 {
|
|
||||||
key, err := crypto.HexToECDSA(privkey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
a, err := ks.ImportECDSA(key, "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := ks.Unlock(a, ""); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Initialize and register the Ethereum protocol
|
|
||||||
db, _ := ethdb.NewMemDatabase()
|
|
||||||
if _, err := test.InsertPreState(db); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ethConf := ð.Config{
|
|
||||||
TestGenesisState: db,
|
|
||||||
TestGenesisBlock: test.Genesis,
|
|
||||||
ChainConfig: ¶ms.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock},
|
|
||||||
}
|
|
||||||
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ethConf) }); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// Initialize and register the Whisper protocol
|
|
||||||
if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return stack, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunTest executes the specified test against an already pre-configured protocol
|
|
||||||
// stack to ensure basic checks pass before running RPC tests.
|
|
||||||
func RunTest(stack *node.Node, test *tests.BlockTest) error {
|
|
||||||
var ethereum *eth.Ethereum
|
|
||||||
stack.Service(ðereum)
|
|
||||||
blockchain := ethereum.BlockChain()
|
|
||||||
|
|
||||||
// Process the blocks and verify the imported headers
|
|
||||||
blocks, err := test.TryBlocksInsert(blockchain)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := test.ValidateImportedHeaders(blockchain, blocks); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Retrieve the assembled state and validate it
|
|
||||||
stateDb, err := blockchain.State()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := test.ValidatePostState(stateDb); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
46
cmd/internal/browser/browser.go
Normal file
46
cmd/internal/browser/browser.go
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
// Copyright 2016 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.
|
||||||
|
|
||||||
|
// Package browser provides utilities for interacting with users' browsers.
|
||||||
|
package browser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Commands returns a list of possible commands to use to open a url.
|
||||||
|
func Commands() [][]string {
|
||||||
|
var cmds [][]string
|
||||||
|
if exe := os.Getenv("BROWSER"); exe != "" {
|
||||||
|
cmds = append(cmds, []string{exe})
|
||||||
|
}
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
cmds = append(cmds, []string{"/usr/bin/open"})
|
||||||
|
case "windows":
|
||||||
|
cmds = append(cmds, []string{"cmd", "/c", "start"})
|
||||||
|
default:
|
||||||
|
cmds = append(cmds, []string{"xdg-open"})
|
||||||
|
}
|
||||||
|
cmds = append(cmds,
|
||||||
|
[]string{"chrome"},
|
||||||
|
[]string{"google-chrome"},
|
||||||
|
[]string{"chromium"},
|
||||||
|
[]string{"firefox"},
|
||||||
|
)
|
||||||
|
return cmds
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open tries to open url in a browser and reports whether it succeeded.
|
||||||
|
func Open(url string) bool {
|
||||||
|
for _, args := range Commands() {
|
||||||
|
cmd := exec.Command(args[0], append(args[1:], url)...)
|
||||||
|
if cmd.Start() == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
@ -17,8 +17,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
@ -26,14 +25,14 @@ import (
|
||||||
func cleandb(ctx *cli.Context) {
|
func cleandb(ctx *cli.Context) {
|
||||||
args := ctx.Args()
|
args := ctx.Args()
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
log.Fatal("need path to chunks database as the first and only argument")
|
utils.Fatalf("Need path to chunks database as the first and only argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
chunkDbPath := args[0]
|
chunkDbPath := args[0]
|
||||||
hash := storage.MakeHashFunc("SHA3")
|
hash := storage.MakeHashFunc("SHA3")
|
||||||
dbStore, err := storage.NewDbStore(chunkDbPath, hash, 10000000, 0)
|
dbStore, err := storage.NewDbStore(chunkDbPath, hash, 10000000, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("cannot initialise dbstore: %v", err)
|
utils.Fatalf("Cannot initialise dbstore: %v", err)
|
||||||
}
|
}
|
||||||
dbStore.Cleanup()
|
dbStore.Cleanup()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,9 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
@ -29,12 +29,11 @@ import (
|
||||||
func hash(ctx *cli.Context) {
|
func hash(ctx *cli.Context) {
|
||||||
args := ctx.Args()
|
args := ctx.Args()
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
log.Fatal("Usage: swarm hash <file name>")
|
utils.Fatalf("Usage: swarm hash <file name>")
|
||||||
}
|
}
|
||||||
f, err := os.Open(args[0])
|
f, err := os.Open(args[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error opening file " + args[1])
|
utils.Fatalf("Error opening file " + args[1])
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
|
|
@ -42,7 +41,7 @@ func hash(ctx *cli.Context) {
|
||||||
chunker := storage.NewTreeChunker(storage.NewChunkerParams())
|
chunker := storage.NewTreeChunker(storage.NewChunkerParams())
|
||||||
key, err := chunker.Split(f, stat.Size(), nil, nil, nil)
|
key, err := chunker.Split(f, stat.Size(), nil, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("%v\n", err)
|
utils.Fatalf("%v\n", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%v\n", key)
|
fmt.Printf("%v\n", key)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethclient"
|
"github.com/ethereum/go-ethereum/ethclient"
|
||||||
"github.com/ethereum/go-ethereum/internal/debug"
|
"github.com/ethereum/go-ethereum/internal/debug"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
|
@ -123,7 +122,7 @@ func init() {
|
||||||
// Override flag defaults so bzzd can run alongside geth.
|
// Override flag defaults so bzzd can run alongside geth.
|
||||||
utils.ListenPortFlag.Value = 30399
|
utils.ListenPortFlag.Value = 30399
|
||||||
utils.IPCPathFlag.Value = utils.DirectoryString{Value: "bzzd.ipc"}
|
utils.IPCPathFlag.Value = utils.DirectoryString{Value: "bzzd.ipc"}
|
||||||
utils.IPCApiFlag.Value = "admin, bzz, chequebook, debug, rpc, web3"
|
utils.IPCApiFlag.Value = "admin, bzz, chequebook, debug, rpc, swarmfs, web3"
|
||||||
|
|
||||||
// Set up the cli app.
|
// Set up the cli app.
|
||||||
app.Action = bzzd
|
app.Action = bzzd
|
||||||
|
|
@ -278,7 +277,7 @@ func bzzd(ctx *cli.Context) error {
|
||||||
signal.Notify(sigc, syscall.SIGTERM)
|
signal.Notify(sigc, syscall.SIGTERM)
|
||||||
defer signal.Stop(sigc)
|
defer signal.Stop(sigc)
|
||||||
<-sigc
|
<-sigc
|
||||||
glog.V(logger.Info).Infoln("Got sigterm, shutting down...")
|
log.Info("Got sigterm, shutting swarm down...")
|
||||||
stack.Stop()
|
stack.Stop()
|
||||||
}()
|
}()
|
||||||
networkId := ctx.GlobalUint64(SwarmNetworkIdFlag.Name)
|
networkId := ctx.GlobalUint64(SwarmNetworkIdFlag.Name)
|
||||||
|
|
@ -343,7 +342,7 @@ func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey {
|
||||||
}
|
}
|
||||||
// Try to load the arg as a hex key file.
|
// Try to load the arg as a hex key file.
|
||||||
if key, err := crypto.LoadECDSA(keyid); err == nil {
|
if key, err := crypto.LoadECDSA(keyid); err == nil {
|
||||||
glog.V(logger.Info).Infof("swarm account key loaded: %#x", crypto.PubkeyToAddress(key.PublicKey))
|
log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey))
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
// Otherwise try getting it from the keystore.
|
// Otherwise try getting it from the keystore.
|
||||||
|
|
@ -400,7 +399,7 @@ func injectBootnodes(srv *p2p.Server, nodes []string) {
|
||||||
for _, url := range nodes {
|
for _, url := range nodes {
|
||||||
n, err := discover.ParseNode(url)
|
n, err := discover.ParseNode(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("invalid bootnode %q", err)
|
log.Error("Invalid swarm bootnode", "err", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
srv.AddPeer(n)
|
srv.AddPeer(n)
|
||||||
|
|
|
||||||
|
|
@ -18,20 +18,20 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gopkg.in/urfave/cli.v1"
|
"encoding/json"
|
||||||
"log"
|
"fmt"
|
||||||
"mime"
|
"mime"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"fmt"
|
|
||||||
"encoding/json"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func add(ctx *cli.Context) {
|
func add(ctx *cli.Context) {
|
||||||
|
|
||||||
args := ctx.Args()
|
args := ctx.Args()
|
||||||
if len(args) < 3 {
|
if len(args) < 3 {
|
||||||
log.Fatal("need atleast three arguments <MHASH> <path> <HASH> [<content-type>]")
|
utils.Fatalf("Need atleast three arguments <MHASH> <path> <HASH> [<content-type>]")
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -44,14 +44,13 @@ func add(ctx *cli.Context) {
|
||||||
mroot manifest
|
mroot manifest
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
if len(args) > 3 {
|
if len(args) > 3 {
|
||||||
ctype = args[3]
|
ctype = args[3]
|
||||||
} else {
|
} else {
|
||||||
ctype = mime.TypeByExtension(filepath.Ext(path))
|
ctype = mime.TypeByExtension(filepath.Ext(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
newManifest := addEntryToManifest (ctx, mhash, path, hash, ctype)
|
newManifest := addEntryToManifest(ctx, mhash, path, hash, ctype)
|
||||||
fmt.Println(newManifest)
|
fmt.Println(newManifest)
|
||||||
|
|
||||||
if !wantManifest {
|
if !wantManifest {
|
||||||
|
|
@ -66,7 +65,7 @@ func update(ctx *cli.Context) {
|
||||||
|
|
||||||
args := ctx.Args()
|
args := ctx.Args()
|
||||||
if len(args) < 3 {
|
if len(args) < 3 {
|
||||||
log.Fatal("need atleast three arguments <MHASH> <path> <HASH>")
|
utils.Fatalf("Need atleast three arguments <MHASH> <path> <HASH>")
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -84,7 +83,7 @@ func update(ctx *cli.Context) {
|
||||||
ctype = mime.TypeByExtension(filepath.Ext(path))
|
ctype = mime.TypeByExtension(filepath.Ext(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
newManifest := updateEntryInManifest (ctx, mhash, path, hash, ctype)
|
newManifest := updateEntryInManifest(ctx, mhash, path, hash, ctype)
|
||||||
fmt.Println(newManifest)
|
fmt.Println(newManifest)
|
||||||
|
|
||||||
if !wantManifest {
|
if !wantManifest {
|
||||||
|
|
@ -98,7 +97,7 @@ func update(ctx *cli.Context) {
|
||||||
func remove(ctx *cli.Context) {
|
func remove(ctx *cli.Context) {
|
||||||
args := ctx.Args()
|
args := ctx.Args()
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
log.Fatal("need atleast two arguments <MHASH> <path>")
|
utils.Fatalf("Need atleast two arguments <MHASH> <path>")
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -109,7 +108,7 @@ func remove(ctx *cli.Context) {
|
||||||
mroot manifest
|
mroot manifest
|
||||||
)
|
)
|
||||||
|
|
||||||
newManifest := removeEntryFromManifest (ctx, mhash, path)
|
newManifest := removeEntryFromManifest(ctx, mhash, path)
|
||||||
fmt.Println(newManifest)
|
fmt.Println(newManifest)
|
||||||
|
|
||||||
if !wantManifest {
|
if !wantManifest {
|
||||||
|
|
@ -120,7 +119,7 @@ func remove(ctx *cli.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func addEntryToManifest(ctx *cli.Context, mhash , path, hash , ctype string) string {
|
func addEntryToManifest(ctx *cli.Context, mhash, path, hash, ctype string) string {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
|
bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
|
||||||
|
|
@ -134,21 +133,20 @@ func addEntryToManifest(ctx *cli.Context, mhash , path, hash , ctype string) st
|
||||||
|
|
||||||
mroot, err := client.downloadManifest(mhash)
|
mroot, err := client.downloadManifest(mhash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("manifest download failed:", err)
|
utils.Fatalf("Manifest download failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: check if the "hash" to add is valid and present in swarm
|
//TODO: check if the "hash" to add is valid and present in swarm
|
||||||
_, err = client.downloadManifest(hash)
|
_, err = client.downloadManifest(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("hash to add is not present:", err)
|
utils.Fatalf("Hash to add is not present: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// See if we path is in this Manifest or do we have to dig deeper
|
// See if we path is in this Manifest or do we have to dig deeper
|
||||||
for _, entry := range mroot.Entries {
|
for _, entry := range mroot.Entries {
|
||||||
if path == entry.Path {
|
if path == entry.Path {
|
||||||
log.Fatal(path, "Already present, not adding anything")
|
utils.Fatalf("Path %s already present, not adding anything", path)
|
||||||
}else {
|
} else {
|
||||||
if entry.ContentType == "application/bzz-manifest+json" {
|
if entry.ContentType == "application/bzz-manifest+json" {
|
||||||
prfxlen := strings.HasPrefix(path, entry.Path)
|
prfxlen := strings.HasPrefix(path, entry.Path)
|
||||||
if prfxlen && len(path) > len(longestPathEntry.Path) {
|
if prfxlen && len(path) > len(longestPathEntry.Path) {
|
||||||
|
|
@ -161,7 +159,7 @@ func addEntryToManifest(ctx *cli.Context, mhash , path, hash , ctype string) st
|
||||||
if longestPathEntry.Path != "" {
|
if longestPathEntry.Path != "" {
|
||||||
// Load the child Manifest add the entry there
|
// Load the child Manifest add the entry there
|
||||||
newPath := path[len(longestPathEntry.Path):]
|
newPath := path[len(longestPathEntry.Path):]
|
||||||
newHash := addEntryToManifest (ctx, longestPathEntry.Hash, newPath, hash, ctype)
|
newHash := addEntryToManifest(ctx, longestPathEntry.Hash, newPath, hash, ctype)
|
||||||
|
|
||||||
// Replace the hash for parent Manifests
|
// Replace the hash for parent Manifests
|
||||||
newMRoot := manifest{}
|
newMRoot := manifest{}
|
||||||
|
|
@ -182,18 +180,15 @@ func addEntryToManifest(ctx *cli.Context, mhash , path, hash , ctype string) st
|
||||||
mroot.Entries = append(mroot.Entries, newEntry)
|
mroot.Entries = append(mroot.Entries, newEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
newManifestHash, err := client.uploadManifest(mroot)
|
newManifestHash, err := client.uploadManifest(mroot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("manifest upload failed:", err)
|
utils.Fatalf("Manifest upload failed: %v", err)
|
||||||
}
|
}
|
||||||
return newManifestHash
|
return newManifestHash
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateEntryInManifest(ctx *cli.Context, mhash , path, hash , ctype string) string {
|
func updateEntryInManifest(ctx *cli.Context, mhash, path, hash, ctype string) string {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
|
bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
|
||||||
|
|
@ -212,17 +207,16 @@ func updateEntryInManifest(ctx *cli.Context, mhash , path, hash , ctype string)
|
||||||
|
|
||||||
mroot, err := client.downloadManifest(mhash)
|
mroot, err := client.downloadManifest(mhash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("manifest download failed:", err)
|
utils.Fatalf("Manifest download failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: check if the "hash" with which to update is valid and present in swarm
|
//TODO: check if the "hash" with which to update is valid and present in swarm
|
||||||
|
|
||||||
|
|
||||||
// See if we path is in this Manifest or do we have to dig deeper
|
// See if we path is in this Manifest or do we have to dig deeper
|
||||||
for _, entry := range mroot.Entries {
|
for _, entry := range mroot.Entries {
|
||||||
if path == entry.Path {
|
if path == entry.Path {
|
||||||
newEntry = entry
|
newEntry = entry
|
||||||
}else {
|
} else {
|
||||||
if entry.ContentType == "application/bzz-manifest+json" {
|
if entry.ContentType == "application/bzz-manifest+json" {
|
||||||
prfxlen := strings.HasPrefix(path, entry.Path)
|
prfxlen := strings.HasPrefix(path, entry.Path)
|
||||||
if prfxlen && len(path) > len(longestPathEntry.Path) {
|
if prfxlen && len(path) > len(longestPathEntry.Path) {
|
||||||
|
|
@ -233,13 +227,13 @@ func updateEntryInManifest(ctx *cli.Context, mhash , path, hash , ctype string)
|
||||||
}
|
}
|
||||||
|
|
||||||
if longestPathEntry.Path == "" && newEntry.Path == "" {
|
if longestPathEntry.Path == "" && newEntry.Path == "" {
|
||||||
log.Fatal(path, " Path not present in the Manifest, not setting anything")
|
utils.Fatalf("Path %s not present in the Manifest, not setting anything", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
if longestPathEntry.Path != "" {
|
if longestPathEntry.Path != "" {
|
||||||
// Load the child Manifest add the entry there
|
// Load the child Manifest add the entry there
|
||||||
newPath := path[len(longestPathEntry.Path):]
|
newPath := path[len(longestPathEntry.Path):]
|
||||||
newHash := updateEntryInManifest (ctx, longestPathEntry.Hash, newPath, hash, ctype)
|
newHash := updateEntryInManifest(ctx, longestPathEntry.Hash, newPath, hash, ctype)
|
||||||
|
|
||||||
// Replace the hash for parent Manifests
|
// Replace the hash for parent Manifests
|
||||||
newMRoot := manifest{}
|
newMRoot := manifest{}
|
||||||
|
|
@ -271,15 +265,14 @@ func updateEntryInManifest(ctx *cli.Context, mhash , path, hash , ctype string)
|
||||||
mroot = newMRoot
|
mroot = newMRoot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
newManifestHash, err := client.uploadManifest(mroot)
|
newManifestHash, err := client.uploadManifest(mroot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("manifest upload failed:", err)
|
utils.Fatalf("Manifest upload failed: %v", err)
|
||||||
}
|
}
|
||||||
return newManifestHash
|
return newManifestHash
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeEntryFromManifest(ctx *cli.Context, mhash , path string) string {
|
func removeEntryFromManifest(ctx *cli.Context, mhash, path string) string {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
|
bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
|
||||||
|
|
@ -298,16 +291,14 @@ func removeEntryFromManifest(ctx *cli.Context, mhash , path string) string {
|
||||||
|
|
||||||
mroot, err := client.downloadManifest(mhash)
|
mroot, err := client.downloadManifest(mhash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("manifest download failed:", err)
|
utils.Fatalf("Manifest download failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// See if we path is in this Manifest or do we have to dig deeper
|
// See if we path is in this Manifest or do we have to dig deeper
|
||||||
for _, entry := range mroot.Entries {
|
for _, entry := range mroot.Entries {
|
||||||
if path == entry.Path {
|
if path == entry.Path {
|
||||||
entryToRemove = entry
|
entryToRemove = entry
|
||||||
}else {
|
} else {
|
||||||
if entry.ContentType == "application/bzz-manifest+json" {
|
if entry.ContentType == "application/bzz-manifest+json" {
|
||||||
prfxlen := strings.HasPrefix(path, entry.Path)
|
prfxlen := strings.HasPrefix(path, entry.Path)
|
||||||
if prfxlen && len(path) > len(longestPathEntry.Path) {
|
if prfxlen && len(path) > len(longestPathEntry.Path) {
|
||||||
|
|
@ -318,13 +309,13 @@ func removeEntryFromManifest(ctx *cli.Context, mhash , path string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
if longestPathEntry.Path == "" && entryToRemove.Path == "" {
|
if longestPathEntry.Path == "" && entryToRemove.Path == "" {
|
||||||
log.Fatal(path, "Path not present in the Manifest, not removing anything")
|
utils.Fatalf("Path %s not present in the Manifest, not removing anything", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
if longestPathEntry.Path != "" {
|
if longestPathEntry.Path != "" {
|
||||||
// Load the child Manifest remove the entry there
|
// Load the child Manifest remove the entry there
|
||||||
newPath := path[len(longestPathEntry.Path):]
|
newPath := path[len(longestPathEntry.Path):]
|
||||||
newHash := removeEntryFromManifest (ctx, longestPathEntry.Hash, newPath)
|
newHash := removeEntryFromManifest(ctx, longestPathEntry.Hash, newPath)
|
||||||
|
|
||||||
// Replace the hash for parent Manifests
|
// Replace the hash for parent Manifests
|
||||||
newMRoot := manifest{}
|
newMRoot := manifest{}
|
||||||
|
|
@ -348,13 +339,9 @@ func removeEntryFromManifest(ctx *cli.Context, mhash , path string) string {
|
||||||
mroot = newMRoot
|
mroot = newMRoot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
newManifestHash, err := client.uploadManifest(mroot)
|
newManifestHash, err := client.uploadManifest(mroot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("manifest upload failed:", err)
|
utils.Fatalf("Manifest upload failed: %v", err)
|
||||||
}
|
}
|
||||||
return newManifestHash
|
return newManifestHash
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -32,6 +31,8 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"gopkg.in/urfave/cli.v1"
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -44,7 +45,7 @@ func upload(ctx *cli.Context) {
|
||||||
defaultPath = ctx.GlobalString(SwarmUploadDefaultPath.Name)
|
defaultPath = ctx.GlobalString(SwarmUploadDefaultPath.Name)
|
||||||
)
|
)
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
log.Fatal("need filename as the first and only argument")
|
utils.Fatalf("Need filename as the first and only argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -53,25 +54,25 @@ func upload(ctx *cli.Context) {
|
||||||
)
|
)
|
||||||
fi, err := os.Stat(expandPath(file))
|
fi, err := os.Stat(expandPath(file))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
utils.Fatalf("Failed to stat file: %v", err)
|
||||||
}
|
}
|
||||||
if fi.IsDir() {
|
if fi.IsDir() {
|
||||||
if !recursive {
|
if !recursive {
|
||||||
log.Fatal("argument is a directory and recursive upload is disabled")
|
utils.Fatalf("Argument is a directory and recursive upload is disabled")
|
||||||
}
|
}
|
||||||
if !wantManifest {
|
if !wantManifest {
|
||||||
log.Fatal("manifest is required for directory uploads")
|
utils.Fatalf("Manifest is required for directory uploads")
|
||||||
}
|
}
|
||||||
mhash, err := client.uploadDirectory(file, defaultPath)
|
mhash, err := client.uploadDirectory(file, defaultPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
utils.Fatalf("Failed to upload directory: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Println(mhash)
|
fmt.Println(mhash)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
entry, err := client.uploadFile(file, fi)
|
entry, err := client.uploadFile(file, fi)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("upload failed:", err)
|
utils.Fatalf("Upload failed: %v", err)
|
||||||
}
|
}
|
||||||
mroot := manifest{[]manifestEntry{entry}}
|
mroot := manifest{[]manifestEntry{entry}}
|
||||||
if !wantManifest {
|
if !wantManifest {
|
||||||
|
|
@ -82,7 +83,7 @@ func upload(ctx *cli.Context) {
|
||||||
}
|
}
|
||||||
hash, err := client.uploadManifest(mroot)
|
hash, err := client.uploadManifest(mroot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("manifest upload failed:", err)
|
utils.Fatalf("Manifest upload failed: %v", err)
|
||||||
}
|
}
|
||||||
fmt.Println(hash)
|
fmt.Println(hash)
|
||||||
}
|
}
|
||||||
|
|
@ -173,7 +174,7 @@ func (c *client) uploadFileContent(file string, fi os.FileInfo) (string, error)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
log.Printf("uploading file %s (%d bytes)", file, fi.Size())
|
log.Info("Uploading swarm content", "file", file, "bytes", fi.Size())
|
||||||
return c.postRaw("application/octet-stream", fi.Size(), fd)
|
return c.postRaw("application/octet-stream", fi.Size(), fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,7 +183,7 @@ func (c *client) uploadManifest(m manifest) (string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
log.Println("uploading manifest")
|
log.Info("Uploading swarm manifest")
|
||||||
return c.postRaw("application/json", int64(len(jsm)), ioutil.NopCloser(bytes.NewReader(jsm)))
|
return c.postRaw("application/json", int64(len(jsm)), ioutil.NopCloser(bytes.NewReader(jsm)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,7 +193,7 @@ func (c *client) uploadToManifest(mhash string, path string, fpath string, fi os
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
log.Printf("uploading file %s (%d bytes) and adding path %v", fpath, fi.Size(), path)
|
log.Info("Uploading swarm content and path", "file", fpath, "bytes", fi.Size(), "path", path)
|
||||||
req, err := http.NewRequest("PUT", c.api+"/bzz:/"+mhash+"/"+path, fd)
|
req, err := http.NewRequest("PUT", c.api+"/bzz:/"+mhash+"/"+path, fd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
@ -233,7 +234,7 @@ func (c *client) postRaw(mimetype string, size int64, body io.ReadCloser) (strin
|
||||||
func (c *client) downloadManifest(mhash string) (manifest, error) {
|
func (c *client) downloadManifest(mhash string) (manifest, error) {
|
||||||
|
|
||||||
mroot := manifest{}
|
mroot := manifest{}
|
||||||
req, err := http.NewRequest("GET", c.api + "/bzzr:/" + mhash, nil)
|
req, err := http.NewRequest("GET", c.api+"/bzzr:/"+mhash, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return mroot, err
|
return mroot, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,16 +23,13 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"regexp"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/internal/debug"
|
"github.com/ethereum/go-ethereum/internal/debug"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
@ -41,15 +38,6 @@ const (
|
||||||
importBatchSize = 2500
|
importBatchSize = 2500
|
||||||
)
|
)
|
||||||
|
|
||||||
func openLogFile(Datadir string, filename string) *os.File {
|
|
||||||
path := common.AbsolutePath(Datadir, filename)
|
|
||||||
file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("error opening log file '%s': %v", filename, err))
|
|
||||||
}
|
|
||||||
return file
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatalf formats a message to standard error and exits the program.
|
// Fatalf formats a message to standard error and exits the program.
|
||||||
// The message is also printed to standard output if standard error
|
// The message is also printed to standard output if standard error
|
||||||
// is redirected to a different file.
|
// is redirected to a different file.
|
||||||
|
|
@ -79,12 +67,12 @@ func StartNode(stack *node.Node) {
|
||||||
signal.Notify(sigc, os.Interrupt)
|
signal.Notify(sigc, os.Interrupt)
|
||||||
defer signal.Stop(sigc)
|
defer signal.Stop(sigc)
|
||||||
<-sigc
|
<-sigc
|
||||||
glog.V(logger.Info).Infoln("Got interrupt, shutting down...")
|
log.Info("Got interrupt, shutting down...")
|
||||||
go stack.Stop()
|
go stack.Stop()
|
||||||
for i := 10; i > 0; i-- {
|
for i := 10; i > 0; i-- {
|
||||||
<-sigc
|
<-sigc
|
||||||
if i > 1 {
|
if i > 1 {
|
||||||
glog.V(logger.Info).Infof("Already shutting down, interrupt %d more times for panic.", i-1)
|
log.Warn("Already shutting down, interrupt more to panic.", "times", i-1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debug.Exit() // ensure trace and CPU profile data is flushed.
|
debug.Exit() // ensure trace and CPU profile data is flushed.
|
||||||
|
|
@ -92,19 +80,6 @@ func StartNode(stack *node.Node) {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatTransactionData(data string) []byte {
|
|
||||||
d := common.StringToByteFunc(data, func(s string) (ret []byte) {
|
|
||||||
slice := regexp.MustCompile(`\n|\s`).Split(s, 1000000000)
|
|
||||||
for _, dataItem := range slice {
|
|
||||||
d := common.FormatData(dataItem)
|
|
||||||
ret = append(ret, d...)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
})
|
|
||||||
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
func ImportChain(chain *core.BlockChain, fn string) error {
|
func ImportChain(chain *core.BlockChain, fn string) error {
|
||||||
// Watch for Ctrl-C while the import is running.
|
// Watch for Ctrl-C while the import is running.
|
||||||
// If a signal is received, the import will stop at the next batch.
|
// If a signal is received, the import will stop at the next batch.
|
||||||
|
|
@ -115,7 +90,7 @@ func ImportChain(chain *core.BlockChain, fn string) error {
|
||||||
defer close(interrupt)
|
defer close(interrupt)
|
||||||
go func() {
|
go func() {
|
||||||
if _, ok := <-interrupt; ok {
|
if _, ok := <-interrupt; ok {
|
||||||
glog.Info("caught interrupt during import, will stop at next batch")
|
log.Info("Interrupted during import, stopping at next batch")
|
||||||
}
|
}
|
||||||
close(stop)
|
close(stop)
|
||||||
}()
|
}()
|
||||||
|
|
@ -128,7 +103,7 @@ func ImportChain(chain *core.BlockChain, fn string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
glog.Infoln("Importing blockchain ", fn)
|
log.Info("Importing blockchain", "file", fn)
|
||||||
fh, err := os.Open(fn)
|
fh, err := os.Open(fn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -176,8 +151,7 @@ func ImportChain(chain *core.BlockChain, fn string) error {
|
||||||
return fmt.Errorf("interrupted")
|
return fmt.Errorf("interrupted")
|
||||||
}
|
}
|
||||||
if hasAllBlocks(chain, blocks[:i]) {
|
if hasAllBlocks(chain, blocks[:i]) {
|
||||||
glog.Infof("skipping batch %d, all blocks present [%x / %x]",
|
log.Info("Skipping batch as all blocks present", "batch", batch, "first", blocks[0].Hash(), "last", blocks[i-1].Hash())
|
||||||
batch, blocks[0].Hash().Bytes()[:4], blocks[i-1].Hash().Bytes()[:4])
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,7 +172,7 @@ func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExportChain(blockchain *core.BlockChain, fn string) error {
|
func ExportChain(blockchain *core.BlockChain, fn string) error {
|
||||||
glog.Infoln("Exporting blockchain to ", fn)
|
log.Info("Exporting blockchain", "file", fn)
|
||||||
fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
|
fh, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -214,13 +188,13 @@ func ExportChain(blockchain *core.BlockChain, fn string) error {
|
||||||
if err := blockchain.Export(writer); err != nil {
|
if err := blockchain.Export(writer); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
glog.Infoln("Exported blockchain to ", fn)
|
log.Info("Exported blockchain", "file", fn)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, last uint64) error {
|
func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, last uint64) error {
|
||||||
glog.Infoln("Exporting blockchain to ", fn)
|
log.Info("Exporting blockchain", "file", fn)
|
||||||
// TODO verify mode perms
|
// TODO verify mode perms
|
||||||
fh, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
|
fh, err := os.OpenFile(fn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -237,6 +211,6 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las
|
||||||
if err := blockchain.ExportN(writer, first, last); err != nil {
|
if err := blockchain.ExportN(writer, first, last); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
glog.Infoln("Exported blockchain to ", fn)
|
log.Info("Exported blockchain to", "file", fn)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,17 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"gopkg.in/urfave/cli.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Custom type which is registered in the flags library which cli uses for
|
// Custom type which is registered in the flags library which cli uses for
|
||||||
|
|
@ -47,7 +52,6 @@ type DirectoryFlag struct {
|
||||||
Name string
|
Name string
|
||||||
Value DirectoryString
|
Value DirectoryString
|
||||||
Usage string
|
Usage string
|
||||||
EnvVar string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self DirectoryFlag) String() string {
|
func (self DirectoryFlag) String() string {
|
||||||
|
|
@ -55,7 +59,7 @@ func (self DirectoryFlag) String() string {
|
||||||
if len(self.Value.Value) > 0 {
|
if len(self.Value.Value) > 0 {
|
||||||
fmtString = "%s \"%v\"\t%v"
|
fmtString = "%s \"%v\"\t%v"
|
||||||
}
|
}
|
||||||
return withEnvHint(self.EnvVar, fmt.Sprintf(fmtString, prefixedNames(self.Name), self.Value.Value, self.Usage))
|
return fmt.Sprintf(fmtString, prefixedNames(self.Name), self.Value.Value, self.Usage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func eachName(longName string, fn func(string)) {
|
func eachName(longName string, fn func(string)) {
|
||||||
|
|
@ -69,21 +73,65 @@ func eachName(longName string, fn func(string)) {
|
||||||
// called by cli library, grabs variable from environment (if in env)
|
// called by cli library, grabs variable from environment (if in env)
|
||||||
// and adds variable to flag set for parsing.
|
// and adds variable to flag set for parsing.
|
||||||
func (self DirectoryFlag) Apply(set *flag.FlagSet) {
|
func (self DirectoryFlag) Apply(set *flag.FlagSet) {
|
||||||
if self.EnvVar != "" {
|
|
||||||
for _, envVar := range strings.Split(self.EnvVar, ",") {
|
|
||||||
envVar = strings.TrimSpace(envVar)
|
|
||||||
if envVal := os.Getenv(envVar); envVal != "" {
|
|
||||||
self.Value.Value = envVal
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eachName(self.Name, func(name string) {
|
eachName(self.Name, func(name string) {
|
||||||
set.Var(&self.Value, self.Name, self.Usage)
|
set.Var(&self.Value, self.Name, self.Usage)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BigFlag is a command line flag that accepts 256 bit big integers in decimal or
|
||||||
|
// hexadecimal syntax.
|
||||||
|
type BigFlag struct {
|
||||||
|
Name string
|
||||||
|
Value *big.Int
|
||||||
|
Usage string
|
||||||
|
}
|
||||||
|
|
||||||
|
// bigValue turns *big.Int into a flag.Value
|
||||||
|
type bigValue big.Int
|
||||||
|
|
||||||
|
func (b *bigValue) String() string {
|
||||||
|
if b == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return (*big.Int)(b).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *bigValue) Set(s string) error {
|
||||||
|
int, ok := math.ParseBig256(s)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("invalid integer syntax")
|
||||||
|
}
|
||||||
|
*b = (bigValue)(*int)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f BigFlag) GetName() string {
|
||||||
|
return f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f BigFlag) String() string {
|
||||||
|
fmtString := "%s %v\t%v"
|
||||||
|
if f.Value != nil {
|
||||||
|
fmtString = "%s \"%v\"\t%v"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f BigFlag) Apply(set *flag.FlagSet) {
|
||||||
|
eachName(f.Name, func(name string) {
|
||||||
|
set.Var((*bigValue)(f.Value), f.Name, f.Usage)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GlobalBig returns the value of a BigFlag from the global flag set.
|
||||||
|
func GlobalBig(ctx *cli.Context, name string) *big.Int {
|
||||||
|
val := ctx.GlobalGeneric(name)
|
||||||
|
if val == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return (*big.Int)(val.(*bigValue))
|
||||||
|
}
|
||||||
|
|
||||||
func prefixFor(name string) (prefix string) {
|
func prefixFor(name string) (prefix string) {
|
||||||
if len(name) == 1 {
|
if len(name) == 1 {
|
||||||
prefix = "-"
|
prefix = "-"
|
||||||
|
|
@ -106,14 +154,6 @@ func prefixedNames(fullName string) (prefixed string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func withEnvHint(envVar, str string) string {
|
|
||||||
envText := ""
|
|
||||||
if envVar != "" {
|
|
||||||
envText = fmt.Sprintf(" [$%s]", strings.Join(strings.Split(envVar, ","), ", $"))
|
|
||||||
}
|
|
||||||
return str + envText
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self DirectoryFlag) GetName() string {
|
func (self DirectoryFlag) GetName() string {
|
||||||
return self.Name
|
return self.Name
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,12 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/ethash"
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -41,8 +41,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethstats"
|
"github.com/ethereum/go-ethereum/ethstats"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/les"
|
"github.com/ethereum/go-ethereum/les"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
|
|
@ -115,6 +114,34 @@ var (
|
||||||
Name: "keystore",
|
Name: "keystore",
|
||||||
Usage: "Directory for the keystore (default = inside the datadir)",
|
Usage: "Directory for the keystore (default = inside the datadir)",
|
||||||
}
|
}
|
||||||
|
EthashCacheDirFlag = DirectoryFlag{
|
||||||
|
Name: "ethash.cachedir",
|
||||||
|
Usage: "Directory to store the ethash verification caches (default = inside the datadir)",
|
||||||
|
}
|
||||||
|
EthashCachesInMemoryFlag = cli.IntFlag{
|
||||||
|
Name: "ethash.cachesinmem",
|
||||||
|
Usage: "Number of recent ethash caches to keep in memory (16MB each)",
|
||||||
|
Value: 2,
|
||||||
|
}
|
||||||
|
EthashCachesOnDiskFlag = cli.IntFlag{
|
||||||
|
Name: "ethash.cachesondisk",
|
||||||
|
Usage: "Number of recent ethash caches to keep on disk (16MB each)",
|
||||||
|
Value: 3,
|
||||||
|
}
|
||||||
|
EthashDatasetDirFlag = DirectoryFlag{
|
||||||
|
Name: "ethash.dagdir",
|
||||||
|
Usage: "Directory to store the ethash mining DAGs (default = inside home folder)",
|
||||||
|
}
|
||||||
|
EthashDatasetsInMemoryFlag = cli.IntFlag{
|
||||||
|
Name: "ethash.dagsinmem",
|
||||||
|
Usage: "Number of recent ethash mining DAGs to keep in memory (1+GB each)",
|
||||||
|
Value: 1,
|
||||||
|
}
|
||||||
|
EthashDatasetsOnDiskFlag = cli.IntFlag{
|
||||||
|
Name: "ethash.dagsondisk",
|
||||||
|
Usage: "Number of recent ethash mining DAGs to keep on disk (1+GB each)",
|
||||||
|
Value: 2,
|
||||||
|
}
|
||||||
NetworkIdFlag = cli.IntFlag{
|
NetworkIdFlag = cli.IntFlag{
|
||||||
Name: "networkid",
|
Name: "networkid",
|
||||||
Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten)",
|
Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten)",
|
||||||
|
|
@ -180,24 +207,20 @@ var (
|
||||||
Usage: "Number of CPU threads to use for mining",
|
Usage: "Number of CPU threads to use for mining",
|
||||||
Value: runtime.NumCPU(),
|
Value: runtime.NumCPU(),
|
||||||
}
|
}
|
||||||
TargetGasLimitFlag = cli.StringFlag{
|
TargetGasLimitFlag = cli.Uint64Flag{
|
||||||
Name: "targetgaslimit",
|
Name: "targetgaslimit",
|
||||||
Usage: "Target gas limit sets the artificial target gas floor for the blocks to mine",
|
Usage: "Target gas limit sets the artificial target gas floor for the blocks to mine",
|
||||||
Value: params.GenesisGasLimit.String(),
|
Value: params.GenesisGasLimit.Uint64(),
|
||||||
}
|
|
||||||
AutoDAGFlag = cli.BoolFlag{
|
|
||||||
Name: "autodag",
|
|
||||||
Usage: "Enable automatic DAG pregeneration",
|
|
||||||
}
|
}
|
||||||
EtherbaseFlag = cli.StringFlag{
|
EtherbaseFlag = cli.StringFlag{
|
||||||
Name: "etherbase",
|
Name: "etherbase",
|
||||||
Usage: "Public address for block mining rewards (default = first account created)",
|
Usage: "Public address for block mining rewards (default = first account created)",
|
||||||
Value: "0",
|
Value: "0",
|
||||||
}
|
}
|
||||||
GasPriceFlag = cli.StringFlag{
|
GasPriceFlag = BigFlag{
|
||||||
Name: "gasprice",
|
Name: "gasprice",
|
||||||
Usage: "Minimal gas price to accept for mining a transactions",
|
Usage: "Minimal gas price to accept for mining a transactions",
|
||||||
Value: new(big.Int).Mul(big.NewInt(20), common.Shannon).String(),
|
Value: big.NewInt(20 * params.Shannon),
|
||||||
}
|
}
|
||||||
ExtraDataFlag = cli.StringFlag{
|
ExtraDataFlag = cli.StringFlag{
|
||||||
Name: "extradata",
|
Name: "extradata",
|
||||||
|
|
@ -245,7 +268,10 @@ var (
|
||||||
Name: "fakepow",
|
Name: "fakepow",
|
||||||
Usage: "Disables proof-of-work verification",
|
Usage: "Disables proof-of-work verification",
|
||||||
}
|
}
|
||||||
|
NoCompactionFlag = cli.BoolFlag{
|
||||||
|
Name: "nocompaction",
|
||||||
|
Usage: "Disables db compaction after import",
|
||||||
|
}
|
||||||
// RPC settings
|
// RPC settings
|
||||||
RPCEnabledFlag = cli.BoolFlag{
|
RPCEnabledFlag = cli.BoolFlag{
|
||||||
Name: "rpc",
|
Name: "rpc",
|
||||||
|
|
@ -383,15 +409,15 @@ var (
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gas price oracle settings
|
// Gas price oracle settings
|
||||||
GpoMinGasPriceFlag = cli.StringFlag{
|
GpoMinGasPriceFlag = BigFlag{
|
||||||
Name: "gpomin",
|
Name: "gpomin",
|
||||||
Usage: "Minimum suggested gas price",
|
Usage: "Minimum suggested gas price",
|
||||||
Value: new(big.Int).Mul(big.NewInt(20), common.Shannon).String(),
|
Value: big.NewInt(20 * params.Shannon),
|
||||||
}
|
}
|
||||||
GpoMaxGasPriceFlag = cli.StringFlag{
|
GpoMaxGasPriceFlag = BigFlag{
|
||||||
Name: "gpomax",
|
Name: "gpomax",
|
||||||
Usage: "Maximum suggested gas price",
|
Usage: "Maximum suggested gas price",
|
||||||
Value: new(big.Int).Mul(big.NewInt(500), common.Shannon).String(),
|
Value: big.NewInt(500 * params.Shannon),
|
||||||
}
|
}
|
||||||
GpoFullBlockRatioFlag = cli.IntFlag{
|
GpoFullBlockRatioFlag = cli.IntFlag{
|
||||||
Name: "gpofull",
|
Name: "gpofull",
|
||||||
|
|
@ -430,6 +456,36 @@ func MakeDataDir(ctx *cli.Context) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakeEthashCacheDir returns the directory to use for storing the ethash cache
|
||||||
|
// dumps.
|
||||||
|
func MakeEthashCacheDir(ctx *cli.Context) string {
|
||||||
|
if ctx.GlobalIsSet(EthashCacheDirFlag.Name) && ctx.GlobalString(EthashCacheDirFlag.Name) == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if !ctx.GlobalIsSet(EthashCacheDirFlag.Name) {
|
||||||
|
return "ethash"
|
||||||
|
}
|
||||||
|
return ctx.GlobalString(EthashCacheDirFlag.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeEthashDatasetDir returns the directory to use for storing the full ethash
|
||||||
|
// dataset dumps.
|
||||||
|
func MakeEthashDatasetDir(ctx *cli.Context) string {
|
||||||
|
if !ctx.GlobalIsSet(EthashDatasetDirFlag.Name) {
|
||||||
|
home := os.Getenv("HOME")
|
||||||
|
if home == "" {
|
||||||
|
if user, err := user.Current(); err == nil {
|
||||||
|
home = user.HomeDir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
return filepath.Join(home, "AppData", "Ethash")
|
||||||
|
}
|
||||||
|
return filepath.Join(home, ".ethash")
|
||||||
|
}
|
||||||
|
return ctx.GlobalString(EthashDatasetDirFlag.Name)
|
||||||
|
}
|
||||||
|
|
||||||
// MakeIPCPath creates an IPC path configuration from the set command line flags,
|
// MakeIPCPath creates an IPC path configuration from the set command line flags,
|
||||||
// returning an empty string if IPC was explicitly disabled, or the set path.
|
// returning an empty string if IPC was explicitly disabled, or the set path.
|
||||||
func MakeIPCPath(ctx *cli.Context) string {
|
func MakeIPCPath(ctx *cli.Context) string {
|
||||||
|
|
@ -493,7 +549,7 @@ func MakeBootstrapNodes(ctx *cli.Context) []*discover.Node {
|
||||||
for _, url := range urls {
|
for _, url := range urls {
|
||||||
node, err := discover.ParseNode(url)
|
node, err := discover.ParseNode(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Error).Infof("Bootstrap URL %s: %v\n", url, err)
|
log.Error("Bootstrap URL invalid", "enode", url, "err", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
bootnodes = append(bootnodes, node)
|
bootnodes = append(bootnodes, node)
|
||||||
|
|
@ -513,7 +569,7 @@ func MakeBootstrapNodesV5(ctx *cli.Context) []*discv5.Node {
|
||||||
for _, url := range urls {
|
for _, url := range urls {
|
||||||
node, err := discv5.ParseNode(url)
|
node, err := discv5.ParseNode(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Error).Infof("Bootstrap URL %s: %v\n", url, err)
|
log.Error("Bootstrap URL invalid", "enode", url, "err", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
bootnodes = append(bootnodes, node)
|
bootnodes = append(bootnodes, node)
|
||||||
|
|
@ -610,7 +666,7 @@ func MakeAddress(ks *keystore.KeyStore, account string) (accounts.Account, error
|
||||||
func MakeEtherbase(ks *keystore.KeyStore, ctx *cli.Context) common.Address {
|
func MakeEtherbase(ks *keystore.KeyStore, ctx *cli.Context) common.Address {
|
||||||
accounts := ks.Accounts()
|
accounts := ks.Accounts()
|
||||||
if !ctx.GlobalIsSet(EtherbaseFlag.Name) && len(accounts) == 0 {
|
if !ctx.GlobalIsSet(EtherbaseFlag.Name) && len(accounts) == 0 {
|
||||||
glog.V(logger.Error).Infoln("WARNING: No etherbase set and no accounts found as default")
|
log.Warn("No etherbase set and no accounts found as default")
|
||||||
return common.Address{}
|
return common.Address{}
|
||||||
}
|
}
|
||||||
etherbase := ctx.GlobalString(EtherbaseFlag.Name)
|
etherbase := ctx.GlobalString(EtherbaseFlag.Name)
|
||||||
|
|
@ -730,7 +786,6 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
|
||||||
|
|
||||||
ethConf := ð.Config{
|
ethConf := ð.Config{
|
||||||
Etherbase: MakeEtherbase(ks, ctx),
|
Etherbase: MakeEtherbase(ks, ctx),
|
||||||
ChainConfig: MakeChainConfig(ctx, stack),
|
|
||||||
FastSync: ctx.GlobalBool(FastSyncFlag.Name),
|
FastSync: ctx.GlobalBool(FastSyncFlag.Name),
|
||||||
LightMode: ctx.GlobalBool(LightModeFlag.Name),
|
LightMode: ctx.GlobalBool(LightModeFlag.Name),
|
||||||
LightServ: ctx.GlobalInt(LightServFlag.Name),
|
LightServ: ctx.GlobalInt(LightServFlag.Name),
|
||||||
|
|
@ -742,15 +797,20 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
|
||||||
MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name),
|
MinerThreads: ctx.GlobalInt(MinerThreadsFlag.Name),
|
||||||
ExtraData: MakeMinerExtra(extra, ctx),
|
ExtraData: MakeMinerExtra(extra, ctx),
|
||||||
DocRoot: ctx.GlobalString(DocRootFlag.Name),
|
DocRoot: ctx.GlobalString(DocRootFlag.Name),
|
||||||
GasPrice: common.String2Big(ctx.GlobalString(GasPriceFlag.Name)),
|
GasPrice: GlobalBig(ctx, GasPriceFlag.Name),
|
||||||
GpoMinGasPrice: common.String2Big(ctx.GlobalString(GpoMinGasPriceFlag.Name)),
|
GpoMinGasPrice: GlobalBig(ctx, GpoMinGasPriceFlag.Name),
|
||||||
GpoMaxGasPrice: common.String2Big(ctx.GlobalString(GpoMaxGasPriceFlag.Name)),
|
GpoMaxGasPrice: GlobalBig(ctx, GpoMaxGasPriceFlag.Name),
|
||||||
GpoFullBlockRatio: ctx.GlobalInt(GpoFullBlockRatioFlag.Name),
|
GpoFullBlockRatio: ctx.GlobalInt(GpoFullBlockRatioFlag.Name),
|
||||||
GpobaseStepDown: ctx.GlobalInt(GpobaseStepDownFlag.Name),
|
GpobaseStepDown: ctx.GlobalInt(GpobaseStepDownFlag.Name),
|
||||||
GpobaseStepUp: ctx.GlobalInt(GpobaseStepUpFlag.Name),
|
GpobaseStepUp: ctx.GlobalInt(GpobaseStepUpFlag.Name),
|
||||||
GpobaseCorrectionFactor: ctx.GlobalInt(GpobaseCorrectionFactorFlag.Name),
|
GpobaseCorrectionFactor: ctx.GlobalInt(GpobaseCorrectionFactorFlag.Name),
|
||||||
SolcPath: ctx.GlobalString(SolcPathFlag.Name),
|
SolcPath: ctx.GlobalString(SolcPathFlag.Name),
|
||||||
AutoDAG: ctx.GlobalBool(AutoDAGFlag.Name) || ctx.GlobalBool(MiningEnabledFlag.Name),
|
EthashCacheDir: MakeEthashCacheDir(ctx),
|
||||||
|
EthashCachesInMem: ctx.GlobalInt(EthashCachesInMemoryFlag.Name),
|
||||||
|
EthashCachesOnDisk: ctx.GlobalInt(EthashCachesOnDiskFlag.Name),
|
||||||
|
EthashDatasetDir: MakeEthashDatasetDir(ctx),
|
||||||
|
EthashDatasetsInMem: ctx.GlobalInt(EthashDatasetsInMemoryFlag.Name),
|
||||||
|
EthashDatasetsOnDisk: ctx.GlobalInt(EthashDatasetsOnDiskFlag.Name),
|
||||||
EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name),
|
EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -761,7 +821,6 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
|
||||||
ethConf.NetworkId = 3
|
ethConf.NetworkId = 3
|
||||||
}
|
}
|
||||||
ethConf.Genesis = core.DefaultTestnetGenesisBlock()
|
ethConf.Genesis = core.DefaultTestnetGenesisBlock()
|
||||||
|
|
||||||
case ctx.GlobalBool(DevModeFlag.Name):
|
case ctx.GlobalBool(DevModeFlag.Name):
|
||||||
ethConf.Genesis = core.DevGenesisBlock()
|
ethConf.Genesis = core.DevGenesisBlock()
|
||||||
if !ctx.GlobalIsSet(GasPriceFlag.Name) {
|
if !ctx.GlobalIsSet(GasPriceFlag.Name) {
|
||||||
|
|
@ -820,66 +879,7 @@ func RegisterEthStatsService(stack *node.Node, url string) {
|
||||||
|
|
||||||
// SetupNetwork configures the system for either the main net or some test network.
|
// SetupNetwork configures the system for either the main net or some test network.
|
||||||
func SetupNetwork(ctx *cli.Context) {
|
func SetupNetwork(ctx *cli.Context) {
|
||||||
params.TargetGasLimit = common.String2Big(ctx.GlobalString(TargetGasLimitFlag.Name))
|
params.TargetGasLimit = new(big.Int).SetUint64(ctx.GlobalUint64(TargetGasLimitFlag.Name))
|
||||||
}
|
|
||||||
|
|
||||||
// MakeChainConfig reads the chain configuration from the database in ctx.Datadir.
|
|
||||||
func MakeChainConfig(ctx *cli.Context, stack *node.Node) *params.ChainConfig {
|
|
||||||
db := MakeChainDatabase(ctx, stack)
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
return MakeChainConfigFromDb(ctx, db)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeChainConfigFromDb reads the chain configuration from the given database.
|
|
||||||
func MakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *params.ChainConfig {
|
|
||||||
// If the chain is already initialized, use any existing chain configs
|
|
||||||
config := new(params.ChainConfig)
|
|
||||||
|
|
||||||
genesis := core.GetBlock(db, core.GetCanonicalHash(db, 0), 0)
|
|
||||||
if genesis != nil {
|
|
||||||
storedConfig, err := core.GetChainConfig(db, genesis.Hash())
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
config = storedConfig
|
|
||||||
case core.ChainConfigNotFoundErr:
|
|
||||||
// No configs found, use empty, will populate below
|
|
||||||
default:
|
|
||||||
Fatalf("Could not make chain configuration: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// set chain id in case it's zero.
|
|
||||||
if config.ChainId == nil {
|
|
||||||
config.ChainId = new(big.Int)
|
|
||||||
}
|
|
||||||
// Check whether we are allowed to set default config params or not:
|
|
||||||
// - If no genesis is set, we're running either mainnet or testnet (private nets use `geth init`)
|
|
||||||
// - If a genesis is already set, ensure we have a configuration for it (mainnet or testnet)
|
|
||||||
defaults := genesis == nil ||
|
|
||||||
(genesis.Hash() == params.MainNetGenesisHash && !ctx.GlobalBool(TestNetFlag.Name)) ||
|
|
||||||
(genesis.Hash() == params.TestNetGenesisHash && ctx.GlobalBool(TestNetFlag.Name))
|
|
||||||
|
|
||||||
if defaults {
|
|
||||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
|
||||||
config = params.TestnetChainConfig
|
|
||||||
} else {
|
|
||||||
// Homestead fork
|
|
||||||
config.HomesteadBlock = params.MainNetHomesteadBlock
|
|
||||||
// DAO fork
|
|
||||||
config.DAOForkBlock = params.MainNetDAOForkBlock
|
|
||||||
config.DAOForkSupport = true
|
|
||||||
|
|
||||||
// DoS reprice fork
|
|
||||||
config.EIP150Block = params.MainNetHomesteadGasRepriceBlock
|
|
||||||
config.EIP150Hash = params.MainNetHomesteadGasRepriceHash
|
|
||||||
|
|
||||||
// DoS state cleanup fork
|
|
||||||
config.EIP155Block = params.MainNetSpuriousDragon
|
|
||||||
config.EIP158Block = params.MainNetSpuriousDragon
|
|
||||||
config.ChainId = params.MainNetChainID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ChainDbName(ctx *cli.Context) string {
|
func ChainDbName(ctx *cli.Context) string {
|
||||||
|
|
@ -905,27 +905,34 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
|
||||||
return chainDb
|
return chainDb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MakeGenesis(ctx *cli.Context) *core.Genesis {
|
||||||
|
var genesis *core.Genesis
|
||||||
|
switch {
|
||||||
|
case ctx.GlobalBool(TestNetFlag.Name):
|
||||||
|
genesis = core.DefaultTestnetGenesisBlock()
|
||||||
|
case ctx.GlobalBool(DevModeFlag.Name):
|
||||||
|
genesis = core.DevGenesisBlock()
|
||||||
|
}
|
||||||
|
return genesis
|
||||||
|
}
|
||||||
|
|
||||||
// MakeChain creates a chain manager from set command line flags.
|
// MakeChain creates a chain manager from set command line flags.
|
||||||
func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chainDb ethdb.Database) {
|
func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chainDb ethdb.Database) {
|
||||||
var err error
|
var err error
|
||||||
chainDb = MakeChainDatabase(ctx, stack)
|
chainDb = MakeChainDatabase(ctx, stack)
|
||||||
|
|
||||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
seal := pow.PoW(pow.FakePow{})
|
||||||
_, err := core.WriteTestNetGenesisBlock(chainDb)
|
|
||||||
if err != nil {
|
|
||||||
glog.Fatalln(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
chainConfig := MakeChainConfigFromDb(ctx, chainDb)
|
|
||||||
|
|
||||||
pow := pow.PoW(core.FakePow{})
|
|
||||||
if !ctx.GlobalBool(FakePoWFlag.Name) {
|
if !ctx.GlobalBool(FakePoWFlag.Name) {
|
||||||
pow = ethash.New()
|
seal = pow.NewFullEthash("", 1, 0, "", 1, 0)
|
||||||
}
|
}
|
||||||
chain, err = core.NewBlockChain(chainDb, chainConfig, pow, new(event.TypeMux), vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)})
|
config, _, err := core.SetupGenesisBlock(chainDb, MakeGenesis(ctx))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Fatalf("Could not start chainmanager: %v", err)
|
Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)}
|
||||||
|
chain, err = core.NewBlockChain(chainDb, config, seal, new(event.TypeMux), vmcfg)
|
||||||
|
if err != nil {
|
||||||
|
Fatalf("Can't create BlockChain: %v", err)
|
||||||
}
|
}
|
||||||
return chain, chainDb
|
return chain, chainDb
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/sha1"
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
|
@ -38,8 +36,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/console"
|
"github.com/ethereum/go-ethereum/console"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||||
|
|
@ -49,6 +46,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const quitCommand = "~Q"
|
const quitCommand = "~Q"
|
||||||
|
const symKeyName = "da919ea33001b04dfc630522e33078ec0df11"
|
||||||
|
|
||||||
// singletons
|
// singletons
|
||||||
var (
|
var (
|
||||||
|
|
@ -67,7 +65,8 @@ var (
|
||||||
asymKey *ecdsa.PrivateKey
|
asymKey *ecdsa.PrivateKey
|
||||||
nodeid *ecdsa.PrivateKey
|
nodeid *ecdsa.PrivateKey
|
||||||
topic whisper.TopicType
|
topic whisper.TopicType
|
||||||
filterID uint32
|
filterID string
|
||||||
|
symPass string
|
||||||
msPassword string
|
msPassword string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -82,13 +81,13 @@ var (
|
||||||
testMode = flag.Bool("t", false, "use of predefined parameters for diagnostics")
|
testMode = flag.Bool("t", false, "use of predefined parameters for diagnostics")
|
||||||
generateKey = flag.Bool("k", false, "generate and show the private key")
|
generateKey = flag.Bool("k", false, "generate and show the private key")
|
||||||
|
|
||||||
|
argVerbosity = flag.Int("verbosity", int(log.LvlWarn), "log verbosity level")
|
||||||
argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds")
|
argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds")
|
||||||
argWorkTime = flag.Uint("work", 5, "work time in seconds")
|
argWorkTime = flag.Uint("work", 5, "work time in seconds")
|
||||||
argPoW = flag.Float64("pow", whisper.MinimumPoW, "PoW for normal messages in float format (e.g. 2.7)")
|
argPoW = flag.Float64("pow", whisper.MinimumPoW, "PoW for normal messages in float format (e.g. 2.7)")
|
||||||
argServerPoW = flag.Float64("mspow", whisper.MinimumPoW, "PoW requirement for Mail Server request")
|
argServerPoW = flag.Float64("mspow", whisper.MinimumPoW, "PoW requirement for Mail Server request")
|
||||||
|
|
||||||
argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)")
|
argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)")
|
||||||
argSalt = flag.String("salt", "", "salt (for topic and key derivation)")
|
|
||||||
argPub = flag.String("pub", "", "public key for asymmetric encryption")
|
argPub = flag.String("pub", "", "public key for asymmetric encryption")
|
||||||
argDBPath = flag.String("dbpath", "", "path to the server's DB directory")
|
argDBPath = flag.String("dbpath", "", "path to the server's DB directory")
|
||||||
argIDFile = flag.String("idfile", "", "file name with node id (private key)")
|
argIDFile = flag.String("idfile", "", "file name with node id (private key)")
|
||||||
|
|
@ -146,7 +145,6 @@ func echo() {
|
||||||
fmt.Printf("pow = %f \n", *argPoW)
|
fmt.Printf("pow = %f \n", *argPoW)
|
||||||
fmt.Printf("mspow = %f \n", *argServerPoW)
|
fmt.Printf("mspow = %f \n", *argServerPoW)
|
||||||
fmt.Printf("ip = %s \n", *argIP)
|
fmt.Printf("ip = %s \n", *argIP)
|
||||||
fmt.Printf("salt = %s \n", *argSalt)
|
|
||||||
fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub)))
|
fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub)))
|
||||||
fmt.Printf("idfile = %s \n", *argIDFile)
|
fmt.Printf("idfile = %s \n", *argIDFile)
|
||||||
fmt.Printf("dbpath = %s \n", *argDBPath)
|
fmt.Printf("dbpath = %s \n", *argDBPath)
|
||||||
|
|
@ -154,8 +152,7 @@ func echo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func initialize() {
|
func initialize() {
|
||||||
glog.SetV(logger.Warn)
|
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*argVerbosity), log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
|
||||||
glog.SetToStderr(true)
|
|
||||||
|
|
||||||
done = make(chan struct{})
|
done = make(chan struct{})
|
||||||
var peers []*discover.Node
|
var peers []*discover.Node
|
||||||
|
|
@ -172,10 +169,7 @@ func initialize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if *testMode {
|
if *testMode {
|
||||||
password := []byte("test password for symmetric encryption")
|
symPass = "wwww" // ascii code: 0x77777777
|
||||||
salt := []byte("test salt for symmetric encryption")
|
|
||||||
symKey = pbkdf2.Key(password, salt, 64, 32, sha256.New)
|
|
||||||
topic = whisper.TopicType{0xFF, 0xFF, 0xFF, 0xFF}
|
|
||||||
msPassword = "mail server test password"
|
msPassword = "mail server test password"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,10 +192,11 @@ func initialize() {
|
||||||
utils.Fatalf("Failed to read Mail Server password: %s", err)
|
utils.Fatalf("Failed to read Mail Server password: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
shh = whisper.NewWhisper(&mailServer)
|
shh = whisper.New()
|
||||||
|
shh.RegisterServer(&mailServer)
|
||||||
mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW)
|
mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW)
|
||||||
} else {
|
} else {
|
||||||
shh = whisper.NewWhisper(nil)
|
shh = whisper.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
asymKey = shh.NewIdentity()
|
asymKey = shh.NewIdentity()
|
||||||
|
|
@ -285,20 +280,18 @@ func configureNode() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*asymmetricMode && !*forwarderMode && !*testMode {
|
if !*asymmetricMode && !*forwarderMode {
|
||||||
pass, err := console.Stdin.PromptPassword("Please enter the password: ")
|
if len(symPass) == 0 {
|
||||||
|
symPass, err = console.Stdin.PromptPassword("Please enter the password: ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to read passphrase: %v", err)
|
utils.Fatalf("Failed to read passphrase: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(*argSalt) == 0 {
|
|
||||||
argSalt = scanLineA("Please enter the salt: ")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
symKey = pbkdf2.Key([]byte(pass), []byte(*argSalt), 65356, 32, sha256.New)
|
shh.AddSymKey(symKeyName, []byte(symPass))
|
||||||
|
symKey = shh.GetSymKey(symKeyName)
|
||||||
if len(*argTopic) == 0 {
|
if len(*argTopic) == 0 {
|
||||||
generateTopic([]byte(pass), []byte(*argSalt))
|
generateTopic([]byte(symPass))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -314,19 +307,17 @@ func configureNode() {
|
||||||
Topics: []whisper.TopicType{topic},
|
Topics: []whisper.TopicType{topic},
|
||||||
AcceptP2P: p2pAccept,
|
AcceptP2P: p2pAccept,
|
||||||
}
|
}
|
||||||
filterID = shh.Watch(&filter)
|
filterID, err = shh.Watch(&filter)
|
||||||
|
if err != nil {
|
||||||
|
utils.Fatalf("Failed to install filter: %s", err)
|
||||||
|
}
|
||||||
fmt.Printf("Filter is configured for the topic: %x \n", topic)
|
fmt.Printf("Filter is configured for the topic: %x \n", topic)
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateTopic(password, salt []byte) {
|
func generateTopic(password []byte) {
|
||||||
const rounds = 4000
|
x := pbkdf2.Key(password, password, 8196, 128, sha512.New)
|
||||||
const size = 128
|
for i := 0; i < len(x); i++ {
|
||||||
x1 := pbkdf2.Key(password, salt, rounds, size, sha512.New)
|
topic[i%whisper.TopicLength] ^= x[i]
|
||||||
x2 := pbkdf2.Key(password, salt, rounds, size, sha1.New)
|
|
||||||
x3 := pbkdf2.Key(x1, x2, rounds, size, sha256.New)
|
|
||||||
|
|
||||||
for i := 0; i < size; i++ {
|
|
||||||
topic[i%whisper.TopicLength] ^= x3[i]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -378,9 +369,9 @@ func sendLoop() {
|
||||||
if *asymmetricMode {
|
if *asymmetricMode {
|
||||||
// print your own message for convenience,
|
// print your own message for convenience,
|
||||||
// because in asymmetric mode it is impossible to decrypt it
|
// because in asymmetric mode it is impossible to decrypt it
|
||||||
hour, min, sec := time.Now().Clock()
|
timestamp := time.Now().Unix()
|
||||||
from := crypto.PubkeyToAddress(asymKey.PublicKey)
|
from := crypto.PubkeyToAddress(asymKey.PublicKey)
|
||||||
fmt.Printf("\n%02d:%02d:%02d <%x>: %s\n", hour, min, sec, from, s)
|
fmt.Printf("\n%d <%x>: %s\n", timestamp, from, s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
common/.gitignore
vendored
12
common/.gitignore
vendored
|
|
@ -1,12 +0,0 @@
|
||||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
|
||||||
#
|
|
||||||
# If you find yourself ignoring temporary files generated by your text editor
|
|
||||||
# or operating system, you probably want to add a global ignore instead:
|
|
||||||
# git config --global core.excludesfile ~/.gitignore_global
|
|
||||||
|
|
||||||
/tmp
|
|
||||||
*/**/*un~
|
|
||||||
*un~
|
|
||||||
.DS_Store
|
|
||||||
*/**/.DS_Store
|
|
||||||
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
language: go
|
|
||||||
go:
|
|
||||||
- 1.2
|
|
||||||
140
common/README.md
140
common/README.md
|
|
@ -1,140 +0,0 @@
|
||||||
# common
|
|
||||||
|
|
||||||
[](https://travis-ci.org/ethereum/go-ethereum)
|
|
||||||
|
|
||||||
The common package contains the ethereum utility library.
|
|
||||||
|
|
||||||
# Installation
|
|
||||||
|
|
||||||
As a subdirectory the main go-ethereum repository, you get it with
|
|
||||||
`go get github.com/ethereum/go-ethereum`.
|
|
||||||
|
|
||||||
# Usage
|
|
||||||
|
|
||||||
## RLP (Recursive Linear Prefix) Encoding
|
|
||||||
|
|
||||||
RLP Encoding is an encoding scheme used by the Ethereum project. It
|
|
||||||
encodes any native value or list to a string.
|
|
||||||
|
|
||||||
More in depth information about the encoding scheme see the
|
|
||||||
[Wiki](http://wiki.ethereum.org/index.php/RLP) article.
|
|
||||||
|
|
||||||
```go
|
|
||||||
rlp := common.Encode("doge")
|
|
||||||
fmt.Printf("%q\n", rlp) // => "\0x83dog"
|
|
||||||
|
|
||||||
rlp = common.Encode([]interface{}{"dog", "cat"})
|
|
||||||
fmt.Printf("%q\n", rlp) // => "\0xc8\0x83dog\0x83cat"
|
|
||||||
decoded := common.Decode(rlp)
|
|
||||||
fmt.Println(decoded) // => ["dog" "cat"]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Patricia Trie
|
|
||||||
|
|
||||||
Patricie Tree is a merkle trie used by the Ethereum project.
|
|
||||||
|
|
||||||
More in depth information about the (modified) Patricia Trie can be
|
|
||||||
found on the [Wiki](http://wiki.ethereum.org/index.php/Patricia_Tree).
|
|
||||||
|
|
||||||
The patricia trie uses a db as backend and could be anything as long as
|
|
||||||
it satisfies the Database interface found in `common/db.go`.
|
|
||||||
|
|
||||||
```go
|
|
||||||
db := NewDatabase()
|
|
||||||
|
|
||||||
// db, root
|
|
||||||
trie := common.NewTrie(db, "")
|
|
||||||
|
|
||||||
trie.Put("puppy", "dog")
|
|
||||||
trie.Put("horse", "stallion")
|
|
||||||
trie.Put("do", "verb")
|
|
||||||
trie.Put("doge", "coin")
|
|
||||||
|
|
||||||
// Look up the key "do" in the trie
|
|
||||||
out := trie.Get("do")
|
|
||||||
fmt.Println(out) // => verb
|
|
||||||
|
|
||||||
trie.Delete("puppy")
|
|
||||||
```
|
|
||||||
|
|
||||||
The patricia trie, in combination with RLP, provides a robust,
|
|
||||||
cryptographically authenticated data structure that can be used to store
|
|
||||||
all (key, value) bindings.
|
|
||||||
|
|
||||||
```go
|
|
||||||
// ... Create db/trie
|
|
||||||
|
|
||||||
// Note that RLP uses interface slices as list
|
|
||||||
value := common.Encode([]interface{}{"one", 2, "three", []interface{}{42}})
|
|
||||||
// Store the RLP encoded value of the list
|
|
||||||
trie.Put("mykey", value)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Value
|
|
||||||
|
|
||||||
Value is a Generic Value which is used in combination with RLP data or
|
|
||||||
`([])interface{}` structures. It may serve as a bridge between RLP data
|
|
||||||
and actual real values and takes care of all the type checking and
|
|
||||||
casting. Unlike Go's `reflect.Value` it does not panic if it's unable to
|
|
||||||
cast to the requested value. It simple returns the base value of that
|
|
||||||
type (e.g. `Slice()` returns []interface{}, `Uint()` return 0, etc).
|
|
||||||
|
|
||||||
### Creating a new Value
|
|
||||||
|
|
||||||
`NewEmptyValue()` returns a new \*Value with it's initial value set to a
|
|
||||||
`[]interface{}`
|
|
||||||
|
|
||||||
`AppendList()` appends a list to the current value.
|
|
||||||
|
|
||||||
`Append(v)` appends the value (v) to the current value/list.
|
|
||||||
|
|
||||||
```go
|
|
||||||
val := common.NewEmptyValue().Append(1).Append("2")
|
|
||||||
val.AppendList().Append(3)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Retrieving values
|
|
||||||
|
|
||||||
`Get(i)` returns the `i` item in the list.
|
|
||||||
|
|
||||||
`Uint()` returns the value as an unsigned int64.
|
|
||||||
|
|
||||||
`Slice()` returns the value as a interface slice.
|
|
||||||
|
|
||||||
`Str()` returns the value as a string.
|
|
||||||
|
|
||||||
`Bytes()` returns the value as a byte slice.
|
|
||||||
|
|
||||||
`Len()` assumes current to be a slice and returns its length.
|
|
||||||
|
|
||||||
`Byte()` returns the value as a single byte.
|
|
||||||
|
|
||||||
```go
|
|
||||||
val := common.NewValue([]interface{}{1,"2",[]interface{}{3}})
|
|
||||||
val.Get(0).Uint() // => 1
|
|
||||||
val.Get(1).Str() // => "2"
|
|
||||||
s := val.Get(2) // => Value([]interface{}{3})
|
|
||||||
s.Get(0).Uint() // => 3
|
|
||||||
```
|
|
||||||
|
|
||||||
## Decoding
|
|
||||||
|
|
||||||
Decoding streams of RLP data is simplified
|
|
||||||
|
|
||||||
```go
|
|
||||||
val := common.NewValueFromBytes(rlpData)
|
|
||||||
val.Get(0).Uint()
|
|
||||||
```
|
|
||||||
|
|
||||||
## Encoding
|
|
||||||
|
|
||||||
Encoding from Value to RLP is done with the `Encode` method. The
|
|
||||||
underlying value can be anything RLP can encode (int, str, lists, bytes)
|
|
||||||
|
|
||||||
```go
|
|
||||||
val := common.NewValue([]interface{}{1,"2",[]interface{}{3}})
|
|
||||||
rlp := val.Encode()
|
|
||||||
// Store the rlp data
|
|
||||||
Store(rlp)
|
|
||||||
```
|
|
||||||
126
common/big.go
126
common/big.go
|
|
@ -24,133 +24,7 @@ var (
|
||||||
Big2 = big.NewInt(2)
|
Big2 = big.NewInt(2)
|
||||||
Big3 = big.NewInt(3)
|
Big3 = big.NewInt(3)
|
||||||
Big0 = big.NewInt(0)
|
Big0 = big.NewInt(0)
|
||||||
BigTrue = Big1
|
|
||||||
BigFalse = Big0
|
|
||||||
Big32 = big.NewInt(32)
|
Big32 = big.NewInt(32)
|
||||||
Big36 = big.NewInt(36)
|
|
||||||
Big97 = big.NewInt(97)
|
|
||||||
Big98 = big.NewInt(98)
|
|
||||||
Big256 = big.NewInt(0xff)
|
Big256 = big.NewInt(0xff)
|
||||||
Big257 = big.NewInt(257)
|
Big257 = big.NewInt(257)
|
||||||
MaxBig = String2Big("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Big pow
|
|
||||||
//
|
|
||||||
// Returns the power of two big integers
|
|
||||||
func BigPow(a, b int) *big.Int {
|
|
||||||
c := new(big.Int)
|
|
||||||
c.Exp(big.NewInt(int64(a)), big.NewInt(int64(b)), big.NewInt(0))
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// Big
|
|
||||||
//
|
|
||||||
// Shortcut for new(big.Int).SetString(..., 0)
|
|
||||||
func Big(num string) *big.Int {
|
|
||||||
n := new(big.Int)
|
|
||||||
n.SetString(num, 0)
|
|
||||||
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes2Big
|
|
||||||
//
|
|
||||||
func BytesToBig(data []byte) *big.Int {
|
|
||||||
n := new(big.Int)
|
|
||||||
n.SetBytes(data)
|
|
||||||
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
func Bytes2Big(data []byte) *big.Int { return BytesToBig(data) }
|
|
||||||
func BigD(data []byte) *big.Int { return BytesToBig(data) }
|
|
||||||
|
|
||||||
func String2Big(num string) *big.Int {
|
|
||||||
n := new(big.Int)
|
|
||||||
n.SetString(num, 0)
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func BitTest(num *big.Int, i int) bool {
|
|
||||||
return num.Bit(i) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// To256
|
|
||||||
//
|
|
||||||
// "cast" the big int to a 256 big int (i.e., limit to)
|
|
||||||
var tt256 = new(big.Int).Lsh(big.NewInt(1), 256)
|
|
||||||
var tt256m1 = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(1))
|
|
||||||
var tt255 = new(big.Int).Lsh(big.NewInt(1), 255)
|
|
||||||
|
|
||||||
func U256(x *big.Int) *big.Int {
|
|
||||||
//if x.Cmp(Big0) < 0 {
|
|
||||||
// return new(big.Int).Add(tt256, x)
|
|
||||||
// }
|
|
||||||
|
|
||||||
x.And(x, tt256m1)
|
|
||||||
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
func S256(x *big.Int) *big.Int {
|
|
||||||
if x.Cmp(tt255) < 0 {
|
|
||||||
return x
|
|
||||||
} else {
|
|
||||||
// We don't want to modify x, ever
|
|
||||||
return new(big.Int).Sub(x, tt256)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func FirstBitSet(v *big.Int) int {
|
|
||||||
for i := 0; i < v.BitLen(); i++ {
|
|
||||||
if v.Bit(i) > 0 {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return v.BitLen()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Big to bytes
|
|
||||||
//
|
|
||||||
// Returns the bytes of a big integer with the size specified by **base**
|
|
||||||
// Attempts to pad the byte array with zeros.
|
|
||||||
func BigToBytes(num *big.Int, base int) []byte {
|
|
||||||
ret := make([]byte, base/8)
|
|
||||||
|
|
||||||
if len(num.Bytes()) > base/8 {
|
|
||||||
return num.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
return append(ret[:len(ret)-len(num.Bytes())], num.Bytes()...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Big copy
|
|
||||||
//
|
|
||||||
// Creates a copy of the given big integer
|
|
||||||
func BigCopy(src *big.Int) *big.Int {
|
|
||||||
return new(big.Int).Set(src)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Big max
|
|
||||||
//
|
|
||||||
// Returns the maximum size big integer
|
|
||||||
func BigMax(x, y *big.Int) *big.Int {
|
|
||||||
if x.Cmp(y) < 0 {
|
|
||||||
return y
|
|
||||||
}
|
|
||||||
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
||||||
// Big min
|
|
||||||
//
|
|
||||||
// Returns the minimum size big integer
|
|
||||||
func BigMin(x, y *big.Int) *big.Int {
|
|
||||||
if x.Cmp(y) > 0 {
|
|
||||||
return y
|
|
||||||
}
|
|
||||||
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
// Copyright 2014 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMisc(t *testing.T) {
|
|
||||||
a := Big("10")
|
|
||||||
b := Big("57896044618658097711785492504343953926634992332820282019728792003956564819968")
|
|
||||||
c := []byte{1, 2, 3, 4}
|
|
||||||
z := BitTest(a, 1)
|
|
||||||
|
|
||||||
if !z {
|
|
||||||
t.Error("Expected true got", z)
|
|
||||||
}
|
|
||||||
|
|
||||||
U256(a)
|
|
||||||
S256(a)
|
|
||||||
|
|
||||||
U256(b)
|
|
||||||
S256(b)
|
|
||||||
|
|
||||||
BigD(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBigMax(t *testing.T) {
|
|
||||||
a := Big("10")
|
|
||||||
b := Big("5")
|
|
||||||
|
|
||||||
max1 := BigMax(a, b)
|
|
||||||
if max1 != a {
|
|
||||||
t.Errorf("Expected %d got %d", a, max1)
|
|
||||||
}
|
|
||||||
|
|
||||||
max2 := BigMax(b, a)
|
|
||||||
if max2 != a {
|
|
||||||
t.Errorf("Expected %d got %d", a, max2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBigMin(t *testing.T) {
|
|
||||||
a := Big("10")
|
|
||||||
b := Big("5")
|
|
||||||
|
|
||||||
min1 := BigMin(a, b)
|
|
||||||
if min1 != b {
|
|
||||||
t.Errorf("Expected %d got %d", b, min1)
|
|
||||||
}
|
|
||||||
|
|
||||||
min2 := BigMin(b, a)
|
|
||||||
if min2 != b {
|
|
||||||
t.Errorf("Expected %d got %d", b, min2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBigCopy(t *testing.T) {
|
|
||||||
a := Big("10")
|
|
||||||
b := BigCopy(a)
|
|
||||||
c := Big("1000000000000")
|
|
||||||
y := BigToBytes(b, 16)
|
|
||||||
ybytes := []byte{0, 10}
|
|
||||||
z := BigToBytes(c, 16)
|
|
||||||
zbytes := []byte{232, 212, 165, 16, 0}
|
|
||||||
|
|
||||||
if !bytes.Equal(y, ybytes) {
|
|
||||||
t.Error("Got", ybytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Equal(z, zbytes) {
|
|
||||||
t.Error("Got", zbytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
155
common/bytes.go
155
common/bytes.go
|
|
@ -18,12 +18,7 @@
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ToHex(b []byte) string {
|
func ToHex(b []byte) string {
|
||||||
|
|
@ -48,65 +43,6 @@ func FromHex(s string) []byte {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Number to bytes
|
|
||||||
//
|
|
||||||
// Returns the number in bytes with the specified base
|
|
||||||
func NumberToBytes(num interface{}, bits int) []byte {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
err := binary.Write(buf, binary.BigEndian, num)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("NumberToBytes failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.Bytes()[buf.Len()-(bits/8):]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bytes to number
|
|
||||||
//
|
|
||||||
// Attempts to cast a byte slice to a unsigned integer
|
|
||||||
func BytesToNumber(b []byte) uint64 {
|
|
||||||
var number uint64
|
|
||||||
|
|
||||||
// Make sure the buffer is 64bits
|
|
||||||
data := make([]byte, 8)
|
|
||||||
data = append(data[:len(b)], b...)
|
|
||||||
|
|
||||||
buf := bytes.NewReader(data)
|
|
||||||
err := binary.Read(buf, binary.BigEndian, &number)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("BytesToNumber failed:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return number
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read variable int
|
|
||||||
//
|
|
||||||
// Read a variable length number in big endian byte order
|
|
||||||
func ReadVarInt(buff []byte) (ret uint64) {
|
|
||||||
switch l := len(buff); {
|
|
||||||
case l > 4:
|
|
||||||
d := LeftPadBytes(buff, 8)
|
|
||||||
binary.Read(bytes.NewReader(d), binary.BigEndian, &ret)
|
|
||||||
case l > 2:
|
|
||||||
var num uint32
|
|
||||||
d := LeftPadBytes(buff, 4)
|
|
||||||
binary.Read(bytes.NewReader(d), binary.BigEndian, &num)
|
|
||||||
ret = uint64(num)
|
|
||||||
case l > 1:
|
|
||||||
var num uint16
|
|
||||||
d := LeftPadBytes(buff, 2)
|
|
||||||
binary.Read(bytes.NewReader(d), binary.BigEndian, &num)
|
|
||||||
ret = uint64(num)
|
|
||||||
default:
|
|
||||||
var num uint8
|
|
||||||
binary.Read(bytes.NewReader(buff), binary.BigEndian, &num)
|
|
||||||
ret = uint64(num)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy bytes
|
// Copy bytes
|
||||||
//
|
//
|
||||||
// Returns an exact copy of the provided bytes
|
// Returns an exact copy of the provided bytes
|
||||||
|
|
@ -152,53 +88,6 @@ func Hex2BytesFixed(str string, flen int) []byte {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func StringToByteFunc(str string, cb func(str string) []byte) (ret []byte) {
|
|
||||||
if len(str) > 1 && str[0:2] == "0x" && !strings.Contains(str, "\n") {
|
|
||||||
ret = Hex2Bytes(str[2:])
|
|
||||||
} else {
|
|
||||||
ret = cb(str)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func FormatData(data string) []byte {
|
|
||||||
if len(data) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// Simple stupid
|
|
||||||
d := new(big.Int)
|
|
||||||
if data[0:1] == "\"" && data[len(data)-1:] == "\"" {
|
|
||||||
return RightPadBytes([]byte(data[1:len(data)-1]), 32)
|
|
||||||
} else if len(data) > 1 && data[:2] == "0x" {
|
|
||||||
d.SetBytes(Hex2Bytes(data[2:]))
|
|
||||||
} else {
|
|
||||||
d.SetString(data, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
return BigToBytes(d, 256)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseData(data ...interface{}) (ret []byte) {
|
|
||||||
for _, item := range data {
|
|
||||||
switch t := item.(type) {
|
|
||||||
case string:
|
|
||||||
var str []byte
|
|
||||||
if IsHex(t) {
|
|
||||||
str = Hex2Bytes(t[2:])
|
|
||||||
} else {
|
|
||||||
str = []byte(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = append(ret, RightPadBytes(str, 32)...)
|
|
||||||
case []byte:
|
|
||||||
ret = append(ret, LeftPadBytes(t, 32)...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func RightPadBytes(slice []byte, l int) []byte {
|
func RightPadBytes(slice []byte, l int) []byte {
|
||||||
if l < len(slice) {
|
if l < len(slice) {
|
||||||
return slice
|
return slice
|
||||||
|
|
@ -220,47 +109,3 @@ func LeftPadBytes(slice []byte, l int) []byte {
|
||||||
|
|
||||||
return padded
|
return padded
|
||||||
}
|
}
|
||||||
|
|
||||||
func LeftPadString(str string, l int) string {
|
|
||||||
if l < len(str) {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
zeros := Bytes2Hex(make([]byte, (l-len(str))/2))
|
|
||||||
|
|
||||||
return zeros + str
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func RightPadString(str string, l int) string {
|
|
||||||
if l < len(str) {
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
zeros := Bytes2Hex(make([]byte, (l-len(str))/2))
|
|
||||||
|
|
||||||
return str + zeros
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func ToAddress(slice []byte) (addr []byte) {
|
|
||||||
if len(slice) < 20 {
|
|
||||||
addr = LeftPadBytes(slice, 20)
|
|
||||||
} else if len(slice) > 20 {
|
|
||||||
addr = slice[len(slice)-20:]
|
|
||||||
} else {
|
|
||||||
addr = slice
|
|
||||||
}
|
|
||||||
|
|
||||||
addr = CopyBytes(addr)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ByteSliceToInterface(slice [][]byte) (ret []interface{}) {
|
|
||||||
for _, i := range slice {
|
|
||||||
ret = append(ret, i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -27,54 +27,6 @@ type BytesSuite struct{}
|
||||||
|
|
||||||
var _ = checker.Suite(&BytesSuite{})
|
var _ = checker.Suite(&BytesSuite{})
|
||||||
|
|
||||||
func (s *BytesSuite) TestNumberToBytes(c *checker.C) {
|
|
||||||
// data1 := int(1)
|
|
||||||
// res1 := NumberToBytes(data1, 16)
|
|
||||||
// c.Check(res1, checker.Panics)
|
|
||||||
|
|
||||||
var data2 float64 = 3.141592653
|
|
||||||
exp2 := []byte{0xe9, 0x38}
|
|
||||||
res2 := NumberToBytes(data2, 16)
|
|
||||||
c.Assert(res2, checker.DeepEquals, exp2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BytesSuite) TestBytesToNumber(c *checker.C) {
|
|
||||||
datasmall := []byte{0xe9, 0x38, 0xe9, 0x38}
|
|
||||||
datalarge := []byte{0xe9, 0x38, 0xe9, 0x38, 0xe9, 0x38, 0xe9, 0x38}
|
|
||||||
|
|
||||||
var expsmall uint64 = 0xe938e938
|
|
||||||
var explarge uint64 = 0x0
|
|
||||||
|
|
||||||
ressmall := BytesToNumber(datasmall)
|
|
||||||
reslarge := BytesToNumber(datalarge)
|
|
||||||
|
|
||||||
c.Assert(ressmall, checker.Equals, expsmall)
|
|
||||||
c.Assert(reslarge, checker.Equals, explarge)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BytesSuite) TestReadVarInt(c *checker.C) {
|
|
||||||
data8 := []byte{1, 2, 3, 4, 5, 6, 7, 8}
|
|
||||||
data4 := []byte{1, 2, 3, 4}
|
|
||||||
data2 := []byte{1, 2}
|
|
||||||
data1 := []byte{1}
|
|
||||||
|
|
||||||
exp8 := uint64(72623859790382856)
|
|
||||||
exp4 := uint64(16909060)
|
|
||||||
exp2 := uint64(258)
|
|
||||||
exp1 := uint64(1)
|
|
||||||
|
|
||||||
res8 := ReadVarInt(data8)
|
|
||||||
res4 := ReadVarInt(data4)
|
|
||||||
res2 := ReadVarInt(data2)
|
|
||||||
res1 := ReadVarInt(data1)
|
|
||||||
|
|
||||||
c.Assert(res8, checker.Equals, exp8)
|
|
||||||
c.Assert(res4, checker.Equals, exp4)
|
|
||||||
c.Assert(res2, checker.Equals, exp2)
|
|
||||||
c.Assert(res1, checker.Equals, exp1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BytesSuite) TestCopyBytes(c *checker.C) {
|
func (s *BytesSuite) TestCopyBytes(c *checker.C) {
|
||||||
data1 := []byte{1, 2, 3, 4}
|
data1 := []byte{1, 2, 3, 4}
|
||||||
exp1 := []byte{1, 2, 3, 4}
|
exp1 := []byte{1, 2, 3, 4}
|
||||||
|
|
@ -95,22 +47,6 @@ func (s *BytesSuite) TestIsHex(c *checker.C) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BytesSuite) TestParseDataString(c *checker.C) {
|
|
||||||
res1 := ParseData("hello", "world", "0x0106")
|
|
||||||
data := "68656c6c6f000000000000000000000000000000000000000000000000000000776f726c640000000000000000000000000000000000000000000000000000000106000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
exp1 := Hex2Bytes(data)
|
|
||||||
c.Assert(res1, checker.DeepEquals, exp1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BytesSuite) TestParseDataBytes(c *checker.C) {
|
|
||||||
data1 := []byte{232, 212, 165, 16, 0}
|
|
||||||
exp1 := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 212, 165, 16, 0}
|
|
||||||
|
|
||||||
res1 := ParseData(data1)
|
|
||||||
c.Assert(res1, checker.DeepEquals, exp1)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BytesSuite) TestLeftPadBytes(c *checker.C) {
|
func (s *BytesSuite) TestLeftPadBytes(c *checker.C) {
|
||||||
val1 := []byte{1, 2, 3, 4}
|
val1 := []byte{1, 2, 3, 4}
|
||||||
exp1 := []byte{0, 0, 0, 0, 1, 2, 3, 4}
|
exp1 := []byte{0, 0, 0, 0, 1, 2, 3, 4}
|
||||||
|
|
@ -122,28 +58,6 @@ func (s *BytesSuite) TestLeftPadBytes(c *checker.C) {
|
||||||
c.Assert(res2, checker.DeepEquals, val1)
|
c.Assert(res2, checker.DeepEquals, val1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BytesSuite) TestFormatData(c *checker.C) {
|
|
||||||
data1 := ""
|
|
||||||
data2 := "0xa9e67e00"
|
|
||||||
data3 := "a9e67e"
|
|
||||||
data4 := "\"a9e67e00\""
|
|
||||||
|
|
||||||
// exp1 := []byte{}
|
|
||||||
exp2 := []byte{00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 0xa9, 0xe6, 0x7e, 00}
|
|
||||||
exp3 := []byte{00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00}
|
|
||||||
exp4 := []byte{0x61, 0x39, 0x65, 0x36, 0x37, 0x65, 0x30, 0x30, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00}
|
|
||||||
|
|
||||||
res1 := FormatData(data1)
|
|
||||||
res2 := FormatData(data2)
|
|
||||||
res3 := FormatData(data3)
|
|
||||||
res4 := FormatData(data4)
|
|
||||||
|
|
||||||
c.Assert(res1, checker.IsNil)
|
|
||||||
c.Assert(res2, checker.DeepEquals, exp2)
|
|
||||||
c.Assert(res3, checker.DeepEquals, exp3)
|
|
||||||
c.Assert(res4, checker.DeepEquals, exp4)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BytesSuite) TestRightPadBytes(c *checker.C) {
|
func (s *BytesSuite) TestRightPadBytes(c *checker.C) {
|
||||||
val := []byte{1, 2, 3, 4}
|
val := []byte{1, 2, 3, 4}
|
||||||
exp := []byte{1, 2, 3, 4, 0, 0, 0, 0}
|
exp := []byte{1, 2, 3, 4, 0, 0, 0, 0}
|
||||||
|
|
@ -155,28 +69,6 @@ func (s *BytesSuite) TestRightPadBytes(c *checker.C) {
|
||||||
c.Assert(resshrt, checker.DeepEquals, val)
|
c.Assert(resshrt, checker.DeepEquals, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *BytesSuite) TestLeftPadString(c *checker.C) {
|
|
||||||
val := "test"
|
|
||||||
exp := "\x30\x30\x30\x30" + val
|
|
||||||
|
|
||||||
resstd := LeftPadString(val, 8)
|
|
||||||
resshrt := LeftPadString(val, 2)
|
|
||||||
|
|
||||||
c.Assert(resstd, checker.Equals, exp)
|
|
||||||
c.Assert(resshrt, checker.Equals, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *BytesSuite) TestRightPadString(c *checker.C) {
|
|
||||||
val := "test"
|
|
||||||
exp := val + "\x30\x30\x30\x30"
|
|
||||||
|
|
||||||
resstd := RightPadString(val, 8)
|
|
||||||
resshrt := RightPadString(val, 2)
|
|
||||||
|
|
||||||
c.Assert(resstd, checker.Equals, exp)
|
|
||||||
c.Assert(resshrt, checker.Equals, val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromHex(t *testing.T) {
|
func TestFromHex(t *testing.T) {
|
||||||
input := "0x01"
|
input := "0x01"
|
||||||
expected := []byte{1}
|
expected := []byte{1}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,7 @@ var (
|
||||||
ErrOddLength = errors.New("hex string has odd length")
|
ErrOddLength = errors.New("hex string has odd length")
|
||||||
ErrUint64Range = errors.New("hex number does not fit into 64 bits")
|
ErrUint64Range = errors.New("hex number does not fit into 64 bits")
|
||||||
ErrUintRange = fmt.Errorf("hex number does not fit into %d bits", uintBits)
|
ErrUintRange = fmt.Errorf("hex number does not fit into %d bits", uintBits)
|
||||||
|
ErrBig256Range = errors.New("hex number does not fit into 256 bits")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Decode decodes a hex string with 0x prefix.
|
// Decode decodes a hex string with 0x prefix.
|
||||||
|
|
@ -59,7 +60,11 @@ func Decode(input string) ([]byte, error) {
|
||||||
if !has0xPrefix(input) {
|
if !has0xPrefix(input) {
|
||||||
return nil, ErrMissingPrefix
|
return nil, ErrMissingPrefix
|
||||||
}
|
}
|
||||||
return hex.DecodeString(input[2:])
|
b, err := hex.DecodeString(input[2:])
|
||||||
|
if err != nil {
|
||||||
|
err = mapError(err)
|
||||||
|
}
|
||||||
|
return b, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustDecode decodes a hex string with 0x prefix. It panics for invalid input.
|
// MustDecode decodes a hex string with 0x prefix. It panics for invalid input.
|
||||||
|
|
@ -126,11 +131,15 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBig decodes a hex string with 0x prefix as a quantity.
|
// DecodeBig decodes a hex string with 0x prefix as a quantity.
|
||||||
|
// Numbers larger than 256 bits are not accepted.
|
||||||
func DecodeBig(input string) (*big.Int, error) {
|
func DecodeBig(input string) (*big.Int, error) {
|
||||||
raw, err := checkNumber(input)
|
raw, err := checkNumber(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if len(raw) > 64 {
|
||||||
|
return nil, ErrBig256Range
|
||||||
|
}
|
||||||
words := make([]big.Word, len(raw)/bigWordNibbles+1)
|
words := make([]big.Word, len(raw)/bigWordNibbles+1)
|
||||||
end := len(raw)
|
end := len(raw)
|
||||||
for i := range words {
|
for i := range words {
|
||||||
|
|
@ -169,7 +178,7 @@ func EncodeBig(bigint *big.Int) string {
|
||||||
if nbits == 0 {
|
if nbits == 0 {
|
||||||
return "0x0"
|
return "0x0"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("0x%x", bigint)
|
return fmt.Sprintf("%#x", bigint)
|
||||||
}
|
}
|
||||||
|
|
||||||
func has0xPrefix(input string) bool {
|
func has0xPrefix(input string) bool {
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ package hexutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
@ -31,7 +30,8 @@ type marshalTest struct {
|
||||||
type unmarshalTest struct {
|
type unmarshalTest struct {
|
||||||
input string
|
input string
|
||||||
want interface{}
|
want interface{}
|
||||||
wantErr error
|
wantErr error // if set, decoding must fail on any platform
|
||||||
|
wantErr32bit error // if set, decoding must fail on 32bit platforms (used for Uint tests)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -47,6 +47,7 @@ var (
|
||||||
{referenceBig("ff"), "0xff"},
|
{referenceBig("ff"), "0xff"},
|
||||||
{referenceBig("112233445566778899aabbccddeeff"), "0x112233445566778899aabbccddeeff"},
|
{referenceBig("112233445566778899aabbccddeeff"), "0x112233445566778899aabbccddeeff"},
|
||||||
{referenceBig("80a7f2c1bcc396c00"), "0x80a7f2c1bcc396c00"},
|
{referenceBig("80a7f2c1bcc396c00"), "0x80a7f2c1bcc396c00"},
|
||||||
|
{referenceBig("-80a7f2c1bcc396c00"), "-0x80a7f2c1bcc396c00"},
|
||||||
}
|
}
|
||||||
|
|
||||||
encodeUint64Tests = []marshalTest{
|
encodeUint64Tests = []marshalTest{
|
||||||
|
|
@ -56,14 +57,21 @@ var (
|
||||||
{uint64(0x1122334455667788), "0x1122334455667788"},
|
{uint64(0x1122334455667788), "0x1122334455667788"},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
encodeUintTests = []marshalTest{
|
||||||
|
{uint(0), "0x0"},
|
||||||
|
{uint(1), "0x1"},
|
||||||
|
{uint(0xff), "0xff"},
|
||||||
|
{uint(0x11223344), "0x11223344"},
|
||||||
|
}
|
||||||
|
|
||||||
decodeBytesTests = []unmarshalTest{
|
decodeBytesTests = []unmarshalTest{
|
||||||
// invalid
|
// invalid
|
||||||
{input: ``, wantErr: ErrEmptyString},
|
{input: ``, wantErr: ErrEmptyString},
|
||||||
{input: `0`, wantErr: ErrMissingPrefix},
|
{input: `0`, wantErr: ErrMissingPrefix},
|
||||||
{input: `0x0`, wantErr: hex.ErrLength},
|
{input: `0x0`, wantErr: ErrOddLength},
|
||||||
{input: `0x023`, wantErr: hex.ErrLength},
|
{input: `0x023`, wantErr: ErrOddLength},
|
||||||
{input: `0xxx`, wantErr: hex.InvalidByteError('x')},
|
{input: `0xxx`, wantErr: ErrSyntax},
|
||||||
{input: `0x01zz01`, wantErr: hex.InvalidByteError('z')},
|
{input: `0x01zz01`, wantErr: ErrSyntax},
|
||||||
// valid
|
// valid
|
||||||
{input: `0x`, want: []byte{}},
|
{input: `0x`, want: []byte{}},
|
||||||
{input: `0X`, want: []byte{}},
|
{input: `0X`, want: []byte{}},
|
||||||
|
|
@ -83,6 +91,10 @@ var (
|
||||||
{input: `0x01`, wantErr: ErrLeadingZero},
|
{input: `0x01`, wantErr: ErrLeadingZero},
|
||||||
{input: `0xx`, wantErr: ErrSyntax},
|
{input: `0xx`, wantErr: ErrSyntax},
|
||||||
{input: `0x1zz01`, wantErr: ErrSyntax},
|
{input: `0x1zz01`, wantErr: ErrSyntax},
|
||||||
|
{
|
||||||
|
input: `0x10000000000000000000000000000000000000000000000000000000000000000`,
|
||||||
|
wantErr: ErrBig256Range,
|
||||||
|
},
|
||||||
// valid
|
// valid
|
||||||
{input: `0x0`, want: big.NewInt(0)},
|
{input: `0x0`, want: big.NewInt(0)},
|
||||||
{input: `0x2`, want: big.NewInt(0x2)},
|
{input: `0x2`, want: big.NewInt(0x2)},
|
||||||
|
|
@ -99,6 +111,10 @@ var (
|
||||||
input: `0xffffffffffffffffffffffffffffffffffff`,
|
input: `0xffffffffffffffffffffffffffffffffffff`,
|
||||||
want: referenceBig("ffffffffffffffffffffffffffffffffffff"),
|
want: referenceBig("ffffffffffffffffffffffffffffffffffff"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: `0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`,
|
||||||
|
want: referenceBig("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
decodeUint64Tests = []unmarshalTest{
|
decodeUint64Tests = []unmarshalTest{
|
||||||
|
|
|
||||||
|
|
@ -25,28 +25,33 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
jsonNull = []byte("null")
|
textZero = []byte(`0x0`)
|
||||||
jsonZero = []byte(`"0x0"`)
|
|
||||||
errNonString = errors.New("cannot unmarshal non-string as hex data")
|
errNonString = errors.New("cannot unmarshal non-string as hex data")
|
||||||
errNegativeBigInt = errors.New("hexutil.Big: can't marshal negative integer")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Bytes marshals/unmarshals as a JSON string with 0x prefix.
|
// Bytes marshals/unmarshals as a JSON string with 0x prefix.
|
||||||
// The empty slice marshals as "0x".
|
// The empty slice marshals as "0x".
|
||||||
type Bytes []byte
|
type Bytes []byte
|
||||||
|
|
||||||
// MarshalJSON implements json.Marshaler.
|
// MarshalText implements encoding.TextMarshaler
|
||||||
func (b Bytes) MarshalJSON() ([]byte, error) {
|
func (b Bytes) MarshalText() ([]byte, error) {
|
||||||
result := make([]byte, len(b)*2+4)
|
result := make([]byte, len(b)*2+2)
|
||||||
copy(result, `"0x`)
|
copy(result, `0x`)
|
||||||
hex.Encode(result[3:], b)
|
hex.Encode(result[2:], b)
|
||||||
result[len(result)-1] = '"'
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON implements json.Unmarshaler.
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
func (b *Bytes) UnmarshalJSON(input []byte) error {
|
func (b *Bytes) UnmarshalJSON(input []byte) error {
|
||||||
raw, err := checkJSON(input)
|
if !isString(input) {
|
||||||
|
return errNonString
|
||||||
|
}
|
||||||
|
return b.UnmarshalText(input[1 : len(input)-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||||
|
func (b *Bytes) UnmarshalText(input []byte) error {
|
||||||
|
raw, err := checkText(input, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -64,17 +69,11 @@ func (b Bytes) String() string {
|
||||||
return Encode(b)
|
return Encode(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON decodes input as a JSON string with 0x prefix. The length of out
|
// UnmarshalFixedText decodes the input as a string with 0x prefix. The length of out
|
||||||
// determines the required input length. This function is commonly used to implement the
|
// determines the required input length. This function is commonly used to implement the
|
||||||
// UnmarshalJSON method for fixed-size types:
|
// UnmarshalText method for fixed-size types.
|
||||||
//
|
func UnmarshalFixedText(typname string, input, out []byte) error {
|
||||||
// type Foo [8]byte
|
raw, err := checkText(input, true)
|
||||||
//
|
|
||||||
// func (f *Foo) UnmarshalJSON(input []byte) error {
|
|
||||||
// return hexutil.UnmarshalJSON("Foo", input, f[:])
|
|
||||||
// }
|
|
||||||
func UnmarshalJSON(typname string, input, out []byte) error {
|
|
||||||
raw, err := checkJSON(input)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -91,34 +90,57 @@ func UnmarshalJSON(typname string, input, out []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Big marshals/unmarshals as a JSON string with 0x prefix. The zero value marshals as
|
// UnmarshalFixedUnprefixedText decodes the input as a string with optional 0x prefix. The
|
||||||
// "0x0". Negative integers are not supported at this time. Attempting to marshal them
|
// length of out determines the required input length. This function is commonly used to
|
||||||
// will return an error.
|
// implement the UnmarshalText method for fixed-size types.
|
||||||
|
func UnmarshalFixedUnprefixedText(typname string, input, out []byte) error {
|
||||||
|
raw, err := checkText(input, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if len(raw)/2 != len(out) {
|
||||||
|
return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname)
|
||||||
|
}
|
||||||
|
// Pre-verify syntax before modifying out.
|
||||||
|
for _, b := range raw {
|
||||||
|
if decodeNibble(b) == badNibble {
|
||||||
|
return ErrSyntax
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hex.Decode(out, raw)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Big marshals/unmarshals as a JSON string with 0x prefix.
|
||||||
|
// The zero value marshals as "0x0".
|
||||||
|
//
|
||||||
|
// Negative integers are not supported at this time. Attempting to marshal them will
|
||||||
|
// return an error. Values larger than 256bits are rejected by Unmarshal but will be
|
||||||
|
// marshaled without error.
|
||||||
type Big big.Int
|
type Big big.Int
|
||||||
|
|
||||||
// MarshalJSON implements json.Marshaler.
|
// MarshalText implements encoding.TextMarshaler
|
||||||
func (b *Big) MarshalJSON() ([]byte, error) {
|
func (b Big) MarshalText() ([]byte, error) {
|
||||||
if b == nil {
|
return []byte(EncodeBig((*big.Int)(&b))), nil
|
||||||
return jsonNull, nil
|
|
||||||
}
|
|
||||||
bigint := (*big.Int)(b)
|
|
||||||
if bigint.Sign() == -1 {
|
|
||||||
return nil, errNegativeBigInt
|
|
||||||
}
|
|
||||||
nbits := bigint.BitLen()
|
|
||||||
if nbits == 0 {
|
|
||||||
return jsonZero, nil
|
|
||||||
}
|
|
||||||
enc := fmt.Sprintf(`"0x%x"`, bigint)
|
|
||||||
return []byte(enc), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON implements json.Unmarshaler.
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
func (b *Big) UnmarshalJSON(input []byte) error {
|
func (b *Big) UnmarshalJSON(input []byte) error {
|
||||||
raw, err := checkNumberJSON(input)
|
if !isString(input) {
|
||||||
|
return errNonString
|
||||||
|
}
|
||||||
|
return b.UnmarshalText(input[1 : len(input)-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements encoding.TextUnmarshaler
|
||||||
|
func (b *Big) UnmarshalText(input []byte) error {
|
||||||
|
raw, err := checkNumberText(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(raw) > 64 {
|
||||||
|
return ErrBig256Range
|
||||||
|
}
|
||||||
words := make([]big.Word, len(raw)/bigWordNibbles+1)
|
words := make([]big.Word, len(raw)/bigWordNibbles+1)
|
||||||
end := len(raw)
|
end := len(raw)
|
||||||
for i := range words {
|
for i := range words {
|
||||||
|
|
@ -156,18 +178,25 @@ func (b *Big) String() string {
|
||||||
// The zero value marshals as "0x0".
|
// The zero value marshals as "0x0".
|
||||||
type Uint64 uint64
|
type Uint64 uint64
|
||||||
|
|
||||||
// MarshalJSON implements json.Marshaler.
|
// MarshalText implements encoding.TextMarshaler.
|
||||||
func (b Uint64) MarshalJSON() ([]byte, error) {
|
func (b Uint64) MarshalText() ([]byte, error) {
|
||||||
buf := make([]byte, 3, 12)
|
buf := make([]byte, 2, 10)
|
||||||
copy(buf, `"0x`)
|
copy(buf, `0x`)
|
||||||
buf = strconv.AppendUint(buf, uint64(b), 16)
|
buf = strconv.AppendUint(buf, uint64(b), 16)
|
||||||
buf = append(buf, '"')
|
|
||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON implements json.Unmarshaler.
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
func (b *Uint64) UnmarshalJSON(input []byte) error {
|
func (b *Uint64) UnmarshalJSON(input []byte) error {
|
||||||
raw, err := checkNumberJSON(input)
|
if !isString(input) {
|
||||||
|
return errNonString
|
||||||
|
}
|
||||||
|
return b.UnmarshalText(input[1 : len(input)-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements encoding.TextUnmarshaler
|
||||||
|
func (b *Uint64) UnmarshalText(input []byte) error {
|
||||||
|
raw, err := checkNumberText(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -196,19 +225,27 @@ func (b Uint64) String() string {
|
||||||
// The zero value marshals as "0x0".
|
// The zero value marshals as "0x0".
|
||||||
type Uint uint
|
type Uint uint
|
||||||
|
|
||||||
// MarshalJSON implements json.Marshaler.
|
// MarshalText implements encoding.TextMarshaler.
|
||||||
func (b Uint) MarshalJSON() ([]byte, error) {
|
func (b Uint) MarshalText() ([]byte, error) {
|
||||||
return Uint64(b).MarshalJSON()
|
return Uint64(b).MarshalText()
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON implements json.Unmarshaler.
|
// UnmarshalJSON implements json.Unmarshaler.
|
||||||
func (b *Uint) UnmarshalJSON(input []byte) error {
|
func (b *Uint) UnmarshalJSON(input []byte) error {
|
||||||
|
if !isString(input) {
|
||||||
|
return errNonString
|
||||||
|
}
|
||||||
|
return b.UnmarshalText(input[1 : len(input)-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||||
|
func (b *Uint) UnmarshalText(input []byte) error {
|
||||||
var u64 Uint64
|
var u64 Uint64
|
||||||
err := u64.UnmarshalJSON(input)
|
err := u64.UnmarshalText(input)
|
||||||
if err != nil {
|
if u64 > Uint64(^uint(0)) || err == ErrUint64Range {
|
||||||
return err
|
|
||||||
} else if u64 > Uint64(^uint(0)) {
|
|
||||||
return ErrUintRange
|
return ErrUintRange
|
||||||
|
} else if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
*b = Uint(u64)
|
*b = Uint(u64)
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -227,28 +264,22 @@ func bytesHave0xPrefix(input []byte) bool {
|
||||||
return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
|
return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkJSON(input []byte) (raw []byte, err error) {
|
func checkText(input []byte, wantPrefix bool) ([]byte, error) {
|
||||||
if !isString(input) {
|
if len(input) == 0 {
|
||||||
return nil, errNonString
|
|
||||||
}
|
|
||||||
if len(input) == 2 {
|
|
||||||
return nil, nil // empty strings are allowed
|
return nil, nil // empty strings are allowed
|
||||||
}
|
}
|
||||||
if !bytesHave0xPrefix(input[1:]) {
|
if bytesHave0xPrefix(input) {
|
||||||
|
input = input[2:]
|
||||||
|
} else if wantPrefix {
|
||||||
return nil, ErrMissingPrefix
|
return nil, ErrMissingPrefix
|
||||||
}
|
}
|
||||||
input = input[3 : len(input)-1]
|
|
||||||
if len(input)%2 != 0 {
|
if len(input)%2 != 0 {
|
||||||
return nil, ErrOddLength
|
return nil, ErrOddLength
|
||||||
}
|
}
|
||||||
return input, nil
|
return input, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkNumberJSON(input []byte) (raw []byte, err error) {
|
func checkNumberText(input []byte) (raw []byte, err error) {
|
||||||
if !isString(input) {
|
|
||||||
return nil, errNonString
|
|
||||||
}
|
|
||||||
input = input[1 : len(input)-1]
|
|
||||||
if len(input) == 0 {
|
if len(input) == 0 {
|
||||||
return nil, nil // empty strings are allowed
|
return nil, nil // empty strings are allowed
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
// Copyright 2017 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -14,28 +14,32 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package errs
|
package hexutil_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testErrors() *Errors {
|
type MyType [5]byte
|
||||||
return &Errors{
|
|
||||||
Package: "TEST",
|
func (v *MyType) UnmarshalText(input []byte) error {
|
||||||
Errors: map[int]string{
|
return hexutil.UnmarshalFixedText("MyType", input, v[:])
|
||||||
0: "zero",
|
|
||||||
1: "one",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestErrorMessage(t *testing.T) {
|
func (v MyType) String() string {
|
||||||
err := testErrors().New(0, "zero detail %v", "available")
|
return hexutil.Bytes(v[:]).String()
|
||||||
message := fmt.Sprintf("%v", err)
|
}
|
||||||
exp := "[TEST] ERROR: zero: zero detail available"
|
|
||||||
if message != exp {
|
func ExampleUnmarshalFixedText() {
|
||||||
t.Errorf("error message incorrect. expected %v, got %v", exp, message)
|
var v1, v2 MyType
|
||||||
}
|
fmt.Println("v1 error:", json.Unmarshal([]byte(`"0x01"`), &v1))
|
||||||
|
fmt.Println("v2 error:", json.Unmarshal([]byte(`"0x0101010101"`), &v2))
|
||||||
|
fmt.Println("v2:", v2)
|
||||||
|
// Output:
|
||||||
|
// v1 error: hex string has length 2, want 10 for MyType
|
||||||
|
// v2 error: <nil>
|
||||||
|
// v2: 0x0101010101
|
||||||
}
|
}
|
||||||
|
|
@ -19,6 +19,8 @@ package hexutil
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
@ -55,9 +57,11 @@ func referenceBytes(s string) []byte {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errJSONEOF = errors.New("unexpected end of JSON input")
|
||||||
|
|
||||||
var unmarshalBytesTests = []unmarshalTest{
|
var unmarshalBytesTests = []unmarshalTest{
|
||||||
// invalid encoding
|
// invalid encoding
|
||||||
{input: "", wantErr: errNonString},
|
{input: "", wantErr: errJSONEOF},
|
||||||
{input: "null", wantErr: errNonString},
|
{input: "null", wantErr: errNonString},
|
||||||
{input: "10", wantErr: errNonString},
|
{input: "10", wantErr: errNonString},
|
||||||
{input: `"0"`, wantErr: ErrMissingPrefix},
|
{input: `"0"`, wantErr: ErrMissingPrefix},
|
||||||
|
|
@ -80,7 +84,7 @@ var unmarshalBytesTests = []unmarshalTest{
|
||||||
func TestUnmarshalBytes(t *testing.T) {
|
func TestUnmarshalBytes(t *testing.T) {
|
||||||
for _, test := range unmarshalBytesTests {
|
for _, test := range unmarshalBytesTests {
|
||||||
var v Bytes
|
var v Bytes
|
||||||
err := v.UnmarshalJSON([]byte(test.input))
|
err := json.Unmarshal([]byte(test.input), &v)
|
||||||
if !checkError(t, test.input, err, test.wantErr) {
|
if !checkError(t, test.input, err, test.wantErr) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -104,7 +108,7 @@ func BenchmarkUnmarshalBytes(b *testing.B) {
|
||||||
func TestMarshalBytes(t *testing.T) {
|
func TestMarshalBytes(t *testing.T) {
|
||||||
for _, test := range encodeBytesTests {
|
for _, test := range encodeBytesTests {
|
||||||
in := test.input.([]byte)
|
in := test.input.([]byte)
|
||||||
out, err := Bytes(in).MarshalJSON()
|
out, err := json.Marshal(Bytes(in))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%x: %v", in, err)
|
t.Errorf("%x: %v", in, err)
|
||||||
continue
|
continue
|
||||||
|
|
@ -122,7 +126,7 @@ func TestMarshalBytes(t *testing.T) {
|
||||||
|
|
||||||
var unmarshalBigTests = []unmarshalTest{
|
var unmarshalBigTests = []unmarshalTest{
|
||||||
// invalid encoding
|
// invalid encoding
|
||||||
{input: "", wantErr: errNonString},
|
{input: "", wantErr: errJSONEOF},
|
||||||
{input: "null", wantErr: errNonString},
|
{input: "null", wantErr: errNonString},
|
||||||
{input: "10", wantErr: errNonString},
|
{input: "10", wantErr: errNonString},
|
||||||
{input: `"0"`, wantErr: ErrMissingPrefix},
|
{input: `"0"`, wantErr: ErrMissingPrefix},
|
||||||
|
|
@ -130,6 +134,10 @@ var unmarshalBigTests = []unmarshalTest{
|
||||||
{input: `"0x01"`, wantErr: ErrLeadingZero},
|
{input: `"0x01"`, wantErr: ErrLeadingZero},
|
||||||
{input: `"0xx"`, wantErr: ErrSyntax},
|
{input: `"0xx"`, wantErr: ErrSyntax},
|
||||||
{input: `"0x1zz01"`, wantErr: ErrSyntax},
|
{input: `"0x1zz01"`, wantErr: ErrSyntax},
|
||||||
|
{
|
||||||
|
input: `"0x10000000000000000000000000000000000000000000000000000000000000000"`,
|
||||||
|
wantErr: ErrBig256Range,
|
||||||
|
},
|
||||||
|
|
||||||
// valid encoding
|
// valid encoding
|
||||||
{input: `""`, want: big.NewInt(0)},
|
{input: `""`, want: big.NewInt(0)},
|
||||||
|
|
@ -148,12 +156,16 @@ var unmarshalBigTests = []unmarshalTest{
|
||||||
input: `"0xffffffffffffffffffffffffffffffffffff"`,
|
input: `"0xffffffffffffffffffffffffffffffffffff"`,
|
||||||
want: referenceBig("ffffffffffffffffffffffffffffffffffff"),
|
want: referenceBig("ffffffffffffffffffffffffffffffffffff"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: `"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"`,
|
||||||
|
want: referenceBig("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalBig(t *testing.T) {
|
func TestUnmarshalBig(t *testing.T) {
|
||||||
for _, test := range unmarshalBigTests {
|
for _, test := range unmarshalBigTests {
|
||||||
var v Big
|
var v Big
|
||||||
err := v.UnmarshalJSON([]byte(test.input))
|
err := json.Unmarshal([]byte(test.input), &v)
|
||||||
if !checkError(t, test.input, err, test.wantErr) {
|
if !checkError(t, test.input, err, test.wantErr) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -177,7 +189,7 @@ func BenchmarkUnmarshalBig(b *testing.B) {
|
||||||
func TestMarshalBig(t *testing.T) {
|
func TestMarshalBig(t *testing.T) {
|
||||||
for _, test := range encodeBigTests {
|
for _, test := range encodeBigTests {
|
||||||
in := test.input.(*big.Int)
|
in := test.input.(*big.Int)
|
||||||
out, err := (*Big)(in).MarshalJSON()
|
out, err := json.Marshal((*Big)(in))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%d: %v", in, err)
|
t.Errorf("%d: %v", in, err)
|
||||||
continue
|
continue
|
||||||
|
|
@ -195,7 +207,7 @@ func TestMarshalBig(t *testing.T) {
|
||||||
|
|
||||||
var unmarshalUint64Tests = []unmarshalTest{
|
var unmarshalUint64Tests = []unmarshalTest{
|
||||||
// invalid encoding
|
// invalid encoding
|
||||||
{input: "", wantErr: errNonString},
|
{input: "", wantErr: errJSONEOF},
|
||||||
{input: "null", wantErr: errNonString},
|
{input: "null", wantErr: errNonString},
|
||||||
{input: "10", wantErr: errNonString},
|
{input: "10", wantErr: errNonString},
|
||||||
{input: `"0"`, wantErr: ErrMissingPrefix},
|
{input: `"0"`, wantErr: ErrMissingPrefix},
|
||||||
|
|
@ -219,7 +231,7 @@ var unmarshalUint64Tests = []unmarshalTest{
|
||||||
func TestUnmarshalUint64(t *testing.T) {
|
func TestUnmarshalUint64(t *testing.T) {
|
||||||
for _, test := range unmarshalUint64Tests {
|
for _, test := range unmarshalUint64Tests {
|
||||||
var v Uint64
|
var v Uint64
|
||||||
err := v.UnmarshalJSON([]byte(test.input))
|
err := json.Unmarshal([]byte(test.input), &v)
|
||||||
if !checkError(t, test.input, err, test.wantErr) {
|
if !checkError(t, test.input, err, test.wantErr) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -241,7 +253,7 @@ func BenchmarkUnmarshalUint64(b *testing.B) {
|
||||||
func TestMarshalUint64(t *testing.T) {
|
func TestMarshalUint64(t *testing.T) {
|
||||||
for _, test := range encodeUint64Tests {
|
for _, test := range encodeUint64Tests {
|
||||||
in := test.input.(uint64)
|
in := test.input.(uint64)
|
||||||
out, err := Uint64(in).MarshalJSON()
|
out, err := json.Marshal(Uint64(in))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("%d: %v", in, err)
|
t.Errorf("%d: %v", in, err)
|
||||||
continue
|
continue
|
||||||
|
|
@ -256,3 +268,107 @@ func TestMarshalUint64(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMarshalUint(t *testing.T) {
|
||||||
|
for _, test := range encodeUintTests {
|
||||||
|
in := test.input.(uint)
|
||||||
|
out, err := json.Marshal(Uint(in))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%d: %v", in, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if want := `"` + test.want + `"`; string(out) != want {
|
||||||
|
t.Errorf("%d: MarshalJSON output mismatch: got %q, want %q", in, out, want)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if out := (Uint)(in).String(); out != test.want {
|
||||||
|
t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// These are variables (not constants) to avoid constant overflow
|
||||||
|
// checks in the compiler on 32bit platforms.
|
||||||
|
maxUint33bits = uint64(^uint32(0)) + 1
|
||||||
|
maxUint64bits = ^uint64(0)
|
||||||
|
)
|
||||||
|
|
||||||
|
var unmarshalUintTests = []unmarshalTest{
|
||||||
|
// invalid encoding
|
||||||
|
{input: "", wantErr: errJSONEOF},
|
||||||
|
{input: "null", wantErr: errNonString},
|
||||||
|
{input: "10", wantErr: errNonString},
|
||||||
|
{input: `"0"`, wantErr: ErrMissingPrefix},
|
||||||
|
{input: `"0x"`, wantErr: ErrEmptyNumber},
|
||||||
|
{input: `"0x01"`, wantErr: ErrLeadingZero},
|
||||||
|
{input: `"0x100000000"`, want: uint(maxUint33bits), wantErr32bit: ErrUintRange},
|
||||||
|
{input: `"0xfffffffffffffffff"`, wantErr: ErrUintRange},
|
||||||
|
{input: `"0xx"`, wantErr: ErrSyntax},
|
||||||
|
{input: `"0x1zz01"`, wantErr: ErrSyntax},
|
||||||
|
|
||||||
|
// valid encoding
|
||||||
|
{input: `""`, want: uint(0)},
|
||||||
|
{input: `"0x0"`, want: uint(0)},
|
||||||
|
{input: `"0x2"`, want: uint(0x2)},
|
||||||
|
{input: `"0x2F2"`, want: uint(0x2f2)},
|
||||||
|
{input: `"0X2F2"`, want: uint(0x2f2)},
|
||||||
|
{input: `"0x1122aaff"`, want: uint(0x1122aaff)},
|
||||||
|
{input: `"0xbbb"`, want: uint(0xbbb)},
|
||||||
|
{input: `"0xffffffff"`, want: uint(0xffffffff)},
|
||||||
|
{input: `"0xffffffffffffffff"`, want: uint(maxUint64bits), wantErr32bit: ErrUintRange},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalUint(t *testing.T) {
|
||||||
|
for _, test := range unmarshalUintTests {
|
||||||
|
var v Uint
|
||||||
|
err := json.Unmarshal([]byte(test.input), &v)
|
||||||
|
if uintBits == 32 && test.wantErr32bit != nil {
|
||||||
|
checkError(t, test.input, err, test.wantErr32bit)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !checkError(t, test.input, err, test.wantErr) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if uint(v) != test.want.(uint) {
|
||||||
|
t.Errorf("input %s: value mismatch: got %d, want %d", test.input, v, test.want)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalFixedUnprefixedText(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input string
|
||||||
|
want []byte
|
||||||
|
wantErr error
|
||||||
|
}{
|
||||||
|
{input: "0x2", wantErr: ErrOddLength},
|
||||||
|
{input: "2", wantErr: ErrOddLength},
|
||||||
|
{input: "4444", wantErr: errors.New("hex string has length 4, want 8 for x")},
|
||||||
|
{input: "4444", wantErr: errors.New("hex string has length 4, want 8 for x")},
|
||||||
|
// check that output is not modified for partially correct input
|
||||||
|
{input: "444444gg", wantErr: ErrSyntax, want: []byte{0, 0, 0, 0}},
|
||||||
|
{input: "0x444444gg", wantErr: ErrSyntax, want: []byte{0, 0, 0, 0}},
|
||||||
|
// valid inputs
|
||||||
|
{input: "44444444", want: []byte{0x44, 0x44, 0x44, 0x44}},
|
||||||
|
{input: "0x44444444", want: []byte{0x44, 0x44, 0x44, 0x44}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
out := make([]byte, 4)
|
||||||
|
err := UnmarshalFixedUnprefixedText("x", []byte(test.input), out)
|
||||||
|
switch {
|
||||||
|
case err == nil && test.wantErr != nil:
|
||||||
|
t.Errorf("%q: got no error, expected %q", test.input, test.wantErr)
|
||||||
|
case err != nil && test.wantErr == nil:
|
||||||
|
t.Errorf("%q: unexpected error %q", test.input, err)
|
||||||
|
case err != nil && err.Error() != test.wantErr.Error():
|
||||||
|
t.Errorf("%q: error mismatch: got %q, want %q", test.input, err, test.wantErr)
|
||||||
|
}
|
||||||
|
if test.want != nil && !bytes.Equal(out, test.want) {
|
||||||
|
t.Errorf("%q: output mismatch: got %x, want %x", test.input, out, test.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
190
common/icap.go
190
common/icap.go
|
|
@ -1,190 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// Spec at https://github.com/ethereum/wiki/wiki/ICAP:-Inter-exchange-Client-Address-Protocol
|
|
||||||
|
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"math/big"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
Base36Chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
||||||
ICAPLengthError = errors.New("Invalid ICAP length")
|
|
||||||
ICAPEncodingError = errors.New("Invalid ICAP encoding")
|
|
||||||
ICAPChecksumError = errors.New("Invalid ICAP checksum")
|
|
||||||
ICAPCountryCodeError = errors.New("Invalid ICAP country code")
|
|
||||||
ICAPAssetIdentError = errors.New("Invalid ICAP asset identifier")
|
|
||||||
ICAPInstCodeError = errors.New("Invalid ICAP institution code")
|
|
||||||
ICAPClientIdentError = errors.New("Invalid ICAP client identifier")
|
|
||||||
)
|
|
||||||
|
|
||||||
func ICAPToAddress(s string) (Address, error) {
|
|
||||||
switch len(s) {
|
|
||||||
case 35: // "XE" + 2 digit checksum + 31 base-36 chars of address
|
|
||||||
return parseICAP(s)
|
|
||||||
case 34: // "XE" + 2 digit checksum + 30 base-36 chars of address
|
|
||||||
return parseICAP(s)
|
|
||||||
case 20: // "XE" + 2 digit checksum + 3-char asset identifier +
|
|
||||||
// 4-char institution identifier + 9-char institution client identifier
|
|
||||||
return parseIndirectICAP(s)
|
|
||||||
default:
|
|
||||||
return Address{}, ICAPLengthError
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseICAP(s string) (Address, error) {
|
|
||||||
if !strings.HasPrefix(s, "XE") {
|
|
||||||
return Address{}, ICAPCountryCodeError
|
|
||||||
}
|
|
||||||
if err := validCheckSum(s); err != nil {
|
|
||||||
return Address{}, err
|
|
||||||
}
|
|
||||||
// checksum is ISO13616, Ethereum address is base-36
|
|
||||||
bigAddr, _ := new(big.Int).SetString(s[4:], 36)
|
|
||||||
return BigToAddress(bigAddr), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseIndirectICAP(s string) (Address, error) {
|
|
||||||
if !strings.HasPrefix(s, "XE") {
|
|
||||||
return Address{}, ICAPCountryCodeError
|
|
||||||
}
|
|
||||||
if s[4:7] != "ETH" {
|
|
||||||
return Address{}, ICAPAssetIdentError
|
|
||||||
}
|
|
||||||
if err := validCheckSum(s); err != nil {
|
|
||||||
return Address{}, err
|
|
||||||
}
|
|
||||||
// TODO: integrate with ICAP namereg
|
|
||||||
return Address{}, errors.New("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddressToICAP(a Address) (string, error) {
|
|
||||||
enc := base36Encode(a.Big())
|
|
||||||
// zero padd encoded address to Direct ICAP length if needed
|
|
||||||
if len(enc) < 30 {
|
|
||||||
enc = join(strings.Repeat("0", 30-len(enc)), enc)
|
|
||||||
}
|
|
||||||
icap := join("XE", checkDigits(enc), enc)
|
|
||||||
return icap, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: integrate with ICAP namereg when it's available
|
|
||||||
func AddressToIndirectICAP(a Address, instCode string) (string, error) {
|
|
||||||
// return addressToIndirectICAP(a, instCode)
|
|
||||||
return "", errors.New("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func addressToIndirectICAP(a Address, instCode string) (string, error) {
|
|
||||||
// TODO: add addressToClientIdent which grabs client ident from ICAP namereg
|
|
||||||
//clientIdent := addressToClientIdent(a)
|
|
||||||
clientIdent := "todo"
|
|
||||||
return clientIdentToIndirectICAP(instCode, clientIdent)
|
|
||||||
}
|
|
||||||
|
|
||||||
func clientIdentToIndirectICAP(instCode, clientIdent string) (string, error) {
|
|
||||||
if len(instCode) != 4 || !validBase36(instCode) {
|
|
||||||
return "", ICAPInstCodeError
|
|
||||||
}
|
|
||||||
if len(clientIdent) != 9 || !validBase36(instCode) {
|
|
||||||
return "", ICAPClientIdentError
|
|
||||||
}
|
|
||||||
|
|
||||||
// currently ETH is only valid asset identifier
|
|
||||||
s := join("ETH", instCode, clientIdent)
|
|
||||||
return join("XE", checkDigits(s), s), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN
|
|
||||||
func validCheckSum(s string) error {
|
|
||||||
s = join(s[4:], s[:4])
|
|
||||||
expanded, err := iso13616Expand(s)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
checkSumNum, _ := new(big.Int).SetString(expanded, 10)
|
|
||||||
if checkSumNum.Mod(checkSumNum, Big97).Cmp(Big1) != 0 {
|
|
||||||
return ICAPChecksumError
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkDigits(s string) string {
|
|
||||||
expanded, _ := iso13616Expand(strings.Join([]string{s, "XE00"}, ""))
|
|
||||||
num, _ := new(big.Int).SetString(expanded, 10)
|
|
||||||
num.Sub(Big98, num.Mod(num, Big97))
|
|
||||||
|
|
||||||
checkDigits := num.String()
|
|
||||||
// zero padd checksum
|
|
||||||
if len(checkDigits) == 1 {
|
|
||||||
checkDigits = join("0", checkDigits)
|
|
||||||
}
|
|
||||||
return checkDigits
|
|
||||||
}
|
|
||||||
|
|
||||||
// not base-36, but expansion to decimal literal: A = 10, B = 11, ... Z = 35
|
|
||||||
func iso13616Expand(s string) (string, error) {
|
|
||||||
var parts []string
|
|
||||||
if !validBase36(s) {
|
|
||||||
return "", ICAPEncodingError
|
|
||||||
}
|
|
||||||
for _, c := range s {
|
|
||||||
i := uint64(c)
|
|
||||||
if i >= 65 {
|
|
||||||
parts = append(parts, strconv.FormatUint(uint64(c)-55, 10))
|
|
||||||
} else {
|
|
||||||
parts = append(parts, string(c))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return join(parts...), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func base36Encode(i *big.Int) string {
|
|
||||||
var chars []rune
|
|
||||||
x := new(big.Int)
|
|
||||||
for {
|
|
||||||
x.Mod(i, Big36)
|
|
||||||
chars = append(chars, rune(Base36Chars[x.Uint64()]))
|
|
||||||
i.Div(i, Big36)
|
|
||||||
if i.Cmp(Big0) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// reverse slice
|
|
||||||
for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
chars[i], chars[j] = chars[j], chars[i]
|
|
||||||
}
|
|
||||||
return string(chars)
|
|
||||||
}
|
|
||||||
|
|
||||||
func validBase36(s string) bool {
|
|
||||||
for _, c := range s {
|
|
||||||
i := uint64(c)
|
|
||||||
// 0-9 or A-Z
|
|
||||||
if i < 48 || (i > 57 && i < 65) || i > 90 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func join(s ...string) string {
|
|
||||||
return strings.Join(s, "")
|
|
||||||
}
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package common
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
/* More test vectors:
|
|
||||||
https://github.com/ethereum/web3.js/blob/master/test/iban.fromAddress.js
|
|
||||||
https://github.com/ethereum/web3.js/blob/master/test/iban.toAddress.js
|
|
||||||
https://github.com/ethereum/web3.js/blob/master/test/iban.isValid.js
|
|
||||||
https://github.com/ethereum/libethereum/blob/develop/test/libethcore/icap.cpp
|
|
||||||
*/
|
|
||||||
|
|
||||||
type icapTest struct {
|
|
||||||
name string
|
|
||||||
addr string
|
|
||||||
icap string
|
|
||||||
}
|
|
||||||
|
|
||||||
var icapOKTests = []icapTest{
|
|
||||||
{"Direct1", "0x52dc504a422f0e2a9e7632a34a50f1a82f8224c7", "XE499OG1EH8ZZI0KXC6N83EKGT1BM97P2O7"},
|
|
||||||
{"Direct2", "0x11c5496aee77c1ba1f0854206a26dda82a81d6d8", "XE1222Q908LN1QBBU6XUQSO1OHWJIOS46OO"},
|
|
||||||
{"DirectZeroPrefix", "0x00c5496aee77c1ba1f0854206a26dda82a81d6d8", "XE7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS"},
|
|
||||||
{"DirectDoubleZeroPrefix", "0x0000a5327eab78357cbf2ae8f3d49fd9d90c7d22", "XE0600DQK33XDTYUCRI0KYM5ELAKXDWWF6"},
|
|
||||||
}
|
|
||||||
|
|
||||||
var icapInvalidTests = []icapTest{
|
|
||||||
{"DirectInvalidCheckSum", "", "XE7438O073KYGTWWZN0F2WZ0R8PX5ZPPZS"},
|
|
||||||
{"DirectInvalidCountryCode", "", "XD7338O073KYGTWWZN0F2WZ0R8PX5ZPPZS"},
|
|
||||||
{"DirectInvalidLength36", "", "XE499OG1EH8ZZI0KXC6N83EKGT1BM97P2O77"},
|
|
||||||
{"DirectInvalidLength33", "", "XE499OG1EH8ZZI0KXC6N83EKGT1BM97P2"},
|
|
||||||
|
|
||||||
{"IndirectInvalidCheckSum", "", "XE35ETHXREGGOPHERSSS"},
|
|
||||||
{"IndirectInvalidAssetIdentifier", "", "XE34ETHXREGGOPHERSSS"},
|
|
||||||
{"IndirectInvalidLength19", "", "XE34ETHXREGGOPHERSS"},
|
|
||||||
{"IndirectInvalidLength21", "", "XE34ETHXREGGOPHERSSSS"},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestICAPOK(t *testing.T) {
|
|
||||||
for _, test := range icapOKTests {
|
|
||||||
decodeEncodeTest(HexToAddress(test.addr), test.icap, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestICAPInvalid(t *testing.T) {
|
|
||||||
for _, test := range icapInvalidTests {
|
|
||||||
failedDecodingTest(test.icap, t)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func decodeEncodeTest(addr0 Address, icap0 string, t *testing.T) {
|
|
||||||
icap1, err := AddressToICAP(addr0)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("ICAP encoding failed: %s", err)
|
|
||||||
}
|
|
||||||
if icap1 != icap0 {
|
|
||||||
t.Errorf("ICAP mismatch: have: %s want: %s", icap1, icap0)
|
|
||||||
}
|
|
||||||
|
|
||||||
addr1, err := ICAPToAddress(icap0)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("ICAP decoding failed: %s", err)
|
|
||||||
}
|
|
||||||
if addr1 != addr0 {
|
|
||||||
t.Errorf("Address mismatch: have: %x want: %x", addr1, addr0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func failedDecodingTest(icap string, t *testing.T) {
|
|
||||||
addr, err := ICAPToAddress(icap)
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("Expected ICAP decoding to fail.")
|
|
||||||
}
|
|
||||||
if addr != (Address{}) {
|
|
||||||
t.Errorf("Expected empty Address on failed ICAP decoding.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
// Copyright 2014 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package common
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// The list type is an anonymous slice handler which can be used
|
|
||||||
// for containing any slice type to use in an environment which
|
|
||||||
// does not support slice types (e.g., JavaScript, QML)
|
|
||||||
type List struct {
|
|
||||||
mut sync.Mutex
|
|
||||||
val interface{}
|
|
||||||
list reflect.Value
|
|
||||||
Length int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialise a new list. Panics if non-slice type is given.
|
|
||||||
func NewList(t interface{}) *List {
|
|
||||||
list := reflect.ValueOf(t)
|
|
||||||
if list.Kind() != reflect.Slice {
|
|
||||||
panic("list container initialized with a non-slice type")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &List{sync.Mutex{}, t, list, list.Len()}
|
|
||||||
}
|
|
||||||
|
|
||||||
func EmptyList() *List {
|
|
||||||
return NewList([]interface{}{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get N element from the embedded slice. Returns nil if OOB.
|
|
||||||
func (self *List) Get(i int) interface{} {
|
|
||||||
if self.list.Len() > i {
|
|
||||||
self.mut.Lock()
|
|
||||||
defer self.mut.Unlock()
|
|
||||||
|
|
||||||
i := self.list.Index(i).Interface()
|
|
||||||
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *List) GetAsJson(i int) interface{} {
|
|
||||||
e := self.Get(i)
|
|
||||||
|
|
||||||
r, _ := json.Marshal(e)
|
|
||||||
|
|
||||||
return string(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Appends value at the end of the slice. Panics when incompatible value
|
|
||||||
// is given.
|
|
||||||
func (self *List) Append(v interface{}) {
|
|
||||||
self.mut.Lock()
|
|
||||||
defer self.mut.Unlock()
|
|
||||||
|
|
||||||
self.list = reflect.Append(self.list, reflect.ValueOf(v))
|
|
||||||
self.Length = self.list.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the underlying slice as interface.
|
|
||||||
func (self *List) Interface() interface{} {
|
|
||||||
return self.list.Interface()
|
|
||||||
}
|
|
||||||
|
|
||||||
// For JavaScript <3
|
|
||||||
func (self *List) ToJSON() string {
|
|
||||||
// make(T, 0) != nil
|
|
||||||
list := make([]interface{}, 0)
|
|
||||||
for i := 0; i < self.Length; i++ {
|
|
||||||
list = append(list, self.Get(i))
|
|
||||||
}
|
|
||||||
|
|
||||||
data, _ := json.Marshal(list)
|
|
||||||
|
|
||||||
return string(data)
|
|
||||||
}
|
|
||||||
179
common/math/big.go
Normal file
179
common/math/big.go
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
// Copyright 2017 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 math provides integer math utilities.
|
||||||
|
package math
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
tt255 = BigPow(2, 255)
|
||||||
|
tt256 = BigPow(2, 256)
|
||||||
|
tt256m1 = new(big.Int).Sub(tt256, big.NewInt(1))
|
||||||
|
MaxBig256 = new(big.Int).Set(tt256m1)
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// number of bits in a big.Word
|
||||||
|
wordBits = 32 << (uint64(^big.Word(0)) >> 63)
|
||||||
|
// number of bytes in a big.Word
|
||||||
|
wordBytes = wordBits / 8
|
||||||
|
)
|
||||||
|
|
||||||
|
// HexOrDecimal256 marshals big.Int as hex or decimal.
|
||||||
|
type HexOrDecimal256 big.Int
|
||||||
|
|
||||||
|
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||||
|
func (i *HexOrDecimal256) UnmarshalText(input []byte) error {
|
||||||
|
bigint, ok := ParseBig256(string(input))
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid hex or decimal integer %q", input)
|
||||||
|
}
|
||||||
|
*i = HexOrDecimal256(*bigint)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText implements encoding.TextMarshaler.
|
||||||
|
func (i *HexOrDecimal256) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(fmt.Sprintf("%#x", (*big.Int)(i))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseBig256 parses s as a 256 bit integer in decimal or hexadecimal syntax.
|
||||||
|
// Leading zeros are accepted. The empty string parses as zero.
|
||||||
|
func ParseBig256(s string) (*big.Int, bool) {
|
||||||
|
if s == "" {
|
||||||
|
return new(big.Int), true
|
||||||
|
}
|
||||||
|
var bigint *big.Int
|
||||||
|
var ok bool
|
||||||
|
if len(s) >= 2 && (s[:2] == "0x" || s[:2] == "0X") {
|
||||||
|
bigint, ok = new(big.Int).SetString(s[2:], 16)
|
||||||
|
} else {
|
||||||
|
bigint, ok = new(big.Int).SetString(s, 10)
|
||||||
|
}
|
||||||
|
if ok && bigint.BitLen() > 256 {
|
||||||
|
bigint, ok = nil, false
|
||||||
|
}
|
||||||
|
return bigint, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustParseBig parses s as a 256 bit big integer and panics if the string is invalid.
|
||||||
|
func MustParseBig256(s string) *big.Int {
|
||||||
|
v, ok := ParseBig256(s)
|
||||||
|
if !ok {
|
||||||
|
panic("invalid 256 bit integer: " + s)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// BigPow returns a ** b as a big integer.
|
||||||
|
func BigPow(a, b int64) *big.Int {
|
||||||
|
r := big.NewInt(a)
|
||||||
|
return r.Exp(r, big.NewInt(b), nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BigMax returns the larger of x or y.
|
||||||
|
func BigMax(x, y *big.Int) *big.Int {
|
||||||
|
if x.Cmp(y) < 0 {
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// BigMin returns the smaller of x or y.
|
||||||
|
func BigMin(x, y *big.Int) *big.Int {
|
||||||
|
if x.Cmp(y) > 0 {
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// FirstBitSet returns the index of the first 1 bit in v, counting from LSB.
|
||||||
|
func FirstBitSet(v *big.Int) int {
|
||||||
|
for i := 0; i < v.BitLen(); i++ {
|
||||||
|
if v.Bit(i) > 0 {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v.BitLen()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PaddedBigBytes encodes a big integer as a big-endian byte slice. The length
|
||||||
|
// of the slice is at least n bytes.
|
||||||
|
func PaddedBigBytes(bigint *big.Int, n int) []byte {
|
||||||
|
if bigint.BitLen()/8 >= n {
|
||||||
|
return bigint.Bytes()
|
||||||
|
}
|
||||||
|
ret := make([]byte, n)
|
||||||
|
ReadBits(bigint, ret)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadBits encodes the absolute value of bigint as big-endian bytes. Callers must ensure
|
||||||
|
// that buf has enough space. If buf is too short the result will be incomplete.
|
||||||
|
func ReadBits(bigint *big.Int, buf []byte) {
|
||||||
|
i := len(buf)
|
||||||
|
for _, d := range bigint.Bits() {
|
||||||
|
for j := 0; j < wordBytes && i > 0; j++ {
|
||||||
|
i--
|
||||||
|
buf[i] = byte(d)
|
||||||
|
d >>= 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// U256 encodes as a 256 bit two's complement number. This operation is destructive.
|
||||||
|
func U256(x *big.Int) *big.Int {
|
||||||
|
return x.And(x, tt256m1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// S256 interprets x as a two's complement number.
|
||||||
|
// x must not exceed 256 bits (the result is undefined if it does) and is not modified.
|
||||||
|
//
|
||||||
|
// S256(0) = 0
|
||||||
|
// S256(1) = 1
|
||||||
|
// S256(2**255) = -2**255
|
||||||
|
// S256(2**256-1) = -1
|
||||||
|
func S256(x *big.Int) *big.Int {
|
||||||
|
if x.Cmp(tt255) < 0 {
|
||||||
|
return x
|
||||||
|
} else {
|
||||||
|
return new(big.Int).Sub(x, tt256)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exp implements exponentiation by squaring.
|
||||||
|
// Exp returns a newly-allocated big integer and does not change
|
||||||
|
// base or exponent. The result is truncated to 256 bits.
|
||||||
|
//
|
||||||
|
// Courtesy @karalabe and @chfast
|
||||||
|
func Exp(base, exponent *big.Int) *big.Int {
|
||||||
|
result := big.NewInt(1)
|
||||||
|
|
||||||
|
for _, word := range exponent.Bits() {
|
||||||
|
for i := 0; i < wordBits; i++ {
|
||||||
|
if word&1 == 1 {
|
||||||
|
U256(result.Mul(result, base))
|
||||||
|
}
|
||||||
|
U256(base.Mul(base, base))
|
||||||
|
word >>= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
220
common/math/big_test.go
Normal file
220
common/math/big_test.go
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package math
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHexOrDecimal256(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input string
|
||||||
|
num *big.Int
|
||||||
|
ok bool
|
||||||
|
}{
|
||||||
|
{"", big.NewInt(0), true},
|
||||||
|
{"0", big.NewInt(0), true},
|
||||||
|
{"0x0", big.NewInt(0), true},
|
||||||
|
{"12345678", big.NewInt(12345678), true},
|
||||||
|
{"0x12345678", big.NewInt(0x12345678), true},
|
||||||
|
{"0X12345678", big.NewInt(0x12345678), true},
|
||||||
|
// Tests for leading zero behaviour:
|
||||||
|
{"0123456789", big.NewInt(123456789), true}, // note: not octal
|
||||||
|
{"00", big.NewInt(0), true},
|
||||||
|
{"0x00", big.NewInt(0), true},
|
||||||
|
{"0x012345678abc", big.NewInt(0x12345678abc), true},
|
||||||
|
// Invalid syntax:
|
||||||
|
{"abcdef", nil, false},
|
||||||
|
{"0xgg", nil, false},
|
||||||
|
// Larger than 256 bits:
|
||||||
|
{"115792089237316195423570985008687907853269984665640564039457584007913129639936", nil, false},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
var num HexOrDecimal256
|
||||||
|
err := num.UnmarshalText([]byte(test.input))
|
||||||
|
if (err == nil) != test.ok {
|
||||||
|
t.Errorf("ParseBig(%q) -> (err == nil) == %t, want %t", test.input, err == nil, test.ok)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if test.num != nil && (*big.Int)(&num).Cmp(test.num) != 0 {
|
||||||
|
t.Errorf("ParseBig(%q) -> %d, want %d", test.input, (*big.Int)(&num), test.num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMustParseBig256(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
if recover() == nil {
|
||||||
|
t.Error("MustParseBig should've panicked")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
MustParseBig256("ggg")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBigMax(t *testing.T) {
|
||||||
|
a := big.NewInt(10)
|
||||||
|
b := big.NewInt(5)
|
||||||
|
|
||||||
|
max1 := BigMax(a, b)
|
||||||
|
if max1 != a {
|
||||||
|
t.Errorf("Expected %d got %d", a, max1)
|
||||||
|
}
|
||||||
|
|
||||||
|
max2 := BigMax(b, a)
|
||||||
|
if max2 != a {
|
||||||
|
t.Errorf("Expected %d got %d", a, max2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBigMin(t *testing.T) {
|
||||||
|
a := big.NewInt(10)
|
||||||
|
b := big.NewInt(5)
|
||||||
|
|
||||||
|
min1 := BigMin(a, b)
|
||||||
|
if min1 != b {
|
||||||
|
t.Errorf("Expected %d got %d", b, min1)
|
||||||
|
}
|
||||||
|
|
||||||
|
min2 := BigMin(b, a)
|
||||||
|
if min2 != b {
|
||||||
|
t.Errorf("Expected %d got %d", b, min2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFirstBigSet(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
num *big.Int
|
||||||
|
ix int
|
||||||
|
}{
|
||||||
|
{big.NewInt(0), 0},
|
||||||
|
{big.NewInt(1), 0},
|
||||||
|
{big.NewInt(2), 1},
|
||||||
|
{big.NewInt(0x100), 8},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
if ix := FirstBitSet(test.num); ix != test.ix {
|
||||||
|
t.Errorf("FirstBitSet(b%b) = %d, want %d", test.num, ix, test.ix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPaddedBigBytes(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
num *big.Int
|
||||||
|
n int
|
||||||
|
result []byte
|
||||||
|
}{
|
||||||
|
{num: big.NewInt(0), n: 4, result: []byte{0, 0, 0, 0}},
|
||||||
|
{num: big.NewInt(1), n: 4, result: []byte{0, 0, 0, 1}},
|
||||||
|
{num: big.NewInt(512), n: 4, result: []byte{0, 0, 2, 0}},
|
||||||
|
{num: BigPow(2, 32), n: 4, result: []byte{1, 0, 0, 0, 0}},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
if result := PaddedBigBytes(test.num, test.n); !bytes.Equal(result, test.result) {
|
||||||
|
t.Errorf("PaddedBigBytes(%d, %d) = %v, want %v", test.num, test.n, result, test.result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkPaddedBigBytes(b *testing.B) {
|
||||||
|
bigint := MustParseBig256("123456789123456789123456789123456789")
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
PaddedBigBytes(bigint, 32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadBits(t *testing.T) {
|
||||||
|
check := func(input string) {
|
||||||
|
want, _ := hex.DecodeString(input)
|
||||||
|
int, _ := new(big.Int).SetString(input, 16)
|
||||||
|
buf := make([]byte, len(want))
|
||||||
|
ReadBits(int, buf)
|
||||||
|
if !bytes.Equal(buf, want) {
|
||||||
|
t.Errorf("have: %x\nwant: %x", buf, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check("000000000000000000000000000000000000000000000000000000FEFCF3F8F0")
|
||||||
|
check("0000000000012345000000000000000000000000000000000000FEFCF3F8F0")
|
||||||
|
check("18F8F8F1000111000110011100222004330052300000000000000000FEFCF3F8F0")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestU256(t *testing.T) {
|
||||||
|
tests := []struct{ x, y *big.Int }{
|
||||||
|
{x: big.NewInt(0), y: big.NewInt(0)},
|
||||||
|
{x: big.NewInt(1), y: big.NewInt(1)},
|
||||||
|
{x: BigPow(2, 255), y: BigPow(2, 255)},
|
||||||
|
{x: BigPow(2, 256), y: big.NewInt(0)},
|
||||||
|
{x: new(big.Int).Add(BigPow(2, 256), big.NewInt(1)), y: big.NewInt(1)},
|
||||||
|
// negative values
|
||||||
|
{x: big.NewInt(-1), y: new(big.Int).Sub(BigPow(2, 256), big.NewInt(1))},
|
||||||
|
{x: big.NewInt(-2), y: new(big.Int).Sub(BigPow(2, 256), big.NewInt(2))},
|
||||||
|
{x: BigPow(2, -255), y: big.NewInt(1)},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
if y := U256(new(big.Int).Set(test.x)); y.Cmp(test.y) != 0 {
|
||||||
|
t.Errorf("U256(%x) = %x, want %x", test.x, y, test.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestS256(t *testing.T) {
|
||||||
|
tests := []struct{ x, y *big.Int }{
|
||||||
|
{x: big.NewInt(0), y: big.NewInt(0)},
|
||||||
|
{x: big.NewInt(1), y: big.NewInt(1)},
|
||||||
|
{x: big.NewInt(2), y: big.NewInt(2)},
|
||||||
|
{
|
||||||
|
x: new(big.Int).Sub(BigPow(2, 255), big.NewInt(1)),
|
||||||
|
y: new(big.Int).Sub(BigPow(2, 255), big.NewInt(1)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: BigPow(2, 255),
|
||||||
|
y: new(big.Int).Neg(BigPow(2, 255)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: new(big.Int).Sub(BigPow(2, 256), big.NewInt(1)),
|
||||||
|
y: big.NewInt(-1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: new(big.Int).Sub(BigPow(2, 256), big.NewInt(2)),
|
||||||
|
y: big.NewInt(-2),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
if y := S256(test.x); y.Cmp(test.y) != 0 {
|
||||||
|
t.Errorf("S256(%x) = %x, want %x", test.x, y, test.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExp(t *testing.T) {
|
||||||
|
tests := []struct{ base, exponent, result *big.Int }{
|
||||||
|
{base: big.NewInt(0), exponent: big.NewInt(0), result: big.NewInt(1)},
|
||||||
|
{base: big.NewInt(1), exponent: big.NewInt(0), result: big.NewInt(1)},
|
||||||
|
{base: big.NewInt(1), exponent: big.NewInt(1), result: big.NewInt(1)},
|
||||||
|
{base: big.NewInt(1), exponent: big.NewInt(2), result: big.NewInt(1)},
|
||||||
|
{base: big.NewInt(3), exponent: big.NewInt(144), result: MustParseBig256("507528786056415600719754159741696356908742250191663887263627442114881")},
|
||||||
|
{base: big.NewInt(2), exponent: big.NewInt(255), result: MustParseBig256("57896044618658097711785492504343953926634992332820282019728792003956564819968")},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
if result := Exp(test.base, test.exponent); result.Cmp(test.result) != 0 {
|
||||||
|
t.Errorf("Exp(%d, %d) = %d, want %d", test.base, test.exponent, result, test.result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package math
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Summer interface {
|
|
||||||
Sum(i int) *big.Int
|
|
||||||
Len() int
|
|
||||||
}
|
|
||||||
|
|
||||||
func Sum(slice Summer) (sum *big.Int) {
|
|
||||||
sum = new(big.Int)
|
|
||||||
|
|
||||||
for i := 0; i < slice.Len(); i++ {
|
|
||||||
sum.Add(sum, slice.Sum(i))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
type Vector struct {
|
|
||||||
Gas, Price *big.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
type VectorsBy func(v1, v2 Vector) bool
|
|
||||||
|
|
||||||
func (self VectorsBy) Sort(vectors []Vector) {
|
|
||||||
bs := vectorSorter{
|
|
||||||
vectors: vectors,
|
|
||||||
by: self,
|
|
||||||
}
|
|
||||||
sort.Sort(bs)
|
|
||||||
}
|
|
||||||
|
|
||||||
type vectorSorter struct {
|
|
||||||
vectors []Vector
|
|
||||||
by func(v1, v2 Vector) bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v vectorSorter) Len() int { return len(v.vectors) }
|
|
||||||
func (v vectorSorter) Less(i, j int) bool { return v.by(v.vectors[i], v.vectors[j]) }
|
|
||||||
func (v vectorSorter) Swap(i, j int) { v.vectors[i], v.vectors[j] = v.vectors[j], v.vectors[i] }
|
|
||||||
|
|
||||||
func PriceSort(v1, v2 Vector) bool { return v1.Price.Cmp(v2.Price) < 0 }
|
|
||||||
func GasSort(v1, v2 Vector) bool { return v1.Gas.Cmp(v2.Gas) < 0 }
|
|
||||||
|
|
||||||
type vectorSummer struct {
|
|
||||||
vectors []Vector
|
|
||||||
by func(v Vector) *big.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
type VectorSum func(v Vector) *big.Int
|
|
||||||
|
|
||||||
func (v VectorSum) Sum(vectors []Vector) *big.Int {
|
|
||||||
vs := vectorSummer{
|
|
||||||
vectors: vectors,
|
|
||||||
by: v,
|
|
||||||
}
|
|
||||||
return Sum(vs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v vectorSummer) Len() int { return len(v.vectors) }
|
|
||||||
func (v vectorSummer) Sum(i int) *big.Int { return v.by(v.vectors[i]) }
|
|
||||||
|
|
||||||
func GasSum(v Vector) *big.Int { return v.Gas }
|
|
||||||
|
|
||||||
var etherInWei = new(big.Rat).SetInt(common.String2Big("1000000000000000000"))
|
|
||||||
|
|
||||||
func GasPrice(bp, gl, ep *big.Int) *big.Int {
|
|
||||||
BP := new(big.Rat).SetInt(bp)
|
|
||||||
GL := new(big.Rat).SetInt(gl)
|
|
||||||
EP := new(big.Rat).SetInt(ep)
|
|
||||||
GP := new(big.Rat).Quo(BP, GL)
|
|
||||||
GP = GP.Quo(GP, EP)
|
|
||||||
|
|
||||||
return GP.Mul(GP, etherInWei).Num()
|
|
||||||
}
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package math
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type summer struct {
|
|
||||||
numbers []*big.Int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s summer) Len() int { return len(s.numbers) }
|
|
||||||
func (s summer) Sum(i int) *big.Int {
|
|
||||||
return s.numbers[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSum(t *testing.T) {
|
|
||||||
summer := summer{numbers: []*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}}
|
|
||||||
sum := Sum(summer)
|
|
||||||
if sum.Cmp(big.NewInt(6)) != 0 {
|
|
||||||
t.Errorf("got sum = %d, want 6", sum)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDist(t *testing.T) {
|
|
||||||
var vectors = []Vector{
|
|
||||||
{big.NewInt(1000), big.NewInt(1234)},
|
|
||||||
{big.NewInt(500), big.NewInt(10023)},
|
|
||||||
{big.NewInt(1034), big.NewInt(1987)},
|
|
||||||
{big.NewInt(1034), big.NewInt(1987)},
|
|
||||||
{big.NewInt(8983), big.NewInt(1977)},
|
|
||||||
{big.NewInt(98382), big.NewInt(1887)},
|
|
||||||
{big.NewInt(12398), big.NewInt(1287)},
|
|
||||||
{big.NewInt(12398), big.NewInt(1487)},
|
|
||||||
{big.NewInt(12398), big.NewInt(1987)},
|
|
||||||
{big.NewInt(12398), big.NewInt(128)},
|
|
||||||
{big.NewInt(12398), big.NewInt(1987)},
|
|
||||||
{big.NewInt(1398), big.NewInt(187)},
|
|
||||||
{big.NewInt(12328), big.NewInt(1927)},
|
|
||||||
{big.NewInt(12398), big.NewInt(1987)},
|
|
||||||
{big.NewInt(22398), big.NewInt(1287)},
|
|
||||||
{big.NewInt(1370), big.NewInt(1981)},
|
|
||||||
{big.NewInt(12398), big.NewInt(1957)},
|
|
||||||
{big.NewInt(42198), big.NewInt(1987)},
|
|
||||||
}
|
|
||||||
|
|
||||||
VectorsBy(GasSort).Sort(vectors)
|
|
||||||
fmt.Println(vectors)
|
|
||||||
|
|
||||||
BP := big.NewInt(15)
|
|
||||||
GL := big.NewInt(1000000)
|
|
||||||
EP := big.NewInt(100)
|
|
||||||
fmt.Println("BP", BP, "GL", GL, "EP", EP)
|
|
||||||
GP := GasPrice(BP, GL, EP)
|
|
||||||
fmt.Println("GP =", GP, "Wei per GU")
|
|
||||||
|
|
||||||
S := len(vectors) / 4
|
|
||||||
fmt.Println("L", len(vectors), "S", S)
|
|
||||||
for i := 1; i <= S*4; i += S {
|
|
||||||
fmt.Printf("T%d = %v\n", i, vectors[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
g := VectorSum(GasSum).Sum(vectors)
|
|
||||||
fmt.Printf("G = ∑g* (%v)\n", g)
|
|
||||||
}
|
|
||||||
99
common/math/integer.go
Normal file
99
common/math/integer.go
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
// Copyright 2017 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 math
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Integer limit values.
|
||||||
|
MaxInt8 = 1<<7 - 1
|
||||||
|
MinInt8 = -1 << 7
|
||||||
|
MaxInt16 = 1<<15 - 1
|
||||||
|
MinInt16 = -1 << 15
|
||||||
|
MaxInt32 = 1<<31 - 1
|
||||||
|
MinInt32 = -1 << 31
|
||||||
|
MaxInt64 = 1<<63 - 1
|
||||||
|
MinInt64 = -1 << 63
|
||||||
|
MaxUint8 = 1<<8 - 1
|
||||||
|
MaxUint16 = 1<<16 - 1
|
||||||
|
MaxUint32 = 1<<32 - 1
|
||||||
|
MaxUint64 = 1<<64 - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// HexOrDecimal64 marshals uint64 as hex or decimal.
|
||||||
|
type HexOrDecimal64 uint64
|
||||||
|
|
||||||
|
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||||
|
func (i *HexOrDecimal64) UnmarshalText(input []byte) error {
|
||||||
|
int, ok := ParseUint64(string(input))
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid hex or decimal integer %q", input)
|
||||||
|
}
|
||||||
|
*i = HexOrDecimal64(int)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText implements encoding.TextMarshaler.
|
||||||
|
func (i HexOrDecimal64) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(fmt.Sprintf("%#x", uint64(i))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseUint64 parses s as an integer in decimal or hexadecimal syntax.
|
||||||
|
// Leading zeros are accepted. The empty string parses as zero.
|
||||||
|
func ParseUint64(s string) (uint64, bool) {
|
||||||
|
if s == "" {
|
||||||
|
return 0, true
|
||||||
|
}
|
||||||
|
if len(s) >= 2 && (s[:2] == "0x" || s[:2] == "0X") {
|
||||||
|
v, err := strconv.ParseUint(s[2:], 16, 64)
|
||||||
|
return v, err == nil
|
||||||
|
}
|
||||||
|
v, err := strconv.ParseUint(s, 10, 64)
|
||||||
|
return v, err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustParseUint64 parses s as an integer and panics if the string is invalid.
|
||||||
|
func MustParseUint64(s string) uint64 {
|
||||||
|
v, ok := ParseUint64(s)
|
||||||
|
if !ok {
|
||||||
|
panic("invalid unsigned 64 bit integer: " + s)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: The following methods need to be optimised using either bit checking or asm
|
||||||
|
|
||||||
|
// SafeSub returns subtraction result and whether overflow occurred.
|
||||||
|
func SafeSub(x, y uint64) (uint64, bool) {
|
||||||
|
return x - y, x < y
|
||||||
|
}
|
||||||
|
|
||||||
|
// SafeAdd returns the result and whether overflow occurred.
|
||||||
|
func SafeAdd(x, y uint64) (uint64, bool) {
|
||||||
|
return x + y, y > MaxUint64-x
|
||||||
|
}
|
||||||
|
|
||||||
|
// SafeMul returns multiplication result and whether overflow occurred.
|
||||||
|
func SafeMul(x, y uint64) (uint64, bool) {
|
||||||
|
if x == 0 || y == 0 {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return x * y, y > MaxUint64/x
|
||||||
|
}
|
||||||
116
common/math/integer_test.go
Normal file
116
common/math/integer_test.go
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
// Copyright 2017 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 math
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type operation byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
sub operation = iota
|
||||||
|
add
|
||||||
|
mul
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOverflow(t *testing.T) {
|
||||||
|
for i, test := range []struct {
|
||||||
|
x uint64
|
||||||
|
y uint64
|
||||||
|
overflow bool
|
||||||
|
op operation
|
||||||
|
}{
|
||||||
|
// add operations
|
||||||
|
{MaxUint64, 1, true, add},
|
||||||
|
{MaxUint64 - 1, 1, false, add},
|
||||||
|
|
||||||
|
// sub operations
|
||||||
|
{0, 1, true, sub},
|
||||||
|
{0, 0, false, sub},
|
||||||
|
|
||||||
|
// mul operations
|
||||||
|
{0, 0, false, mul},
|
||||||
|
{10, 10, false, mul},
|
||||||
|
{MaxUint64, 2, true, mul},
|
||||||
|
{MaxUint64, 1, false, mul},
|
||||||
|
} {
|
||||||
|
var overflows bool
|
||||||
|
switch test.op {
|
||||||
|
case sub:
|
||||||
|
_, overflows = SafeSub(test.x, test.y)
|
||||||
|
case add:
|
||||||
|
_, overflows = SafeAdd(test.x, test.y)
|
||||||
|
case mul:
|
||||||
|
_, overflows = SafeMul(test.x, test.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.overflow != overflows {
|
||||||
|
t.Errorf("%d failed. Expected test to be %v, got %v", i, test.overflow, overflows)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHexOrDecimal64(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input string
|
||||||
|
num uint64
|
||||||
|
ok bool
|
||||||
|
}{
|
||||||
|
{"", 0, true},
|
||||||
|
{"0", 0, true},
|
||||||
|
{"0x0", 0, true},
|
||||||
|
{"12345678", 12345678, true},
|
||||||
|
{"0x12345678", 0x12345678, true},
|
||||||
|
{"0X12345678", 0x12345678, true},
|
||||||
|
// Tests for leading zero behaviour:
|
||||||
|
{"0123456789", 123456789, true}, // note: not octal
|
||||||
|
{"0x00", 0, true},
|
||||||
|
{"0x012345678abc", 0x12345678abc, true},
|
||||||
|
// Invalid syntax:
|
||||||
|
{"abcdef", 0, false},
|
||||||
|
{"0xgg", 0, false},
|
||||||
|
// Doesn't fit into 64 bits:
|
||||||
|
{"18446744073709551617", 0, false},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
var num HexOrDecimal64
|
||||||
|
err := num.UnmarshalText([]byte(test.input))
|
||||||
|
if (err == nil) != test.ok {
|
||||||
|
t.Errorf("ParseUint64(%q) -> (err == nil) = %t, want %t", test.input, err == nil, test.ok)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err == nil && uint64(num) != test.num {
|
||||||
|
t.Errorf("ParseUint64(%q) -> %d, want %d", test.input, num, test.num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMustParseUint64(t *testing.T) {
|
||||||
|
if v := MustParseUint64("12345"); v != 12345 {
|
||||||
|
t.Errorf(`MustParseUint64("12345") = %d, want 12345`, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMustParseUint64Panic(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
if recover() == nil {
|
||||||
|
t.Error("MustParseBig should've panicked")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
MustParseUint64("ggg")
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,6 @@ package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type StorageSize float64
|
type StorageSize float64
|
||||||
|
|
@ -36,54 +35,3 @@ func (self StorageSize) String() string {
|
||||||
func (self StorageSize) Int64() int64 {
|
func (self StorageSize) Int64() int64 {
|
||||||
return int64(self)
|
return int64(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
// The different number of units
|
|
||||||
var (
|
|
||||||
Douglas = BigPow(10, 42)
|
|
||||||
Einstein = BigPow(10, 21)
|
|
||||||
Ether = BigPow(10, 18)
|
|
||||||
Finney = BigPow(10, 15)
|
|
||||||
Szabo = BigPow(10, 12)
|
|
||||||
Shannon = BigPow(10, 9)
|
|
||||||
Babbage = BigPow(10, 6)
|
|
||||||
Ada = BigPow(10, 3)
|
|
||||||
Wei = big.NewInt(1)
|
|
||||||
)
|
|
||||||
|
|
||||||
//
|
|
||||||
// Currency to string
|
|
||||||
// Returns a string representing a human readable format
|
|
||||||
func CurrencyToString(num *big.Int) string {
|
|
||||||
var (
|
|
||||||
fin *big.Int = num
|
|
||||||
denom string = "Wei"
|
|
||||||
)
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case num.Cmp(Ether) >= 0:
|
|
||||||
fin = new(big.Int).Div(num, Ether)
|
|
||||||
denom = "Ether"
|
|
||||||
case num.Cmp(Finney) >= 0:
|
|
||||||
fin = new(big.Int).Div(num, Finney)
|
|
||||||
denom = "Finney"
|
|
||||||
case num.Cmp(Szabo) >= 0:
|
|
||||||
fin = new(big.Int).Div(num, Szabo)
|
|
||||||
denom = "Szabo"
|
|
||||||
case num.Cmp(Shannon) >= 0:
|
|
||||||
fin = new(big.Int).Div(num, Shannon)
|
|
||||||
denom = "Shannon"
|
|
||||||
case num.Cmp(Babbage) >= 0:
|
|
||||||
fin = new(big.Int).Div(num, Babbage)
|
|
||||||
denom = "Babbage"
|
|
||||||
case num.Cmp(Ada) >= 0:
|
|
||||||
fin = new(big.Int).Div(num, Ada)
|
|
||||||
denom = "Ada"
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO add comment clarifying expected behavior
|
|
||||||
if len(fin.String()) > 5 {
|
|
||||||
return fmt.Sprintf("%sE%d %s", fin.String()[0:5], len(fin.String())-5, denom)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%v %s", fin, denom)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -17,43 +17,22 @@
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"testing"
|
||||||
|
|
||||||
checker "gopkg.in/check.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SizeSuite struct{}
|
func TestStorageSizeString(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
size StorageSize
|
||||||
|
str string
|
||||||
|
}{
|
||||||
|
{2381273, "2.38 mB"},
|
||||||
|
{2192, "2.19 kB"},
|
||||||
|
{12, "12.00 B"},
|
||||||
|
}
|
||||||
|
|
||||||
var _ = checker.Suite(&SizeSuite{})
|
for _, test := range tests {
|
||||||
|
if test.size.String() != test.str {
|
||||||
func (s *SizeSuite) TestStorageSizeString(c *checker.C) {
|
t.Errorf("%f: got %q, want %q", float64(test.size), test.size.String(), test.str)
|
||||||
data1 := 2381273
|
}
|
||||||
data2 := 2192
|
}
|
||||||
data3 := 12
|
|
||||||
|
|
||||||
exp1 := "2.38 mB"
|
|
||||||
exp2 := "2.19 kB"
|
|
||||||
exp3 := "12.00 B"
|
|
||||||
|
|
||||||
c.Assert(StorageSize(data1).String(), checker.Equals, exp1)
|
|
||||||
c.Assert(StorageSize(data2).String(), checker.Equals, exp2)
|
|
||||||
c.Assert(StorageSize(data3).String(), checker.Equals, exp3)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SizeSuite) TestCommon(c *checker.C) {
|
|
||||||
ether := CurrencyToString(BigPow(10, 19))
|
|
||||||
finney := CurrencyToString(BigPow(10, 16))
|
|
||||||
szabo := CurrencyToString(BigPow(10, 13))
|
|
||||||
shannon := CurrencyToString(BigPow(10, 10))
|
|
||||||
babbage := CurrencyToString(BigPow(10, 7))
|
|
||||||
ada := CurrencyToString(BigPow(10, 4))
|
|
||||||
wei := CurrencyToString(big.NewInt(10))
|
|
||||||
|
|
||||||
c.Assert(ether, checker.Equals, "10 Ether")
|
|
||||||
c.Assert(finney, checker.Equals, "10 Finney")
|
|
||||||
c.Assert(szabo, checker.Equals, "10 Szabo")
|
|
||||||
c.Assert(shannon, checker.Equals, "10 Shannon")
|
|
||||||
c.Assert(babbage, checker.Equals, "10 Babbage")
|
|
||||||
c.Assert(ada, checker.Equals, "10 Ada")
|
|
||||||
c.Assert(wei, checker.Equals, "10 Wei")
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
103
common/types.go
103
common/types.go
|
|
@ -17,6 +17,7 @@
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
@ -30,13 +31,8 @@ const (
|
||||||
AddressLength = 20
|
AddressLength = 20
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
|
||||||
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
|
type Hash [HashLength]byte
|
||||||
Hash [HashLength]byte
|
|
||||||
|
|
||||||
// Address represents the 20 byte address of an Ethereum account.
|
|
||||||
Address [AddressLength]byte
|
|
||||||
)
|
|
||||||
|
|
||||||
func BytesToHash(b []byte) Hash {
|
func BytesToHash(b []byte) Hash {
|
||||||
var h Hash
|
var h Hash
|
||||||
|
|
@ -47,22 +43,38 @@ func StringToHash(s string) Hash { return BytesToHash([]byte(s)) }
|
||||||
func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
|
func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) }
|
||||||
func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
|
func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
|
||||||
|
|
||||||
// Don't use the default 'String' method in case we want to overwrite
|
|
||||||
|
|
||||||
// Get the string representation of the underlying hash
|
// Get the string representation of the underlying hash
|
||||||
func (h Hash) Str() string { return string(h[:]) }
|
func (h Hash) Str() string { return string(h[:]) }
|
||||||
func (h Hash) Bytes() []byte { return h[:] }
|
func (h Hash) Bytes() []byte { return h[:] }
|
||||||
func (h Hash) Big() *big.Int { return Bytes2Big(h[:]) }
|
func (h Hash) Big() *big.Int { return new(big.Int).SetBytes(h[:]) }
|
||||||
func (h Hash) Hex() string { return hexutil.Encode(h[:]) }
|
func (h Hash) Hex() string { return hexutil.Encode(h[:]) }
|
||||||
|
|
||||||
// UnmarshalJSON parses a hash in its hex from to a hash.
|
// TerminalString implements log.TerminalStringer, formatting a string for console
|
||||||
func (h *Hash) UnmarshalJSON(input []byte) error {
|
// output during logging.
|
||||||
return hexutil.UnmarshalJSON("Hash", input, h[:])
|
func (h Hash) TerminalString() string {
|
||||||
|
return fmt.Sprintf("%x…%x", h[:3], h[29:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize given hash to JSON
|
// String implements the stringer interface and is used also by the logger when
|
||||||
func (h Hash) MarshalJSON() ([]byte, error) {
|
// doing full logging into a file.
|
||||||
return hexutil.Bytes(h[:]).MarshalJSON()
|
func (h Hash) String() string {
|
||||||
|
return h.Hex()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format implements fmt.Formatter, forcing the byte slice to be formatted as is,
|
||||||
|
// without going through the stringer interface used for logging.
|
||||||
|
func (h Hash) Format(s fmt.State, c rune) {
|
||||||
|
fmt.Fprintf(s, "%"+string(c), h[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText parses a hash in hex syntax.
|
||||||
|
func (h *Hash) UnmarshalText(input []byte) error {
|
||||||
|
return hexutil.UnmarshalFixedText("Hash", input, h[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText returns the hex representation of h.
|
||||||
|
func (h Hash) MarshalText() ([]byte, error) {
|
||||||
|
return hexutil.Bytes(h[:]).MarshalText()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the hash to the value of b. If b is larger than len(h) it will panic
|
// Sets the hash to the value of b. If b is larger than len(h) it will panic
|
||||||
|
|
@ -97,7 +109,24 @@ func EmptyHash(h Hash) bool {
|
||||||
return h == Hash{}
|
return h == Hash{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnprefixedHash allows marshaling a Hash without 0x prefix.
|
||||||
|
type UnprefixedHash Hash
|
||||||
|
|
||||||
|
// UnmarshalText decodes the hash from hex. The 0x prefix is optional.
|
||||||
|
func (h *UnprefixedHash) UnmarshalText(input []byte) error {
|
||||||
|
return hexutil.UnmarshalFixedUnprefixedText("UnprefixedHash", input, h[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText encodes the hash as hex.
|
||||||
|
func (h UnprefixedHash) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(hex.EncodeToString(h[:])), nil
|
||||||
|
}
|
||||||
|
|
||||||
/////////// Address
|
/////////// Address
|
||||||
|
|
||||||
|
// Address represents the 20 byte address of an Ethereum account.
|
||||||
|
type Address [AddressLength]byte
|
||||||
|
|
||||||
func BytesToAddress(b []byte) Address {
|
func BytesToAddress(b []byte) Address {
|
||||||
var a Address
|
var a Address
|
||||||
a.SetBytes(b)
|
a.SetBytes(b)
|
||||||
|
|
@ -122,10 +151,21 @@ func IsHexAddress(s string) bool {
|
||||||
// Get the string representation of the underlying address
|
// Get the string representation of the underlying address
|
||||||
func (a Address) Str() string { return string(a[:]) }
|
func (a Address) Str() string { return string(a[:]) }
|
||||||
func (a Address) Bytes() []byte { return a[:] }
|
func (a Address) Bytes() []byte { return a[:] }
|
||||||
func (a Address) Big() *big.Int { return Bytes2Big(a[:]) }
|
func (a Address) Big() *big.Int { return new(big.Int).SetBytes(a[:]) }
|
||||||
func (a Address) Hash() Hash { return BytesToHash(a[:]) }
|
func (a Address) Hash() Hash { return BytesToHash(a[:]) }
|
||||||
func (a Address) Hex() string { return hexutil.Encode(a[:]) }
|
func (a Address) Hex() string { return hexutil.Encode(a[:]) }
|
||||||
|
|
||||||
|
// String implements the stringer interface and is used also by the logger.
|
||||||
|
func (a Address) String() string {
|
||||||
|
return a.Hex()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format implements fmt.Formatter, forcing the byte slice to be formatted as is,
|
||||||
|
// without going through the stringer interface used for logging.
|
||||||
|
func (a Address) Format(s fmt.State, c rune) {
|
||||||
|
fmt.Fprintf(s, "%"+string(c), a[:])
|
||||||
|
}
|
||||||
|
|
||||||
// Sets the address to the value of b. If b is larger than len(a) it will panic
|
// Sets the address to the value of b. If b is larger than len(a) it will panic
|
||||||
func (a *Address) SetBytes(b []byte) {
|
func (a *Address) SetBytes(b []byte) {
|
||||||
if len(b) > len(a) {
|
if len(b) > len(a) {
|
||||||
|
|
@ -144,22 +184,25 @@ func (a *Address) Set(other Address) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Serialize given address to JSON
|
// MarshalText returns the hex representation of a.
|
||||||
func (a Address) MarshalJSON() ([]byte, error) {
|
func (a Address) MarshalText() ([]byte, error) {
|
||||||
return hexutil.Bytes(a[:]).MarshalJSON()
|
return hexutil.Bytes(a[:]).MarshalText()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse address from raw json data
|
// UnmarshalText parses a hash in hex syntax.
|
||||||
func (a *Address) UnmarshalJSON(input []byte) error {
|
func (a *Address) UnmarshalText(input []byte) error {
|
||||||
return hexutil.UnmarshalJSON("Address", input, a[:])
|
return hexutil.UnmarshalFixedText("Address", input, a[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// PP Pretty Prints a byte slice in the following format:
|
// UnprefixedHash allows marshaling an Address without 0x prefix.
|
||||||
// hex(value[:4])...(hex[len(value)-4:])
|
type UnprefixedAddress Address
|
||||||
func PP(value []byte) string {
|
|
||||||
if len(value) <= 8 {
|
|
||||||
return Bytes2Hex(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%x...%x", value[:4], value[len(value)-4])
|
// UnmarshalText decodes the address from hex. The 0x prefix is optional.
|
||||||
|
func (a *UnprefixedAddress) UnmarshalText(input []byte) error {
|
||||||
|
return hexutil.UnmarshalFixedUnprefixedText("UnprefixedAddress", input, a[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText encodes the address as hex.
|
||||||
|
func (a UnprefixedAddress) MarshalText() ([]byte, error) {
|
||||||
|
return []byte(hex.EncodeToString(a[:])), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ func HexTo_N_(s string) _N_ { return BytesTo_N_(FromHex(s)) }
|
||||||
// Get the string representation of the underlying hash
|
// Get the string representation of the underlying hash
|
||||||
func (h _N_) Str() string { return string(h[:]) }
|
func (h _N_) Str() string { return string(h[:]) }
|
||||||
func (h _N_) Bytes() []byte { return h[:] }
|
func (h _N_) Bytes() []byte { return h[:] }
|
||||||
func (h _N_) Big() *big.Int { return Bytes2Big(h[:]) }
|
func (h _N_) Big() *big.Int { return new(big.Int).SetBytes(h[:]) }
|
||||||
func (h _N_) Hex() string { return "0x" + Bytes2Hex(h[:]) }
|
func (h _N_) Hex() string { return "0x" + Bytes2Hex(h[:]) }
|
||||||
|
|
||||||
// Sets the hash to the value of b. If b is larger than len(h) it will panic
|
// Sets the hash to the value of b. If b is larger than len(h) it will panic
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -37,7 +38,6 @@ func TestBytesConversion(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHashJsonValidation(t *testing.T) {
|
func TestHashJsonValidation(t *testing.T) {
|
||||||
var h Hash
|
|
||||||
var tests = []struct {
|
var tests = []struct {
|
||||||
Prefix string
|
Prefix string
|
||||||
Size int
|
Size int
|
||||||
|
|
@ -52,7 +52,8 @@ func TestHashJsonValidation(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
input := `"` + test.Prefix + strings.Repeat("0", test.Size) + `"`
|
input := `"` + test.Prefix + strings.Repeat("0", test.Size) + `"`
|
||||||
err := h.UnmarshalJSON([]byte(input))
|
var v Hash
|
||||||
|
err := json.Unmarshal([]byte(input), &v)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if test.Error != "" {
|
if test.Error != "" {
|
||||||
t.Errorf("%s: error mismatch: have nil, want %q", input, test.Error)
|
t.Errorf("%s: error mismatch: have nil, want %q", input, test.Error)
|
||||||
|
|
@ -66,7 +67,6 @@ func TestHashJsonValidation(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAddressUnmarshalJSON(t *testing.T) {
|
func TestAddressUnmarshalJSON(t *testing.T) {
|
||||||
var a Address
|
|
||||||
var tests = []struct {
|
var tests = []struct {
|
||||||
Input string
|
Input string
|
||||||
ShouldErr bool
|
ShouldErr bool
|
||||||
|
|
@ -81,7 +81,8 @@ func TestAddressUnmarshalJSON(t *testing.T) {
|
||||||
{`"0x0000000000000000000000000000000000000010"`, false, big.NewInt(16)},
|
{`"0x0000000000000000000000000000000000000010"`, false, big.NewInt(16)},
|
||||||
}
|
}
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
err := a.UnmarshalJSON([]byte(test.Input))
|
var v Address
|
||||||
|
err := json.Unmarshal([]byte(test.Input), &v)
|
||||||
if err != nil && !test.ShouldErr {
|
if err != nil && !test.ShouldErr {
|
||||||
t.Errorf("test #%d: unexpected error: %v", i, err)
|
t.Errorf("test #%d: unexpected error: %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
@ -89,8 +90,8 @@ func TestAddressUnmarshalJSON(t *testing.T) {
|
||||||
if test.ShouldErr {
|
if test.ShouldErr {
|
||||||
t.Errorf("test #%d: expected error, got none", i)
|
t.Errorf("test #%d: expected error, got none", i)
|
||||||
}
|
}
|
||||||
if a.Big().Cmp(test.Output) != 0 {
|
if v.Big().Cmp(test.Output) != 0 {
|
||||||
t.Errorf("test #%d: address mismatch: have %v, want %v", i, a.Big(), test.Output)
|
t.Errorf("test #%d: address mismatch: have %v, want %v", i, v.Big(), test.Output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"github.com/robertkrimen/otto"
|
"github.com/robertkrimen/otto"
|
||||||
)
|
)
|
||||||
|
|
@ -306,7 +305,7 @@ func setError(resp *otto.Object, code int, msg string) {
|
||||||
func throwJSException(msg interface{}) otto.Value {
|
func throwJSException(msg interface{}) otto.Value {
|
||||||
val, err := otto.ToValue(msg)
|
val, err := otto.ToValue(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Error).Infof("Failed to serialize JavaScript exception %v: %v", msg, err)
|
log.Error("Failed to serialize JavaScript exception", "exception", msg, "err", err)
|
||||||
}
|
}
|
||||||
panic(val)
|
panic(val)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,17 +21,16 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/internal/jsre"
|
"github.com/ethereum/go-ethereum/internal/jsre"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -97,7 +96,7 @@ func newTester(t *testing.T, confOverride func(*eth.Config)) *tester {
|
||||||
t.Fatalf("failed to create node: %v", err)
|
t.Fatalf("failed to create node: %v", err)
|
||||||
}
|
}
|
||||||
ethConf := ð.Config{
|
ethConf := ð.Config{
|
||||||
ChainConfig: ¶ms.ChainConfig{HomesteadBlock: new(big.Int), ChainId: new(big.Int)},
|
Genesis: core.DevGenesisBlock(),
|
||||||
Etherbase: common.HexToAddress(testAddress),
|
Etherbase: common.HexToAddress(testAddress),
|
||||||
PowTest: true,
|
PowTest: true,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ package chequebook
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -37,13 +38,12 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/contracts/chequebook/contract"
|
"github.com/ethereum/go-ethereum/contracts/chequebook/contract"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/swarm/services/swap/swap"
|
"github.com/ethereum/go-ethereum/swarm/services/swap/swap"
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(zelig): watch peer solvency and notify of bouncing cheques
|
// TODO(zelig): watch peer solvency and notify of bouncing cheques
|
||||||
|
|
@ -105,6 +105,8 @@ type Chequebook struct {
|
||||||
txhash string // tx hash of last deposit tx
|
txhash string // tx hash of last deposit tx
|
||||||
threshold *big.Int // threshold that triggers autodeposit if not nil
|
threshold *big.Int // threshold that triggers autodeposit if not nil
|
||||||
buffer *big.Int // buffer to keep on top of balance for fork protection
|
buffer *big.Int // buffer to keep on top of balance for fork protection
|
||||||
|
|
||||||
|
log log.Logger // contextual logger with the contrac address embedded
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Chequebook) String() string {
|
func (self *Chequebook) String() string {
|
||||||
|
|
@ -136,11 +138,12 @@ func NewChequebook(path string, contractAddr common.Address, prvKey *ecdsa.Priva
|
||||||
owner: transactOpts.From,
|
owner: transactOpts.From,
|
||||||
contract: chbook,
|
contract: chbook,
|
||||||
session: session,
|
session: session,
|
||||||
|
log: log.New("contract", contractAddr),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contractAddr != common.Address{}) {
|
if (contractAddr != common.Address{}) {
|
||||||
self.setBalanceFromBlockChain()
|
self.setBalanceFromBlockChain()
|
||||||
glog.V(logger.Detail).Infof("new chequebook initialised from %s (owner: %v, balance: %s)", contractAddr.Hex(), self.owner.Hex(), self.balance.String())
|
self.log.Trace("New chequebook initialised", "owner", self.owner, "balance", self.balance)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -148,7 +151,7 @@ func NewChequebook(path string, contractAddr common.Address, prvKey *ecdsa.Priva
|
||||||
func (self *Chequebook) setBalanceFromBlockChain() {
|
func (self *Chequebook) setBalanceFromBlockChain() {
|
||||||
balance, err := self.backend.BalanceAt(context.TODO(), self.contractAddr, nil)
|
balance, err := self.backend.BalanceAt(context.TODO(), self.contractAddr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Error).Infof("can't get balance: %v", err)
|
log.Error("Failed to retrieve chequebook balance", "err", err)
|
||||||
} else {
|
} else {
|
||||||
self.balance.Set(balance)
|
self.balance.Set(balance)
|
||||||
}
|
}
|
||||||
|
|
@ -161,7 +164,6 @@ func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend Backend, chec
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
self, _ = NewChequebook(path, common.Address{}, prvKey, backend)
|
self, _ = NewChequebook(path, common.Address{}, prvKey, backend)
|
||||||
|
|
||||||
err = json.Unmarshal(data, self)
|
err = json.Unmarshal(data, self)
|
||||||
|
|
@ -171,8 +173,7 @@ func LoadChequebook(path string, prvKey *ecdsa.PrivateKey, backend Backend, chec
|
||||||
if checkBalance {
|
if checkBalance {
|
||||||
self.setBalanceFromBlockChain()
|
self.setBalanceFromBlockChain()
|
||||||
}
|
}
|
||||||
|
log.Trace("Loaded chequebook from disk", "path", path)
|
||||||
glog.V(logger.Detail).Infof("loaded chequebook (%s, owner: %v, balance: %v) initialised from %v", self.contractAddr.Hex(), self.owner.Hex(), self.balance, path)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -227,7 +228,7 @@ func (self *Chequebook) Save() (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
glog.V(logger.Detail).Infof("saving chequebook (%s) to %v", self.contractAddr.Hex(), self.path)
|
self.log.Trace("Saving chequebook to disk", self.path)
|
||||||
|
|
||||||
return ioutil.WriteFile(self.path, data, os.ModePerm)
|
return ioutil.WriteFile(self.path, data, os.ModePerm)
|
||||||
}
|
}
|
||||||
|
|
@ -248,7 +249,7 @@ func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *
|
||||||
defer self.lock.Unlock()
|
defer self.lock.Unlock()
|
||||||
self.lock.Lock()
|
self.lock.Lock()
|
||||||
|
|
||||||
if amount.Cmp(common.Big0) <= 0 {
|
if amount.Sign() <= 0 {
|
||||||
return nil, fmt.Errorf("amount must be greater than zero (%v)", amount)
|
return nil, fmt.Errorf("amount must be greater than zero (%v)", amount)
|
||||||
}
|
}
|
||||||
if self.balance.Cmp(amount) < 0 {
|
if self.balance.Cmp(amount) < 0 {
|
||||||
|
|
@ -340,12 +341,12 @@ func (self *Chequebook) deposit(amount *big.Int) (string, error) {
|
||||||
chbookRaw := &contract.ChequebookRaw{Contract: self.contract}
|
chbookRaw := &contract.ChequebookRaw{Contract: self.contract}
|
||||||
tx, err := chbookRaw.Transfer(depositTransactor)
|
tx, err := chbookRaw.Transfer(depositTransactor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.V(logger.Warn).Infof("error depositing %d wei to chequebook (%s, balance: %v, target: %v): %v", amount, self.contractAddr.Hex(), self.balance, self.buffer, err)
|
self.log.Warn("Failed to fund chequebook", "amount", amount, "balance", self.balance, "target", self.buffer, "err", err)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
// assume that transaction is actually successful, we add the amount to balance right away
|
// assume that transaction is actually successful, we add the amount to balance right away
|
||||||
self.balance.Add(self.balance, amount)
|
self.balance.Add(self.balance, amount)
|
||||||
glog.V(logger.Detail).Infof("deposited %d wei to chequebook (%s, balance: %v, target: %v)", amount, self.contractAddr.Hex(), self.balance, self.buffer)
|
self.log.Trace("Deposited funds to chequebook", "amount", amount, "balance", self.balance, "target", self.buffer)
|
||||||
return tx.Hash().Hex(), nil
|
return tx.Hash().Hex(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -441,6 +442,7 @@ type Inbox struct {
|
||||||
maxUncashed *big.Int // threshold that triggers autocashing
|
maxUncashed *big.Int // threshold that triggers autocashing
|
||||||
cashed *big.Int // cumulative amount cashed
|
cashed *big.Int // cumulative amount cashed
|
||||||
cheque *Cheque // last cheque, nil if none yet received
|
cheque *Cheque // last cheque, nil if none yet received
|
||||||
|
log log.Logger // contextual logger with the contrac address embedded
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewInbox creates an Inbox. An Inboxes is not persisted, the cumulative sum is updated
|
// NewInbox creates an Inbox. An Inboxes is not persisted, the cumulative sum is updated
|
||||||
|
|
@ -468,8 +470,9 @@ func NewInbox(prvKey *ecdsa.PrivateKey, contractAddr, beneficiary common.Address
|
||||||
signer: signer,
|
signer: signer,
|
||||||
session: session,
|
session: session,
|
||||||
cashed: new(big.Int).Set(common.Big0),
|
cashed: new(big.Int).Set(common.Big0),
|
||||||
|
log: log.New("contract", contractAddr),
|
||||||
}
|
}
|
||||||
glog.V(logger.Detail).Infof("initialised inbox (%s -> %s) expected signer: %x", self.contract.Hex(), self.beneficiary.Hex(), crypto.FromECDSAPub(signer))
|
self.log.Trace("New chequebook inbox initialized", "beneficiary", self.beneficiary, "signer", hexutil.Bytes(crypto.FromECDSAPub(signer)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -491,7 +494,7 @@ func (self *Inbox) Stop() {
|
||||||
func (self *Inbox) Cash() (txhash string, err error) {
|
func (self *Inbox) Cash() (txhash string, err error) {
|
||||||
if self.cheque != nil {
|
if self.cheque != nil {
|
||||||
txhash, err = self.cheque.Cash(self.session)
|
txhash, err = self.cheque.Cash(self.session)
|
||||||
glog.V(logger.Detail).Infof("cashing cheque (total: %v) on chequebook (%s) sending to %v", self.cheque.Amount, self.contract.Hex(), self.beneficiary.Hex())
|
self.log.Trace("Cashing in chequebook cheque", "amount", self.cheque.Amount, "beneficiary", self.beneficiary)
|
||||||
self.cashed = self.cheque.Amount
|
self.cashed = self.cheque.Amount
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
@ -516,7 +519,7 @@ func (self *Inbox) autoCash(cashInterval time.Duration) {
|
||||||
self.quit = nil
|
self.quit = nil
|
||||||
}
|
}
|
||||||
// if maxUncashed is set to 0, then autocash on receipt
|
// if maxUncashed is set to 0, then autocash on receipt
|
||||||
if cashInterval == time.Duration(0) || self.maxUncashed != nil && self.maxUncashed.Cmp(common.Big0) == 0 {
|
if cashInterval == time.Duration(0) || self.maxUncashed != nil && self.maxUncashed.Sign() == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -575,7 +578,7 @@ func (self *Inbox) Receive(promise swap.Promise) (*big.Int, error) {
|
||||||
self.Cash()
|
self.Cash()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
glog.V(logger.Detail).Infof("received cheque of %v wei in inbox (%s, uncashed: %v)", amount, self.contract.Hex(), uncashed)
|
self.log.Trace("Received cheque in chequebook inbox", "amount", amount, "uncashed", uncashed)
|
||||||
}
|
}
|
||||||
|
|
||||||
return amount, err
|
return amount, err
|
||||||
|
|
@ -583,7 +586,7 @@ func (self *Inbox) Receive(promise swap.Promise) (*big.Int, error) {
|
||||||
|
|
||||||
// Verify verifies cheque for signer, contract, beneficiary, amount, valid signature.
|
// Verify verifies cheque for signer, contract, beneficiary, amount, valid signature.
|
||||||
func (self *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary common.Address, sum *big.Int) (*big.Int, error) {
|
func (self *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary common.Address, sum *big.Int) (*big.Int, error) {
|
||||||
glog.V(logger.Detail).Infof("verify cheque: %v - sum: %v", self, sum)
|
log.Trace("Verifying chequebook cheque", "cheque", self, "sum", sum)
|
||||||
if sum == nil {
|
if sum == nil {
|
||||||
return nil, fmt.Errorf("invalid amount")
|
return nil, fmt.Errorf("invalid amount")
|
||||||
}
|
}
|
||||||
|
|
@ -598,7 +601,7 @@ func (self *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary com
|
||||||
amount := new(big.Int).Set(self.Amount)
|
amount := new(big.Int).Set(self.Amount)
|
||||||
if sum != nil {
|
if sum != nil {
|
||||||
amount.Sub(amount, sum)
|
amount.Sub(amount, sum)
|
||||||
if amount.Cmp(common.Big0) <= 0 {
|
if amount.Sign() <= 0 {
|
||||||
return nil, fmt.Errorf("incorrect amount: %v <= 0", amount)
|
return nil, fmt.Errorf("incorrect amount: %v <= 0", amount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,11 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func newTestBackend() *backends.SimulatedBackend {
|
func newTestBackend() *backends.SimulatedBackend {
|
||||||
return backends.NewSimulatedBackend(
|
return backends.NewSimulatedBackend(core.GenesisAlloc{
|
||||||
core.GenesisAccount{Address: addr0, Balance: big.NewInt(1000000000)},
|
addr0: {Balance: big.NewInt(1000000000)},
|
||||||
core.GenesisAccount{Address: addr1, Balance: big.NewInt(1000000000)},
|
addr1: {Balance: big.NewInt(1000000000)},
|
||||||
core.GenesisAccount{Address: addr2, Balance: big.NewInt(1000000000)},
|
addr2: {Balance: big.NewInt(1000000000)},
|
||||||
)
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func deploy(prvKey *ecdsa.PrivateKey, amount *big.Int, backend *backends.SimulatedBackend) (common.Address, error) {
|
func deploy(prvKey *ecdsa.PrivateKey, amount *big.Int, backend *backends.SimulatedBackend) (common.Address, error) {
|
||||||
|
|
@ -88,7 +88,7 @@ func TestIssueAndReceive(t *testing.T) {
|
||||||
t.Fatalf("expected no error, got %v", err)
|
t.Fatalf("expected no error, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if chbook.Balance().Cmp(common.Big0) != 0 {
|
if chbook.Balance().Sign() != 0 {
|
||||||
t.Errorf("expected: %v, got %v", "0", chbook.Balance())
|
t.Errorf("expected: %v, got %v", "0", chbook.Balance())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestENS(t *testing.T) {
|
func TestENS(t *testing.T) {
|
||||||
contractBackend := backends.NewSimulatedBackend(core.GenesisAccount{Address: addr, Balance: big.NewInt(1000000000)})
|
contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}})
|
||||||
transactOpts := bind.NewKeyedTransactor(key)
|
transactOpts := bind.NewKeyedTransactor(key)
|
||||||
// Workaround for bug estimating gas in the call to Register
|
// Workaround for bug estimating gas in the call to Register
|
||||||
transactOpts.GasLimit = big.NewInt(1000000)
|
transactOpts.GasLimit = big.NewInt(1000000)
|
||||||
|
|
|
||||||
|
|
@ -35,11 +35,11 @@ func setupReleaseTest(t *testing.T, prefund ...*ecdsa.PrivateKey) (*ecdsa.Privat
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
auth := bind.NewKeyedTransactor(key)
|
auth := bind.NewKeyedTransactor(key)
|
||||||
|
|
||||||
accounts := []core.GenesisAccount{{Address: auth.From, Balance: big.NewInt(10000000000)}}
|
alloc := core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}
|
||||||
for _, key := range prefund {
|
for _, key := range prefund {
|
||||||
accounts = append(accounts, core.GenesisAccount{Address: crypto.PubkeyToAddress(key.PublicKey), Balance: big.NewInt(10000000000)})
|
alloc[crypto.PubkeyToAddress(key.PublicKey)] = core.GenesisAccount{Balance: big.NewInt(10000000000)}
|
||||||
}
|
}
|
||||||
sim := backends.NewSimulatedBackend(accounts...)
|
sim := backends.NewSimulatedBackend(alloc)
|
||||||
|
|
||||||
// Deploy a version oracle contract, commit and return
|
// Deploy a version oracle contract, commit and return
|
||||||
_, _, oracle, err := DeployReleaseOracle(auth, sim, []common.Address{auth.From})
|
_, _, oracle, err := DeployReleaseOracle(auth, sim, []common.Address{auth.From})
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ package release
|
||||||
//go:generate abigen --sol ./contract.sol --pkg release --out ./contract.go
|
//go:generate abigen --sol ./contract.sol --pkg release --out ./contract.go
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -29,12 +30,10 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
"github.com/ethereum/go-ethereum/les"
|
"github.com/ethereum/go-ethereum/les"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Interval to check for new releases
|
// Interval to check for new releases
|
||||||
|
|
@ -117,22 +116,31 @@ func (r *ReleaseService) checker() {
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
// If the time arrived, check for a new release
|
|
||||||
case <-timer.C:
|
case <-timer.C:
|
||||||
// Rechedule the timer before continuing
|
// Rechedule the timer before continuing
|
||||||
timer.Reset(releaseRecheckInterval)
|
timer.Reset(releaseRecheckInterval)
|
||||||
|
r.checkVersion()
|
||||||
|
case errc := <-r.quit:
|
||||||
|
errc <- nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ReleaseService) checkVersion() {
|
||||||
// Retrieve the current version, and handle missing contracts gracefully
|
// Retrieve the current version, and handle missing contracts gracefully
|
||||||
ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
opts := &bind.CallOpts{Context: ctx}
|
opts := &bind.CallOpts{Context: ctx}
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
version, err := r.oracle.CurrentVersion(opts)
|
version, err := r.oracle.CurrentVersion(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == bind.ErrNoCode {
|
if err == bind.ErrNoCode {
|
||||||
glog.V(logger.Debug).Infof("Release oracle not found at %x", r.config.Oracle)
|
log.Debug("Release oracle not found", "contract", r.config.Oracle)
|
||||||
continue
|
} else {
|
||||||
|
log.Error("Failed to retrieve current release", "err", err)
|
||||||
}
|
}
|
||||||
glog.V(logger.Error).Infof("Failed to retrieve current release: %v", err)
|
return
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
// Version was successfully retrieved, notify if newer than ours
|
// Version was successfully retrieved, notify if newer than ours
|
||||||
if version.Major > r.config.Major ||
|
if version.Major > r.config.Major ||
|
||||||
|
|
@ -144,19 +152,13 @@ func (r *ReleaseService) checker() {
|
||||||
howtofix := fmt.Sprintf("Please check https://github.com/ethereum/go-ethereum/releases for new releases")
|
howtofix := fmt.Sprintf("Please check https://github.com/ethereum/go-ethereum/releases for new releases")
|
||||||
separator := strings.Repeat("-", len(warning))
|
separator := strings.Repeat("-", len(warning))
|
||||||
|
|
||||||
glog.V(logger.Warn).Info(separator)
|
log.Warn(separator)
|
||||||
glog.V(logger.Warn).Info(warning)
|
log.Warn(warning)
|
||||||
glog.V(logger.Warn).Info(howtofix)
|
log.Warn(howtofix)
|
||||||
glog.V(logger.Warn).Info(separator)
|
log.Warn(separator)
|
||||||
} else {
|
} else {
|
||||||
glog.V(logger.Debug).Infof("Client v%d.%d.%d-%x seems up to date with upstream v%d.%d.%d-%x",
|
log.Debug("Client seems up to date with upstream",
|
||||||
r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4], version.Major, version.Minor, version.Patch, version.Commit[:4])
|
"local", fmt.Sprintf("v%d.%d.%d-%x", r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4]),
|
||||||
}
|
"upstream", fmt.Sprintf("v%d.%d.%d-%x", version.Major, version.Minor, version.Patch, version.Commit[:4]))
|
||||||
|
|
||||||
// If termination was requested, return
|
|
||||||
case errc := <-r.quit:
|
|
||||||
errc <- nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
64
core/asm.go
64
core/asm.go
|
|
@ -1,64 +0,0 @@
|
||||||
// Copyright 2014 The go-ethereum Authors
|
|
||||||
// This file is part of the go-ethereum library.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package core
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Disassemble(script []byte) (asm []string) {
|
|
||||||
pc := new(big.Int)
|
|
||||||
for {
|
|
||||||
if pc.Cmp(big.NewInt(int64(len(script)))) >= 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the memory location of pc
|
|
||||||
val := script[pc.Int64()]
|
|
||||||
// Get the opcode (it must be an opcode!)
|
|
||||||
op := vm.OpCode(val)
|
|
||||||
|
|
||||||
asm = append(asm, fmt.Sprintf("%04v: %v", pc, op))
|
|
||||||
|
|
||||||
switch op {
|
|
||||||
case vm.PUSH1, vm.PUSH2, vm.PUSH3, vm.PUSH4, vm.PUSH5, vm.PUSH6, vm.PUSH7, vm.PUSH8,
|
|
||||||
vm.PUSH9, vm.PUSH10, vm.PUSH11, vm.PUSH12, vm.PUSH13, vm.PUSH14, vm.PUSH15,
|
|
||||||
vm.PUSH16, vm.PUSH17, vm.PUSH18, vm.PUSH19, vm.PUSH20, vm.PUSH21, vm.PUSH22,
|
|
||||||
vm.PUSH23, vm.PUSH24, vm.PUSH25, vm.PUSH26, vm.PUSH27, vm.PUSH28, vm.PUSH29,
|
|
||||||
vm.PUSH30, vm.PUSH31, vm.PUSH32:
|
|
||||||
pc.Add(pc, common.Big1)
|
|
||||||
a := int64(op) - int64(vm.PUSH1) + 1
|
|
||||||
if int(pc.Int64()+a) > len(script) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data := script[pc.Int64() : pc.Int64()+a]
|
|
||||||
if len(data) == 0 {
|
|
||||||
data = []byte{0}
|
|
||||||
}
|
|
||||||
asm = append(asm, fmt.Sprintf("%04v: 0x%x", pc, data))
|
|
||||||
|
|
||||||
pc.Add(pc, big.NewInt(a-1))
|
|
||||||
}
|
|
||||||
|
|
||||||
pc.Add(pc, common.Big1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
139
core/asm/asm.go
Normal file
139
core/asm/asm.go
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
// Copyright 2017 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/>.
|
||||||
|
|
||||||
|
// Provides support for dealing with EVM assembly instructions (e.g., disassembling them).
|
||||||
|
package asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Iterator for disassembled EVM instructions
|
||||||
|
type instructionIterator struct {
|
||||||
|
code []byte
|
||||||
|
pc uint64
|
||||||
|
arg []byte
|
||||||
|
op vm.OpCode
|
||||||
|
error error
|
||||||
|
started bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new instruction iterator.
|
||||||
|
func NewInstructionIterator(code []byte) *instructionIterator {
|
||||||
|
it := new(instructionIterator)
|
||||||
|
it.code = code
|
||||||
|
return it
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if there is a next instruction and moves on.
|
||||||
|
func (it *instructionIterator) Next() bool {
|
||||||
|
if it.error != nil || uint64(len(it.code)) <= it.pc {
|
||||||
|
// We previously reached an error or the end.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if it.started {
|
||||||
|
// Since the iteration has been already started we move to the next instruction.
|
||||||
|
if it.arg != nil {
|
||||||
|
it.pc += uint64(len(it.arg))
|
||||||
|
}
|
||||||
|
it.pc++
|
||||||
|
} else {
|
||||||
|
// We start the iteration from the first instruction.
|
||||||
|
it.started = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if uint64(len(it.code)) <= it.pc {
|
||||||
|
// We reached the end.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
it.op = vm.OpCode(it.code[it.pc])
|
||||||
|
if it.op.IsPush() {
|
||||||
|
a := uint64(it.op) - uint64(vm.PUSH1) + 1
|
||||||
|
u := it.pc + 1 + a
|
||||||
|
if uint64(len(it.code)) <= it.pc || uint64(len(it.code)) < u {
|
||||||
|
it.error = fmt.Errorf("incomplete push instruction at %v", it.pc)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
it.arg = it.code[it.pc+1 : u]
|
||||||
|
} else {
|
||||||
|
it.arg = nil
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns any error that may have been encountered.
|
||||||
|
func (it *instructionIterator) Error() error {
|
||||||
|
return it.error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the PC of the current instruction.
|
||||||
|
func (it *instructionIterator) PC() uint64 {
|
||||||
|
return it.pc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the opcode of the current instruction.
|
||||||
|
func (it *instructionIterator) Op() vm.OpCode {
|
||||||
|
return it.op
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the argument of the current instruction.
|
||||||
|
func (it *instructionIterator) Arg() []byte {
|
||||||
|
return it.arg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pretty-print all disassembled EVM instructions to stdout.
|
||||||
|
func PrintDisassembled(code string) error {
|
||||||
|
script, err := hex.DecodeString(code)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
it := NewInstructionIterator(script)
|
||||||
|
for it.Next() {
|
||||||
|
if it.Arg() != nil && 0 < len(it.Arg()) {
|
||||||
|
fmt.Printf("%06v: %v 0x%x\n", it.PC(), it.Op(), it.Arg())
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%06v: %v\n", it.PC(), it.Op())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := it.Error(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return all disassembled EVM instructions in human-readable format.
|
||||||
|
func Disassemble(script []byte) ([]string, error) {
|
||||||
|
instrs := make([]string, 0)
|
||||||
|
|
||||||
|
it := NewInstructionIterator(script)
|
||||||
|
for it.Next() {
|
||||||
|
if it.Arg() != nil && 0 < len(it.Arg()) {
|
||||||
|
instrs = append(instrs, fmt.Sprintf("%06v: %v 0x%x\n", it.PC(), it.Op(), it.Arg()))
|
||||||
|
} else {
|
||||||
|
instrs = append(instrs, fmt.Sprintf("%06v: %v\n", it.PC(), it.Op()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := it.Error(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return instrs, nil
|
||||||
|
}
|
||||||
74
core/asm/asm_test.go
Normal file
74
core/asm/asm_test.go
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright 2017 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 asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"encoding/hex"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tests disassembling the instructions for valid evm code
|
||||||
|
func TestInstructionIteratorValid(t *testing.T) {
|
||||||
|
cnt := 0
|
||||||
|
script, _ := hex.DecodeString("61000000")
|
||||||
|
|
||||||
|
it := NewInstructionIterator(script)
|
||||||
|
for it.Next() {
|
||||||
|
cnt++
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := it.Error(); err != nil {
|
||||||
|
t.Errorf("Expected 2, but encountered error %v instead.", err)
|
||||||
|
}
|
||||||
|
if cnt != 2 {
|
||||||
|
t.Errorf("Expected 2, but got %v instead.", cnt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests disassembling the instructions for invalid evm code
|
||||||
|
func TestInstructionIteratorInvalid(t *testing.T) {
|
||||||
|
cnt := 0
|
||||||
|
script, _ := hex.DecodeString("6100")
|
||||||
|
|
||||||
|
it := NewInstructionIterator(script)
|
||||||
|
for it.Next() {
|
||||||
|
cnt++
|
||||||
|
}
|
||||||
|
|
||||||
|
if it.Error() == nil {
|
||||||
|
t.Errorf("Expected an error, but got %v instead.", cnt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests disassembling the instructions for empty evm code
|
||||||
|
func TestInstructionIteratorEmpty(t *testing.T) {
|
||||||
|
cnt := 0
|
||||||
|
script, _ := hex.DecodeString("")
|
||||||
|
|
||||||
|
it := NewInstructionIterator(script)
|
||||||
|
for it.Next() {
|
||||||
|
cnt++
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := it.Error(); err != nil {
|
||||||
|
t.Errorf("Expected 0, but encountered error %v instead.", err)
|
||||||
|
}
|
||||||
|
if cnt != 0 {
|
||||||
|
t.Errorf("Expected 0, but got %v instead.", cnt)
|
||||||
|
}
|
||||||
|
}
|
||||||
281
core/asm/compiler.go
Normal file
281
core/asm/compiler.go
Normal file
|
|
@ -0,0 +1,281 @@
|
||||||
|
// Copyright 2017 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 asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Compiler contains information about the parsed source
|
||||||
|
// and holds the tokens for the program.
|
||||||
|
type Compiler struct {
|
||||||
|
tokens []token
|
||||||
|
binary []interface{}
|
||||||
|
|
||||||
|
labels map[string]int
|
||||||
|
|
||||||
|
pc, pos int
|
||||||
|
|
||||||
|
debug bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// newCompiler returns a new allocated compiler.
|
||||||
|
func NewCompiler(debug bool) *Compiler {
|
||||||
|
return &Compiler{
|
||||||
|
labels: make(map[string]int),
|
||||||
|
debug: debug,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feed feeds tokens in to ch and are interpreted by
|
||||||
|
// the compiler.
|
||||||
|
//
|
||||||
|
// feed is the first pass in the compile stage as it
|
||||||
|
// collect the used labels in the program and keeps a
|
||||||
|
// program counter which is used to determine the locations
|
||||||
|
// of the jump dests. The labels can than be used in the
|
||||||
|
// second stage to push labels and determine the right
|
||||||
|
// position.
|
||||||
|
func (c *Compiler) Feed(ch <-chan token) {
|
||||||
|
for i := range ch {
|
||||||
|
switch i.typ {
|
||||||
|
case number:
|
||||||
|
num := math.MustParseBig256(i.text).Bytes()
|
||||||
|
if len(num) == 0 {
|
||||||
|
num = []byte{0}
|
||||||
|
}
|
||||||
|
c.pc += len(num)
|
||||||
|
case stringValue:
|
||||||
|
c.pc += len(i.text) - 2
|
||||||
|
case element:
|
||||||
|
c.pc++
|
||||||
|
case labelDef:
|
||||||
|
c.labels[i.text] = c.pc
|
||||||
|
c.pc++
|
||||||
|
case label:
|
||||||
|
c.pc += 5
|
||||||
|
}
|
||||||
|
|
||||||
|
c.tokens = append(c.tokens, i)
|
||||||
|
}
|
||||||
|
if c.debug {
|
||||||
|
fmt.Fprintln(os.Stderr, "found", len(c.labels), "labels")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile compiles the current tokens and returns a
|
||||||
|
// binary string that can be interpreted by the EVM
|
||||||
|
// and an error if it failed.
|
||||||
|
//
|
||||||
|
// compile is the second stage in the compile phase
|
||||||
|
// which compiles the tokens to EVM instructions.
|
||||||
|
func (c *Compiler) Compile() (string, []error) {
|
||||||
|
var errors []error
|
||||||
|
// continue looping over the tokens until
|
||||||
|
// the stack has been exhausted.
|
||||||
|
for c.pos < len(c.tokens) {
|
||||||
|
if err := c.compileLine(); err != nil {
|
||||||
|
errors = append(errors, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// turn the binary to hex
|
||||||
|
var bin string
|
||||||
|
for _, v := range c.binary {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case vm.OpCode:
|
||||||
|
bin += fmt.Sprintf("%x", []byte{byte(v)})
|
||||||
|
case []byte:
|
||||||
|
bin += fmt.Sprintf("%x", v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bin, errors
|
||||||
|
}
|
||||||
|
|
||||||
|
// next returns the next token and increments the
|
||||||
|
// posititon.
|
||||||
|
func (c *Compiler) next() token {
|
||||||
|
token := c.tokens[c.pos]
|
||||||
|
c.pos++
|
||||||
|
return token
|
||||||
|
}
|
||||||
|
|
||||||
|
// compile line compiles a single line instruction e.g.
|
||||||
|
// "push 1", "jump @labal".
|
||||||
|
func (c *Compiler) compileLine() error {
|
||||||
|
n := c.next()
|
||||||
|
if n.typ != lineStart {
|
||||||
|
return compileErr(n, n.typ.String(), lineStart.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
lvalue := c.next()
|
||||||
|
switch lvalue.typ {
|
||||||
|
case eof:
|
||||||
|
return nil
|
||||||
|
case element:
|
||||||
|
if err := c.compileElement(lvalue); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case labelDef:
|
||||||
|
c.compileLabel()
|
||||||
|
case lineEnd:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return compileErr(lvalue, lvalue.text, fmt.Sprintf("%v or %v", labelDef, element))
|
||||||
|
}
|
||||||
|
|
||||||
|
if n := c.next(); n.typ != lineEnd {
|
||||||
|
return compileErr(n, n.text, lineEnd.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// compileNumber compiles the number to bytes
|
||||||
|
func (c *Compiler) compileNumber(element token) (int, error) {
|
||||||
|
num := math.MustParseBig256(element.text).Bytes()
|
||||||
|
if len(num) == 0 {
|
||||||
|
num = []byte{0}
|
||||||
|
}
|
||||||
|
c.pushBin(num)
|
||||||
|
return len(num), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// compileElement compiles the element (push & label or both)
|
||||||
|
// to a binary representation and may error if incorrect statements
|
||||||
|
// where fed.
|
||||||
|
func (c *Compiler) compileElement(element token) error {
|
||||||
|
// check for a jump. jumps must be read and compiled
|
||||||
|
// from right to left.
|
||||||
|
if isJump(element.text) {
|
||||||
|
rvalue := c.next()
|
||||||
|
switch rvalue.typ {
|
||||||
|
case number:
|
||||||
|
// TODO figure out how to return the error properly
|
||||||
|
c.compileNumber(rvalue)
|
||||||
|
case stringValue:
|
||||||
|
// strings are quoted, remove them.
|
||||||
|
c.pushBin(rvalue.text[1 : len(rvalue.text)-2])
|
||||||
|
case label:
|
||||||
|
c.pushBin(vm.PUSH4)
|
||||||
|
pos := big.NewInt(int64(c.labels[rvalue.text])).Bytes()
|
||||||
|
pos = append(make([]byte, 4-len(pos)), pos...)
|
||||||
|
c.pushBin(pos)
|
||||||
|
default:
|
||||||
|
return compileErr(rvalue, rvalue.text, "number, string or label")
|
||||||
|
}
|
||||||
|
// push the operation
|
||||||
|
c.pushBin(toBinary(element.text))
|
||||||
|
return nil
|
||||||
|
} else if isPush(element.text) {
|
||||||
|
// handle pushes. pushes are read from left to right.
|
||||||
|
var value []byte
|
||||||
|
|
||||||
|
rvalue := c.next()
|
||||||
|
switch rvalue.typ {
|
||||||
|
case number:
|
||||||
|
value = math.MustParseBig256(rvalue.text).Bytes()
|
||||||
|
if len(value) == 0 {
|
||||||
|
value = []byte{0}
|
||||||
|
}
|
||||||
|
case stringValue:
|
||||||
|
value = []byte(rvalue.text[1 : len(rvalue.text)-1])
|
||||||
|
case label:
|
||||||
|
value = make([]byte, 4)
|
||||||
|
copy(value, big.NewInt(int64(c.labels[rvalue.text])).Bytes())
|
||||||
|
default:
|
||||||
|
return compileErr(rvalue, rvalue.text, "number, string or label")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(value) > 32 {
|
||||||
|
return fmt.Errorf("%d type error: unsupported string or number with size > 32", rvalue.lineno)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.pushBin(vm.OpCode(int(vm.PUSH1) - 1 + len(value)))
|
||||||
|
c.pushBin(value)
|
||||||
|
} else {
|
||||||
|
c.pushBin(toBinary(element.text))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// compileLabel pushes a jumpdest to the binary slice.
|
||||||
|
func (c *Compiler) compileLabel() {
|
||||||
|
c.pushBin(vm.JUMPDEST)
|
||||||
|
}
|
||||||
|
|
||||||
|
// pushBin pushes the value v to the binary stack.
|
||||||
|
func (c *Compiler) pushBin(v interface{}) {
|
||||||
|
if c.debug {
|
||||||
|
fmt.Printf("%d: %v\n", len(c.binary), v)
|
||||||
|
}
|
||||||
|
c.binary = append(c.binary, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isPush returns whether the string op is either any of
|
||||||
|
// push(N).
|
||||||
|
func isPush(op string) bool {
|
||||||
|
if op == "push" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isJump returns whether the string op is jump(i)
|
||||||
|
func isJump(op string) bool {
|
||||||
|
return op == "jumpi" || op == "jump"
|
||||||
|
}
|
||||||
|
|
||||||
|
// toBinary converts text to a vm.OpCode
|
||||||
|
func toBinary(text string) vm.OpCode {
|
||||||
|
if isPush(text) {
|
||||||
|
text = "push1"
|
||||||
|
}
|
||||||
|
return vm.StringToOp(strings.ToUpper(text))
|
||||||
|
}
|
||||||
|
|
||||||
|
type compileError struct {
|
||||||
|
got string
|
||||||
|
want string
|
||||||
|
|
||||||
|
lineno int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err compileError) Error() string {
|
||||||
|
return fmt.Sprintf("%d syntax error: unexpected %v, expected %v", err.lineno, err.got, err.want)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
errExpBol = errors.New("expected beginning of line")
|
||||||
|
errExpElementOrLabel = errors.New("expected beginning of line")
|
||||||
|
)
|
||||||
|
|
||||||
|
func compileErr(c token, got, want string) error {
|
||||||
|
return compileError{
|
||||||
|
got: got,
|
||||||
|
want: want,
|
||||||
|
lineno: c.lineno,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2016 The go-ethereum Authors
|
// Copyright 2017 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
@ -14,13 +14,23 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// 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/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// Contains the wrappers from the math/big package that require Go 1.7 and above.
|
package asm
|
||||||
|
|
||||||
// +build go1.7
|
import "testing"
|
||||||
|
|
||||||
package geth
|
func lexAll(src string) []token {
|
||||||
|
ch := Lex("test.asm", []byte(src), false)
|
||||||
|
|
||||||
// GetString returns the value of x as a formatted string in some number base.
|
var tokens []token
|
||||||
func (bi *BigInt) GetString(base int) string {
|
for i := range ch {
|
||||||
return bi.bigint.Text(base)
|
tokens = append(tokens, i)
|
||||||
|
}
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComment(t *testing.T) {
|
||||||
|
tokens := lexAll(";; this is a comment")
|
||||||
|
if len(tokens) != 2 { // {new line, EOF}
|
||||||
|
t.Error("expected no tokens")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
291
core/asm/lexer.go
Normal file
291
core/asm/lexer.go
Normal file
|
|
@ -0,0 +1,291 @@
|
||||||
|
// Copyright 2017 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 asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
// stateFn is used through the lifetime of the
|
||||||
|
// lexer to parse the different values at the
|
||||||
|
// current state.
|
||||||
|
type stateFn func(*lexer) stateFn
|
||||||
|
|
||||||
|
// token is emitted when the lexer has discovered
|
||||||
|
// a new parsable token. These are delivered over
|
||||||
|
// the tokens channels of the lexer
|
||||||
|
type token struct {
|
||||||
|
typ tokenType
|
||||||
|
lineno int
|
||||||
|
text string
|
||||||
|
}
|
||||||
|
|
||||||
|
// tokenType are the different types the lexer
|
||||||
|
// is able to parse and return.
|
||||||
|
type tokenType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
eof tokenType = iota // end of file
|
||||||
|
lineStart // emitted when a line starts
|
||||||
|
lineEnd // emitted when a line ends
|
||||||
|
invalidStatement // any invalid statement
|
||||||
|
element // any element during element parsing
|
||||||
|
label // label is emitted when a labal is found
|
||||||
|
labelDef // label definition is emitted when a new label is found
|
||||||
|
number // number is emitted when a number is found
|
||||||
|
stringValue // stringValue is emitted when a string has been found
|
||||||
|
|
||||||
|
Numbers = "1234567890" // characters representing any decimal number
|
||||||
|
HexadecimalNumbers = Numbers + "aAbBcCdDeEfF" // characters representing any hexadecimal
|
||||||
|
Alpha = "abcdefghijklmnopqrstuwvxyzABCDEFGHIJKLMNOPQRSTUWVXYZ" // characters representing alphanumeric
|
||||||
|
)
|
||||||
|
|
||||||
|
// String implements stringer
|
||||||
|
func (it tokenType) String() string {
|
||||||
|
if int(it) > len(stringtokenTypes) {
|
||||||
|
return "invalid"
|
||||||
|
}
|
||||||
|
return stringtokenTypes[it]
|
||||||
|
}
|
||||||
|
|
||||||
|
var stringtokenTypes = []string{
|
||||||
|
eof: "EOF",
|
||||||
|
invalidStatement: "invalid statement",
|
||||||
|
element: "element",
|
||||||
|
lineEnd: "end of line",
|
||||||
|
lineStart: "new line",
|
||||||
|
label: "label",
|
||||||
|
labelDef: "label definition",
|
||||||
|
number: "number",
|
||||||
|
stringValue: "string",
|
||||||
|
}
|
||||||
|
|
||||||
|
// lexer is the basic construct for parsing
|
||||||
|
// source code and turning them in to tokens.
|
||||||
|
// Tokens are interpreted by the compiler.
|
||||||
|
type lexer struct {
|
||||||
|
input string // input contains the source code of the program
|
||||||
|
|
||||||
|
tokens chan token // tokens is used to deliver tokens to the listener
|
||||||
|
state stateFn // the current state function
|
||||||
|
|
||||||
|
lineno int // current line number in the source file
|
||||||
|
start, pos, width int // positions for lexing and returning value
|
||||||
|
|
||||||
|
debug bool // flag for triggering debug output
|
||||||
|
}
|
||||||
|
|
||||||
|
// lex lexes the program by name with the given source. It returns a
|
||||||
|
// channel on which the tokens are delivered.
|
||||||
|
func Lex(name string, source []byte, debug bool) <-chan token {
|
||||||
|
ch := make(chan token)
|
||||||
|
l := &lexer{
|
||||||
|
input: string(source),
|
||||||
|
tokens: ch,
|
||||||
|
state: lexLine,
|
||||||
|
debug: debug,
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
l.emit(lineStart)
|
||||||
|
for l.state != nil {
|
||||||
|
l.state = l.state(l)
|
||||||
|
}
|
||||||
|
l.emit(eof)
|
||||||
|
close(l.tokens)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return ch
|
||||||
|
}
|
||||||
|
|
||||||
|
// next returns the next rune in the program's source.
|
||||||
|
func (l *lexer) next() (rune rune) {
|
||||||
|
if l.pos >= len(l.input) {
|
||||||
|
l.width = 0
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
rune, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
|
||||||
|
l.pos += l.width
|
||||||
|
return rune
|
||||||
|
}
|
||||||
|
|
||||||
|
// backup backsup the last parsed element (multi-character)
|
||||||
|
func (l *lexer) backup() {
|
||||||
|
l.pos -= l.width
|
||||||
|
}
|
||||||
|
|
||||||
|
// peek returns the next rune but does not advance the seeker
|
||||||
|
func (l *lexer) peek() rune {
|
||||||
|
r := l.next()
|
||||||
|
l.backup()
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore advances the seeker and ignores the value
|
||||||
|
func (l *lexer) ignore() {
|
||||||
|
l.start = l.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accepts checks whether the given input matches the next rune
|
||||||
|
func (l *lexer) accept(valid string) bool {
|
||||||
|
if strings.IndexRune(valid, l.next()) >= 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
l.backup()
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// acceptRun will continue to advance the seeker until valid
|
||||||
|
// can no longer be met.
|
||||||
|
func (l *lexer) acceptRun(valid string) {
|
||||||
|
for strings.IndexRune(valid, l.next()) >= 0 {
|
||||||
|
}
|
||||||
|
l.backup()
|
||||||
|
}
|
||||||
|
|
||||||
|
// acceptRunUntil is the inverse of acceptRun and will continue
|
||||||
|
// to advance the seeker until the rune has been found.
|
||||||
|
func (l *lexer) acceptRunUntil(until rune) bool {
|
||||||
|
// Continues running until a rune is found
|
||||||
|
for i := l.next(); strings.IndexRune(string(until), i) == -1; i = l.next() {
|
||||||
|
if i == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// blob returns the current value
|
||||||
|
func (l *lexer) blob() string {
|
||||||
|
return l.input[l.start:l.pos]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emits a new token on to token channel for processing
|
||||||
|
func (l *lexer) emit(t tokenType) {
|
||||||
|
token := token{t, l.lineno, l.blob()}
|
||||||
|
|
||||||
|
if l.debug {
|
||||||
|
fmt.Fprintf(os.Stderr, "%04d: (%-20v) %s\n", token.lineno, token.typ, token.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.tokens <- token
|
||||||
|
l.start = l.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
// lexLine is state function for lexing lines
|
||||||
|
func lexLine(l *lexer) stateFn {
|
||||||
|
for {
|
||||||
|
switch r := l.next(); {
|
||||||
|
case r == '\n':
|
||||||
|
l.emit(lineEnd)
|
||||||
|
l.ignore()
|
||||||
|
l.lineno++
|
||||||
|
|
||||||
|
l.emit(lineStart)
|
||||||
|
case r == ';' && l.peek() == ';':
|
||||||
|
return lexComment
|
||||||
|
case isSpace(r):
|
||||||
|
l.ignore()
|
||||||
|
case isAlphaNumeric(r) || r == '_':
|
||||||
|
return lexElement
|
||||||
|
case isNumber(r):
|
||||||
|
return lexNumber
|
||||||
|
case r == '@':
|
||||||
|
l.ignore()
|
||||||
|
return lexLabel
|
||||||
|
case r == '"':
|
||||||
|
return lexInsideString
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// lexComment parses the current position until the end
|
||||||
|
// of the line and discards the text.
|
||||||
|
func lexComment(l *lexer) stateFn {
|
||||||
|
l.acceptRunUntil('\n')
|
||||||
|
l.ignore()
|
||||||
|
|
||||||
|
return lexLine
|
||||||
|
}
|
||||||
|
|
||||||
|
// lexLabel parses the current label, emits and returns
|
||||||
|
// the lex text state function to advance the parsing
|
||||||
|
// process.
|
||||||
|
func lexLabel(l *lexer) stateFn {
|
||||||
|
l.acceptRun(Alpha + "_")
|
||||||
|
|
||||||
|
l.emit(label)
|
||||||
|
|
||||||
|
return lexLine
|
||||||
|
}
|
||||||
|
|
||||||
|
// lexInsideString lexes the inside of a string until
|
||||||
|
// until the state function finds the closing quote.
|
||||||
|
// It returns the lex text state function.
|
||||||
|
func lexInsideString(l *lexer) stateFn {
|
||||||
|
if l.acceptRunUntil('"') {
|
||||||
|
l.emit(stringValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lexLine
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexNumber(l *lexer) stateFn {
|
||||||
|
acceptance := Numbers
|
||||||
|
if l.accept("0") && l.accept("xX") {
|
||||||
|
acceptance = HexadecimalNumbers
|
||||||
|
}
|
||||||
|
l.acceptRun(acceptance)
|
||||||
|
|
||||||
|
l.emit(number)
|
||||||
|
|
||||||
|
return lexLine
|
||||||
|
}
|
||||||
|
|
||||||
|
func lexElement(l *lexer) stateFn {
|
||||||
|
l.acceptRun(Alpha + "_" + Numbers)
|
||||||
|
|
||||||
|
if l.peek() == ':' {
|
||||||
|
l.emit(labelDef)
|
||||||
|
|
||||||
|
l.accept(":")
|
||||||
|
l.ignore()
|
||||||
|
} else {
|
||||||
|
l.emit(element)
|
||||||
|
}
|
||||||
|
return lexLine
|
||||||
|
}
|
||||||
|
|
||||||
|
func isAlphaNumeric(t rune) bool {
|
||||||
|
return unicode.IsLetter(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isSpace(t rune) bool {
|
||||||
|
return unicode.IsSpace(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNumber(t rune) bool {
|
||||||
|
return unicode.IsNumber(t)
|
||||||
|
}
|
||||||
|
|
@ -24,12 +24,14 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkInsertChain_empty_memdb(b *testing.B) {
|
func BenchmarkInsertChain_empty_memdb(b *testing.B) {
|
||||||
|
|
@ -73,7 +75,7 @@ var (
|
||||||
// This is the content of the genesis block used by the benchmarks.
|
// This is the content of the genesis block used by the benchmarks.
|
||||||
benchRootKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
benchRootKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
benchRootAddr = crypto.PubkeyToAddress(benchRootKey.PublicKey)
|
benchRootAddr = crypto.PubkeyToAddress(benchRootKey.PublicKey)
|
||||||
benchRootFunds = common.BigPow(2, 100)
|
benchRootFunds = math.BigPow(2, 100)
|
||||||
)
|
)
|
||||||
|
|
||||||
// genValueTx returns a block generator that includes a single
|
// genValueTx returns a block generator that includes a single
|
||||||
|
|
@ -92,6 +94,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
|
||||||
var (
|
var (
|
||||||
ringKeys = make([]*ecdsa.PrivateKey, 1000)
|
ringKeys = make([]*ecdsa.PrivateKey, 1000)
|
||||||
ringAddrs = make([]common.Address, len(ringKeys))
|
ringAddrs = make([]common.Address, len(ringKeys))
|
||||||
|
bigTxGas = new(big.Int).SetUint64(params.TxGas)
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
@ -111,8 +114,8 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
|
||||||
return func(i int, gen *BlockGen) {
|
return func(i int, gen *BlockGen) {
|
||||||
gas := CalcGasLimit(gen.PrevBlock(i - 1))
|
gas := CalcGasLimit(gen.PrevBlock(i - 1))
|
||||||
for {
|
for {
|
||||||
gas.Sub(gas, params.TxGas)
|
gas.Sub(gas, bigTxGas)
|
||||||
if gas.Cmp(params.TxGas) < 0 {
|
if gas.Cmp(bigTxGas) < 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
to := (from + 1) % naccounts
|
to := (from + 1) % naccounts
|
||||||
|
|
@ -120,7 +123,7 @@ func genTxRing(naccounts int) func(int, *BlockGen) {
|
||||||
gen.TxNonce(ringAddrs[from]),
|
gen.TxNonce(ringAddrs[from]),
|
||||||
ringAddrs[to],
|
ringAddrs[to],
|
||||||
benchRootFunds,
|
benchRootFunds,
|
||||||
params.TxGas,
|
bigTxGas,
|
||||||
nil,
|
nil,
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
|
@ -163,13 +166,17 @@ 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})
|
gspec := Genesis{
|
||||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, b.N, gen)
|
Config: params.TestChainConfig,
|
||||||
|
Alloc: GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}},
|
||||||
|
}
|
||||||
|
genesis := gspec.MustCommit(db)
|
||||||
|
chain, _ := GenerateChain(gspec.Config, 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.
|
||||||
evmux := new(event.TypeMux)
|
evmux := new(event.TypeMux)
|
||||||
chainman, _ := NewBlockChain(db, ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, FakePow{}, evmux, vm.Config{})
|
chainman, _ := NewBlockChain(db, gspec.Config, pow.FakePow{}, evmux, vm.Config{})
|
||||||
defer chainman.Stop()
|
defer chainman.Stop()
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
@ -279,7 +286,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error opening database at %v: %v", dir, err)
|
b.Fatalf("error opening database at %v: %v", dir, err)
|
||||||
}
|
}
|
||||||
chain, err := NewBlockChain(db, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{})
|
chain, err := NewBlockChain(db, params.TestChainConfig, pow.FakePow{}, new(event.TypeMux), vm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.Fatalf("error creating chain: %v", err)
|
b.Fatalf("error creating chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/pow"
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
"gopkg.in/fatih/set.v0"
|
"gopkg.in/fatih/set.v0"
|
||||||
|
|
@ -169,7 +170,7 @@ func (v *BlockValidator) VerifyUncles(block, parent *types.Block) error {
|
||||||
for h := range ancestors {
|
for h := range ancestors {
|
||||||
branch += fmt.Sprintf(" O - %x\n |\n", h)
|
branch += fmt.Sprintf(" O - %x\n |\n", h)
|
||||||
}
|
}
|
||||||
glog.Infoln(branch)
|
log.Warn(branch)
|
||||||
return UncleError("uncle[%d](%x) is ancestor", i, hash[:4])
|
return UncleError("uncle[%d](%x) is ancestor", i, hash[:4])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,12 +205,12 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b
|
||||||
//
|
//
|
||||||
// See YP section 4.3.4. "Block Header Validity"
|
// See YP section 4.3.4. "Block Header Validity"
|
||||||
func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {
|
func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {
|
||||||
if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
|
if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
|
||||||
return fmt.Errorf("Header extra data too long (%d)", len(header.Extra))
|
return fmt.Errorf("Header extra data too long (%d)", len(header.Extra))
|
||||||
}
|
}
|
||||||
|
|
||||||
if uncle {
|
if uncle {
|
||||||
if header.Time.Cmp(common.MaxBig) == 1 {
|
if header.Time.Cmp(math.MaxBig256) == 1 {
|
||||||
return BlockTSTooBigErr
|
return BlockTSTooBigErr
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -243,7 +244,7 @@ func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Heade
|
||||||
|
|
||||||
if checkPow {
|
if checkPow {
|
||||||
// Verify the nonce of the header. Return an error if it's not valid
|
// Verify the nonce of the header. Return an error if it's not valid
|
||||||
if !pow.Verify(types.NewBlockWithHeader(header)) {
|
if err := pow.Verify(types.NewBlockWithHeader(header)); err != nil {
|
||||||
return &BlockNonceErr{header.Number, header.Hash(), header.Nonce.Uint64()}
|
return &BlockNonceErr{header.Number, header.Hash(), header.Nonce.Uint64()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -344,7 +345,7 @@ func calcDifficultyFrontier(time, parentTime uint64, parentNumber, parentDiff *b
|
||||||
expDiff := periodCount.Sub(periodCount, common.Big2)
|
expDiff := periodCount.Sub(periodCount, common.Big2)
|
||||||
expDiff.Exp(common.Big2, expDiff, nil)
|
expDiff.Exp(common.Big2, expDiff, nil)
|
||||||
diff.Add(diff, expDiff)
|
diff.Add(diff, expDiff)
|
||||||
diff = common.BigMax(diff, params.MinimumDifficulty)
|
diff = math.BigMax(diff, params.MinimumDifficulty)
|
||||||
}
|
}
|
||||||
|
|
||||||
return diff
|
return diff
|
||||||
|
|
@ -372,13 +373,13 @@ func CalcGasLimit(parent *types.Block) *big.Int {
|
||||||
*/
|
*/
|
||||||
gl := new(big.Int).Sub(parent.GasLimit(), decay)
|
gl := new(big.Int).Sub(parent.GasLimit(), decay)
|
||||||
gl = gl.Add(gl, contrib)
|
gl = gl.Add(gl, contrib)
|
||||||
gl.Set(common.BigMax(gl, params.MinGasLimit))
|
gl.Set(math.BigMax(gl, params.MinGasLimit))
|
||||||
|
|
||||||
// however, if we're now below the target (TargetGasLimit) we increase the
|
// however, if we're now below the target (TargetGasLimit) we increase the
|
||||||
// limit as much as we can (parentGasLimit / 1024 -1)
|
// limit as much as we can (parentGasLimit / 1024 -1)
|
||||||
if gl.Cmp(params.TargetGasLimit) < 0 {
|
if gl.Cmp(params.TargetGasLimit) < 0 {
|
||||||
gl.Add(parent.GasLimit(), decay)
|
gl.Add(parent.GasLimit(), decay)
|
||||||
gl.Set(common.BigMin(gl, params.TargetGasLimit))
|
gl.Set(math.BigMin(gl, params.TargetGasLimit))
|
||||||
}
|
}
|
||||||
return gl
|
return gl
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,50 +17,36 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testChainConfig() *params.ChainConfig {
|
func testGenesis(account common.Address, balance *big.Int) *Genesis {
|
||||||
return params.TestChainConfig
|
return &Genesis{
|
||||||
//return ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0)}
|
Config: params.TestChainConfig,
|
||||||
}
|
Alloc: GenesisAlloc{account: {Balance: balance}},
|
||||||
|
|
||||||
func proc() (Validator, *BlockChain) {
|
|
||||||
db, _ := ethdb.NewMemDatabase()
|
|
||||||
var mux event.TypeMux
|
|
||||||
|
|
||||||
WriteTestNetGenesisBlock(db)
|
|
||||||
blockchain, err := NewBlockChain(db, testChainConfig(), thePow(), &mux, vm.Config{})
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
}
|
||||||
return blockchain.validator, blockchain
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNumber(t *testing.T) {
|
func TestNumber(t *testing.T) {
|
||||||
_, chain := proc()
|
chain := newTestBlockChain()
|
||||||
|
|
||||||
statedb, _ := state.New(chain.Genesis().Root(), chain.chainDb)
|
statedb, _ := state.New(chain.Genesis().Root(), chain.chainDb)
|
||||||
cfg := testChainConfig()
|
header := makeHeader(chain.config, chain.Genesis(), statedb)
|
||||||
header := makeHeader(cfg, chain.Genesis(), statedb)
|
|
||||||
header.Number = big.NewInt(3)
|
header.Number = big.NewInt(3)
|
||||||
err := ValidateHeader(cfg, FakePow{}, header, chain.Genesis().Header(), false, false)
|
err := ValidateHeader(chain.config, pow.FakePow{}, header, chain.Genesis().Header(), false, false)
|
||||||
if err != BlockNumberErr {
|
if err != BlockNumberErr {
|
||||||
t.Errorf("expected block number error, got %q", err)
|
t.Errorf("expected block number error, got %q", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
header = makeHeader(cfg, chain.Genesis(), statedb)
|
header = makeHeader(chain.config, chain.Genesis(), statedb)
|
||||||
err = ValidateHeader(cfg, FakePow{}, header, chain.Genesis().Header(), false, false)
|
err = ValidateHeader(chain.config, pow.FakePow{}, header, chain.Genesis().Header(), false, false)
|
||||||
if err == BlockNumberErr {
|
if err == BlockNumberErr {
|
||||||
t.Errorf("didn't expect block number error")
|
t.Errorf("didn't expect block number error")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
|
||||||
"github.com/ethereum/go-ethereum/metrics"
|
"github.com/ethereum/go-ethereum/metrics"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/pow"
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
|
|
@ -60,6 +59,7 @@ const (
|
||||||
// must be bumped when consensus algorithm is changed, this forces the upgradedb
|
// must be bumped when consensus algorithm is changed, this forces the upgradedb
|
||||||
// command to be run (forces the blocks to be imported again using the new algorithm)
|
// command to be run (forces the blocks to be imported again using the new algorithm)
|
||||||
BlockChainVersion = 3
|
BlockChainVersion = 3
|
||||||
|
badBlockLimit = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
// BlockChain represents the canonical chain given a database with a genesis
|
// BlockChain represents the canonical chain given a database with a genesis
|
||||||
|
|
@ -108,6 +108,8 @@ type BlockChain struct {
|
||||||
processor Processor // block processor interface
|
processor Processor // block processor interface
|
||||||
validator Validator // block and state validator interface
|
validator Validator // block and state validator interface
|
||||||
vmConfig vm.Config
|
vmConfig vm.Config
|
||||||
|
|
||||||
|
badBlocks *lru.Cache // Bad block cache
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBlockChain returns a fully initialised block chain using information
|
// NewBlockChain returns a fully initialised block chain using information
|
||||||
|
|
@ -118,6 +120,7 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.P
|
||||||
bodyRLPCache, _ := lru.New(bodyCacheLimit)
|
bodyRLPCache, _ := lru.New(bodyCacheLimit)
|
||||||
blockCache, _ := lru.New(blockCacheLimit)
|
blockCache, _ := lru.New(blockCacheLimit)
|
||||||
futureBlocks, _ := lru.New(maxFutureBlocks)
|
futureBlocks, _ := lru.New(maxFutureBlocks)
|
||||||
|
badBlocks, _ := lru.New(badBlockLimit)
|
||||||
|
|
||||||
bc := &BlockChain{
|
bc := &BlockChain{
|
||||||
config: config,
|
config: config,
|
||||||
|
|
@ -130,6 +133,7 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.P
|
||||||
futureBlocks: futureBlocks,
|
futureBlocks: futureBlocks,
|
||||||
pow: pow,
|
pow: pow,
|
||||||
vmConfig: vmConfig,
|
vmConfig: vmConfig,
|
||||||
|
badBlocks: badBlocks,
|
||||||
}
|
}
|
||||||
bc.SetValidator(NewBlockValidator(config, bc, pow))
|
bc.SetValidator(NewBlockValidator(config, bc, pow))
|
||||||
bc.SetProcessor(NewStateProcessor(config, bc))
|
bc.SetProcessor(NewStateProcessor(config, bc))
|
||||||
|
|
@ -156,9 +160,9 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.P
|
||||||
headerByNumber := bc.GetHeaderByNumber(header.Number.Uint64())
|
headerByNumber := bc.GetHeaderByNumber(header.Number.Uint64())
|
||||||
// make sure the headerByNumber (if present) is in our current canonical chain
|
// make sure the headerByNumber (if present) is in our current canonical chain
|
||||||
if headerByNumber != nil && headerByNumber.Hash() == header.Hash() {
|
if headerByNumber != nil && headerByNumber.Hash() == header.Hash() {
|
||||||
glog.V(logger.Error).Infof("Found bad hash, rewinding chain to block #%d [%x…]", header.Number, header.ParentHash[:4])
|
log.Error("Found bad hash, rewinding chain", "number", header.Number, "hash", header.ParentHash)
|
||||||
bc.SetHead(header.Number.Uint64() - 1)
|
bc.SetHead(header.Number.Uint64() - 1)
|
||||||
glog.V(logger.Error).Infoln("Chain rewind was successful, resuming normal operation")
|
log.Error("Chain rewind was successful, resuming normal operation")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -178,16 +182,25 @@ func (self *BlockChain) loadLastState() error {
|
||||||
head := GetHeadBlockHash(self.chainDb)
|
head := GetHeadBlockHash(self.chainDb)
|
||||||
if head == (common.Hash{}) {
|
if head == (common.Hash{}) {
|
||||||
// Corrupt or empty database, init from scratch
|
// Corrupt or empty database, init from scratch
|
||||||
self.Reset()
|
log.Warn("Empty database, resetting chain")
|
||||||
} else {
|
return self.Reset()
|
||||||
if block := self.GetBlockByHash(head); block != nil {
|
}
|
||||||
// Block found, set as the current head
|
// Make sure the entire head block is available
|
||||||
self.currentBlock = block
|
currentBlock := self.GetBlockByHash(head)
|
||||||
} else {
|
if currentBlock == nil {
|
||||||
// Corrupt or empty database, init from scratch
|
// Corrupt or empty database, init from scratch
|
||||||
self.Reset()
|
log.Warn("Head block missing, resetting chain", "hash", head)
|
||||||
|
return self.Reset()
|
||||||
}
|
}
|
||||||
|
// Make sure the state associated with the block is available
|
||||||
|
if _, err := state.New(currentBlock.Root(), self.chainDb); err != nil {
|
||||||
|
// Dangling block without a state associated, init from scratch
|
||||||
|
log.Warn("Head state missing, resetting chain", "number", currentBlock.Number(), "hash", currentBlock.Hash())
|
||||||
|
return self.Reset()
|
||||||
}
|
}
|
||||||
|
// Everything seems to be fine, set as the head block
|
||||||
|
self.currentBlock = currentBlock
|
||||||
|
|
||||||
// Restore the last known head header
|
// Restore the last known head header
|
||||||
currentHeader := self.currentBlock.Header()
|
currentHeader := self.currentBlock.Header()
|
||||||
if head := GetHeadHeaderHash(self.chainDb); head != (common.Hash{}) {
|
if head := GetHeadHeaderHash(self.chainDb); head != (common.Hash{}) {
|
||||||
|
|
@ -196,6 +209,7 @@ func (self *BlockChain) loadLastState() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.hc.SetCurrentHeader(currentHeader)
|
self.hc.SetCurrentHeader(currentHeader)
|
||||||
|
|
||||||
// Restore the last known head fast block
|
// Restore the last known head fast block
|
||||||
self.currentFastBlock = self.currentBlock
|
self.currentFastBlock = self.currentBlock
|
||||||
if head := GetHeadFastBlockHash(self.chainDb); head != (common.Hash{}) {
|
if head := GetHeadFastBlockHash(self.chainDb); head != (common.Hash{}) {
|
||||||
|
|
@ -209,16 +223,18 @@ func (self *BlockChain) loadLastState() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
self.stateCache = statedb
|
self.stateCache = statedb
|
||||||
self.stateCache.GetAccount(common.Address{})
|
|
||||||
|
|
||||||
// Issue a status log for the user
|
// Issue a status log for the user
|
||||||
headerTd := self.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64())
|
headerTd := self.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64())
|
||||||
blockTd := self.GetTd(self.currentBlock.Hash(), self.currentBlock.NumberU64())
|
blockTd := self.GetTd(self.currentBlock.Hash(), self.currentBlock.NumberU64())
|
||||||
fastTd := self.GetTd(self.currentFastBlock.Hash(), self.currentFastBlock.NumberU64())
|
fastTd := self.GetTd(self.currentFastBlock.Hash(), self.currentFastBlock.NumberU64())
|
||||||
|
|
||||||
glog.V(logger.Info).Infof("Last header: #%d [%x…] TD=%v", currentHeader.Number, currentHeader.Hash().Bytes()[:4], headerTd)
|
log.Info("Loaded most recent local header", "number", currentHeader.Number, "hash", currentHeader.Hash(), "td", headerTd)
|
||||||
glog.V(logger.Info).Infof("Last block: #%d [%x…] TD=%v", self.currentBlock.Number(), self.currentBlock.Hash().Bytes()[:4], blockTd)
|
log.Info("Loaded most recent local full block", "number", self.currentBlock.Number(), "hash", self.currentBlock.Hash(), "td", blockTd)
|
||||||
glog.V(logger.Info).Infof("Fast block: #%d [%x…] TD=%v", self.currentFastBlock.Number(), self.currentFastBlock.Hash().Bytes()[:4], fastTd)
|
log.Info("Loaded most recent local fast block", "number", self.currentFastBlock.Number(), "hash", self.currentFastBlock.Hash(), "td", fastTd)
|
||||||
|
|
||||||
|
// Try to be smart and issue a pow verification for the head to pre-generate caches
|
||||||
|
go self.pow.Verify(types.NewBlockWithHeader(currentHeader))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -227,14 +243,18 @@ func (self *BlockChain) loadLastState() error {
|
||||||
// above the new head will be deleted and the new one set. In the case of blocks
|
// above the new head will be deleted and the new one set. In the case of blocks
|
||||||
// though, the head may be further rewound if block bodies are missing (non-archive
|
// though, the head may be further rewound if block bodies are missing (non-archive
|
||||||
// nodes after a fast sync).
|
// nodes after a fast sync).
|
||||||
func (bc *BlockChain) SetHead(head uint64) {
|
func (bc *BlockChain) SetHead(head uint64) error {
|
||||||
|
log.Warn("Rewinding blockchain", "target", head)
|
||||||
|
|
||||||
bc.mu.Lock()
|
bc.mu.Lock()
|
||||||
defer bc.mu.Unlock()
|
defer bc.mu.Unlock()
|
||||||
|
|
||||||
|
// Rewind the header chain, deleting all block bodies until then
|
||||||
delFn := func(hash common.Hash, num uint64) {
|
delFn := func(hash common.Hash, num uint64) {
|
||||||
DeleteBody(bc.chainDb, hash, num)
|
DeleteBody(bc.chainDb, hash, num)
|
||||||
}
|
}
|
||||||
bc.hc.SetHead(head, delFn)
|
bc.hc.SetHead(head, delFn)
|
||||||
|
currentHeader := bc.hc.CurrentHeader()
|
||||||
|
|
||||||
// Clear out any stale content from the caches
|
// Clear out any stale content from the caches
|
||||||
bc.bodyCache.Purge()
|
bc.bodyCache.Purge()
|
||||||
|
|
@ -242,29 +262,34 @@ func (bc *BlockChain) SetHead(head uint64) {
|
||||||
bc.blockCache.Purge()
|
bc.blockCache.Purge()
|
||||||
bc.futureBlocks.Purge()
|
bc.futureBlocks.Purge()
|
||||||
|
|
||||||
// Update all computed fields to the new head
|
// Rewind the block chain, ensuring we don't end up with a stateless head block
|
||||||
currentHeader := bc.hc.CurrentHeader()
|
|
||||||
if bc.currentBlock != nil && currentHeader.Number.Uint64() < bc.currentBlock.NumberU64() {
|
if bc.currentBlock != nil && currentHeader.Number.Uint64() < bc.currentBlock.NumberU64() {
|
||||||
bc.currentBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
|
bc.currentBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
|
||||||
}
|
}
|
||||||
|
if bc.currentBlock != nil {
|
||||||
|
if _, err := state.New(bc.currentBlock.Root(), bc.chainDb); err != nil {
|
||||||
|
// Rewound state missing, rolled back to before pivot, reset to genesis
|
||||||
|
bc.currentBlock = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Rewind the fast block in a simpleton way to the target head
|
||||||
if bc.currentFastBlock != nil && currentHeader.Number.Uint64() < bc.currentFastBlock.NumberU64() {
|
if bc.currentFastBlock != nil && currentHeader.Number.Uint64() < bc.currentFastBlock.NumberU64() {
|
||||||
bc.currentFastBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
|
bc.currentFastBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
|
||||||
}
|
}
|
||||||
|
// If either blocks reached nil, reset to the genesis state
|
||||||
if bc.currentBlock == nil {
|
if bc.currentBlock == nil {
|
||||||
bc.currentBlock = bc.genesisBlock
|
bc.currentBlock = bc.genesisBlock
|
||||||
}
|
}
|
||||||
if bc.currentFastBlock == nil {
|
if bc.currentFastBlock == nil {
|
||||||
bc.currentFastBlock = bc.genesisBlock
|
bc.currentFastBlock = bc.genesisBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := WriteHeadBlockHash(bc.chainDb, bc.currentBlock.Hash()); err != nil {
|
if err := WriteHeadBlockHash(bc.chainDb, bc.currentBlock.Hash()); err != nil {
|
||||||
glog.Fatalf("failed to reset head block hash: %v", err)
|
log.Crit("Failed to reset head full block", "err", err)
|
||||||
}
|
}
|
||||||
if err := WriteHeadFastBlockHash(bc.chainDb, bc.currentFastBlock.Hash()); err != nil {
|
if err := WriteHeadFastBlockHash(bc.chainDb, bc.currentFastBlock.Hash()); err != nil {
|
||||||
glog.Fatalf("failed to reset head fast block hash: %v", err)
|
log.Crit("Failed to reset head fast block", "err", err)
|
||||||
}
|
}
|
||||||
bc.loadLastState()
|
return bc.loadLastState()
|
||||||
}
|
}
|
||||||
|
|
||||||
// FastSyncCommitHead sets the current head block to the one defined by the hash
|
// FastSyncCommitHead sets the current head block to the one defined by the hash
|
||||||
|
|
@ -283,7 +308,7 @@ func (self *BlockChain) FastSyncCommitHead(hash common.Hash) error {
|
||||||
self.currentBlock = block
|
self.currentBlock = block
|
||||||
self.mu.Unlock()
|
self.mu.Unlock()
|
||||||
|
|
||||||
glog.V(logger.Info).Infof("committed block #%d [%x…] as new head", block.Number(), hash[:4])
|
log.Info("Committed new head block", "number", block.Number(), "hash", hash)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -372,25 +397,26 @@ func (self *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset purges the entire blockchain, restoring it to its genesis state.
|
// Reset purges the entire blockchain, restoring it to its genesis state.
|
||||||
func (bc *BlockChain) Reset() {
|
func (bc *BlockChain) Reset() error {
|
||||||
bc.ResetWithGenesisBlock(bc.genesisBlock)
|
return bc.ResetWithGenesisBlock(bc.genesisBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ResetWithGenesisBlock purges the entire blockchain, restoring it to the
|
// ResetWithGenesisBlock purges the entire blockchain, restoring it to the
|
||||||
// specified genesis state.
|
// specified genesis state.
|
||||||
func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) {
|
func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
|
||||||
// Dump the entire block chain and purge the caches
|
// Dump the entire block chain and purge the caches
|
||||||
bc.SetHead(0)
|
if err := bc.SetHead(0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
bc.mu.Lock()
|
bc.mu.Lock()
|
||||||
defer bc.mu.Unlock()
|
defer bc.mu.Unlock()
|
||||||
|
|
||||||
// Prepare the genesis block and reinitialise the chain
|
// Prepare the genesis block and reinitialise the chain
|
||||||
if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
|
if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
|
||||||
glog.Fatalf("failed to write genesis block TD: %v", err)
|
log.Crit("Failed to write genesis block TD", "err", err)
|
||||||
}
|
}
|
||||||
if err := WriteBlock(bc.chainDb, genesis); err != nil {
|
if err := WriteBlock(bc.chainDb, genesis); err != nil {
|
||||||
glog.Fatalf("failed to write genesis block: %v", err)
|
log.Crit("Failed to write genesis block", "err", err)
|
||||||
}
|
}
|
||||||
bc.genesisBlock = genesis
|
bc.genesisBlock = genesis
|
||||||
bc.insert(bc.genesisBlock)
|
bc.insert(bc.genesisBlock)
|
||||||
|
|
@ -398,6 +424,8 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) {
|
||||||
bc.hc.SetGenesis(bc.genesisBlock.Header())
|
bc.hc.SetGenesis(bc.genesisBlock.Header())
|
||||||
bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
|
bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
|
||||||
bc.currentFastBlock = bc.genesisBlock
|
bc.currentFastBlock = bc.genesisBlock
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export writes the active chain to the given writer.
|
// Export writes the active chain to the given writer.
|
||||||
|
|
@ -413,8 +441,7 @@ func (self *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
|
||||||
if first > last {
|
if first > last {
|
||||||
return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last)
|
return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last)
|
||||||
}
|
}
|
||||||
|
log.Info("Exporting batch of blocks", "count", last-first+1)
|
||||||
glog.V(logger.Info).Infof("exporting %d blocks...\n", last-first+1)
|
|
||||||
|
|
||||||
for nr := first; nr <= last; nr++ {
|
for nr := first; nr <= last; nr++ {
|
||||||
block := self.GetBlockByNumber(nr)
|
block := self.GetBlockByNumber(nr)
|
||||||
|
|
@ -442,10 +469,10 @@ func (bc *BlockChain) insert(block *types.Block) {
|
||||||
|
|
||||||
// Add the block to the canonical chain number scheme and mark as the head
|
// Add the block to the canonical chain number scheme and mark as the head
|
||||||
if err := WriteCanonicalHash(bc.chainDb, block.Hash(), block.NumberU64()); err != nil {
|
if err := WriteCanonicalHash(bc.chainDb, block.Hash(), block.NumberU64()); err != nil {
|
||||||
glog.Fatalf("failed to insert block number: %v", err)
|
log.Crit("Failed to insert block number", "err", err)
|
||||||
}
|
}
|
||||||
if err := WriteHeadBlockHash(bc.chainDb, block.Hash()); err != nil {
|
if err := WriteHeadBlockHash(bc.chainDb, block.Hash()); err != nil {
|
||||||
glog.Fatalf("failed to insert head block hash: %v", err)
|
log.Crit("Failed to insert head block hash", "err", err)
|
||||||
}
|
}
|
||||||
bc.currentBlock = block
|
bc.currentBlock = block
|
||||||
|
|
||||||
|
|
@ -454,7 +481,7 @@ func (bc *BlockChain) insert(block *types.Block) {
|
||||||
bc.hc.SetCurrentHeader(block.Header())
|
bc.hc.SetCurrentHeader(block.Header())
|
||||||
|
|
||||||
if err := WriteHeadFastBlockHash(bc.chainDb, block.Hash()); err != nil {
|
if err := WriteHeadFastBlockHash(bc.chainDb, block.Hash()); err != nil {
|
||||||
glog.Fatalf("failed to insert head fast block hash: %v", err)
|
log.Crit("Failed to insert head fast block hash", "err", err)
|
||||||
}
|
}
|
||||||
bc.currentFastBlock = block
|
bc.currentFastBlock = block
|
||||||
}
|
}
|
||||||
|
|
@ -585,14 +612,13 @@ func (bc *BlockChain) Stop() {
|
||||||
atomic.StoreInt32(&bc.procInterrupt, 1)
|
atomic.StoreInt32(&bc.procInterrupt, 1)
|
||||||
|
|
||||||
bc.wg.Wait()
|
bc.wg.Wait()
|
||||||
|
log.Info("Blockchain manager stopped")
|
||||||
glog.V(logger.Info).Infoln("Chain manager stopped")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BlockChain) procFutureBlocks() {
|
func (self *BlockChain) procFutureBlocks() {
|
||||||
blocks := make([]*types.Block, 0, self.futureBlocks.Len())
|
blocks := make([]*types.Block, 0, self.futureBlocks.Len())
|
||||||
for _, hash := range self.futureBlocks.Keys() {
|
for _, hash := range self.futureBlocks.Keys() {
|
||||||
if block, exist := self.futureBlocks.Get(hash); exist {
|
if block, exist := self.futureBlocks.Peek(hash); exist {
|
||||||
blocks = append(blocks, block.(*types.Block))
|
blocks = append(blocks, block.(*types.Block))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -680,11 +706,11 @@ func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain
|
||||||
for i := 1; i < len(blockChain); i++ {
|
for i := 1; i < len(blockChain); i++ {
|
||||||
if blockChain[i].NumberU64() != blockChain[i-1].NumberU64()+1 || blockChain[i].ParentHash() != blockChain[i-1].Hash() {
|
if blockChain[i].NumberU64() != blockChain[i-1].NumberU64()+1 || blockChain[i].ParentHash() != blockChain[i-1].Hash() {
|
||||||
// Chain broke ancestry, log a messge (programming error) and skip insertion
|
// Chain broke ancestry, log a messge (programming error) and skip insertion
|
||||||
failure := fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, blockChain[i-1].NumberU64(),
|
log.Error("Non contiguous receipt insert", "number", blockChain[i].Number(), "hash", blockChain[i].Hash(), "parent", blockChain[i].ParentHash(),
|
||||||
blockChain[i-1].Hash().Bytes()[:4], i, blockChain[i].NumberU64(), blockChain[i].Hash().Bytes()[:4], blockChain[i].ParentHash().Bytes()[:4])
|
"prevnumber", blockChain[i-1].Number(), "prevhash", blockChain[i-1].Hash())
|
||||||
|
|
||||||
glog.V(logger.Error).Info(failure.Error())
|
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, blockChain[i-1].NumberU64(),
|
||||||
return 0, failure
|
blockChain[i-1].Hash().Bytes()[:4], i, blockChain[i].NumberU64(), blockChain[i].Hash().Bytes()[:4], blockChain[i].ParentHash().Bytes()[:4])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Pre-checks passed, start the block body and receipt imports
|
// Pre-checks passed, start the block body and receipt imports
|
||||||
|
|
@ -731,31 +757,31 @@ func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain
|
||||||
if err := WriteBody(self.chainDb, block.Hash(), block.NumberU64(), block.Body()); err != nil {
|
if err := WriteBody(self.chainDb, block.Hash(), block.NumberU64(), block.Body()); err != nil {
|
||||||
errs[index] = fmt.Errorf("failed to write block body: %v", err)
|
errs[index] = fmt.Errorf("failed to write block body: %v", err)
|
||||||
atomic.AddInt32(&failed, 1)
|
atomic.AddInt32(&failed, 1)
|
||||||
glog.Fatal(errs[index])
|
log.Crit("Failed to write block body", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := WriteBlockReceipts(self.chainDb, block.Hash(), block.NumberU64(), receipts); err != nil {
|
if err := WriteBlockReceipts(self.chainDb, block.Hash(), block.NumberU64(), receipts); err != nil {
|
||||||
errs[index] = fmt.Errorf("failed to write block receipts: %v", err)
|
errs[index] = fmt.Errorf("failed to write block receipts: %v", err)
|
||||||
atomic.AddInt32(&failed, 1)
|
atomic.AddInt32(&failed, 1)
|
||||||
glog.Fatal(errs[index])
|
log.Crit("Failed to write block receipts", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := WriteMipmapBloom(self.chainDb, block.NumberU64(), receipts); err != nil {
|
if err := WriteMipmapBloom(self.chainDb, block.NumberU64(), receipts); err != nil {
|
||||||
errs[index] = fmt.Errorf("failed to write log blooms: %v", err)
|
errs[index] = fmt.Errorf("failed to write log blooms: %v", err)
|
||||||
atomic.AddInt32(&failed, 1)
|
atomic.AddInt32(&failed, 1)
|
||||||
glog.Fatal(errs[index])
|
log.Crit("Failed to write log blooms", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := WriteTransactions(self.chainDb, block); err != nil {
|
if err := WriteTransactions(self.chainDb, block); err != nil {
|
||||||
errs[index] = fmt.Errorf("failed to write individual transactions: %v", err)
|
errs[index] = fmt.Errorf("failed to write individual transactions: %v", err)
|
||||||
atomic.AddInt32(&failed, 1)
|
atomic.AddInt32(&failed, 1)
|
||||||
glog.Fatal(errs[index])
|
log.Crit("Failed to write individual transactions", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := WriteReceipts(self.chainDb, receipts); err != nil {
|
if err := WriteReceipts(self.chainDb, receipts); err != nil {
|
||||||
errs[index] = fmt.Errorf("failed to write individual receipts: %v", err)
|
errs[index] = fmt.Errorf("failed to write individual receipts: %v", err)
|
||||||
atomic.AddInt32(&failed, 1)
|
atomic.AddInt32(&failed, 1)
|
||||||
glog.Fatal(errs[index])
|
log.Crit("Failed to write individual receipts", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
atomic.AddInt32(&stats.processed, 1)
|
atomic.AddInt32(&stats.processed, 1)
|
||||||
|
|
@ -781,28 +807,27 @@ func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if atomic.LoadInt32(&self.procInterrupt) == 1 {
|
if atomic.LoadInt32(&self.procInterrupt) == 1 {
|
||||||
glog.V(logger.Debug).Infoln("premature abort during receipt chain processing")
|
log.Debug("Premature abort during receipts processing")
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
// Update the head fast sync block if better
|
// Update the head fast sync block if better
|
||||||
self.mu.Lock()
|
self.mu.Lock()
|
||||||
|
|
||||||
head := blockChain[len(errs)-1]
|
head := blockChain[len(errs)-1]
|
||||||
if self.GetTd(self.currentFastBlock.Hash(), self.currentFastBlock.NumberU64()).Cmp(self.GetTd(head.Hash(), head.NumberU64())) < 0 {
|
if td := self.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case
|
||||||
|
if self.GetTd(self.currentFastBlock.Hash(), self.currentFastBlock.NumberU64()).Cmp(td) < 0 {
|
||||||
if err := WriteHeadFastBlockHash(self.chainDb, head.Hash()); err != nil {
|
if err := WriteHeadFastBlockHash(self.chainDb, head.Hash()); err != nil {
|
||||||
glog.Fatalf("failed to update head fast block hash: %v", err)
|
log.Crit("Failed to update head fast block hash", "err", err)
|
||||||
}
|
}
|
||||||
self.currentFastBlock = head
|
self.currentFastBlock = head
|
||||||
}
|
}
|
||||||
|
}
|
||||||
self.mu.Unlock()
|
self.mu.Unlock()
|
||||||
|
|
||||||
// Report some public statistics so the user has a clue what's going on
|
// Report some public statistics so the user has a clue what's going on
|
||||||
first, last := blockChain[0], blockChain[len(blockChain)-1]
|
last := blockChain[len(blockChain)-1]
|
||||||
|
log.Info("Imported new block receipts", "count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)),
|
||||||
ignored := ""
|
"number", last.Number(), "hash", last.Hash(), "ignored", stats.ignored)
|
||||||
if stats.ignored > 0 {
|
|
||||||
ignored = fmt.Sprintf(" (%d ignored)", stats.ignored)
|
|
||||||
}
|
|
||||||
glog.V(logger.Info).Infof("imported %4d receipts in %9v. #%d [%x… / %x…]%s", stats.processed, common.PrettyDuration(time.Since(start)), last.Number(), first.Hash().Bytes()[:4], last.Hash().Bytes()[:4], ignored)
|
|
||||||
|
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
@ -826,10 +851,10 @@ func (self *BlockChain) WriteBlock(block *types.Block) (status WriteStatus, err
|
||||||
|
|
||||||
// Irrelevant of the canonical status, write the block itself to the database
|
// Irrelevant of the canonical status, write the block itself to the database
|
||||||
if err := self.hc.WriteTd(block.Hash(), block.NumberU64(), externTd); err != nil {
|
if err := self.hc.WriteTd(block.Hash(), block.NumberU64(), externTd); err != nil {
|
||||||
glog.Fatalf("failed to write block total difficulty: %v", err)
|
log.Crit("Failed to write block total difficulty", "err", err)
|
||||||
}
|
}
|
||||||
if err := WriteBlock(self.chainDb, block); err != nil {
|
if err := WriteBlock(self.chainDb, block); err != nil {
|
||||||
glog.Fatalf("failed to write block contents: %v", err)
|
log.Crit("Failed to write block contents", "err", 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
|
||||||
|
|
@ -860,11 +885,11 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
for i := 1; i < len(chain); i++ {
|
for i := 1; i < len(chain); i++ {
|
||||||
if chain[i].NumberU64() != chain[i-1].NumberU64()+1 || chain[i].ParentHash() != chain[i-1].Hash() {
|
if chain[i].NumberU64() != chain[i-1].NumberU64()+1 || chain[i].ParentHash() != chain[i-1].Hash() {
|
||||||
// Chain broke ancestry, log a messge (programming error) and skip insertion
|
// Chain broke ancestry, log a messge (programming error) and skip insertion
|
||||||
failure := fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])",
|
log.Error("Non contiguous block insert", "number", chain[i].Number(), "hash", chain[i].Hash(),
|
||||||
i-1, chain[i-1].NumberU64(), chain[i-1].Hash().Bytes()[:4], i, chain[i].NumberU64(), chain[i].Hash().Bytes()[:4], chain[i].ParentHash().Bytes()[:4])
|
"parent", chain[i].ParentHash(), "prevnumber", chain[i-1].Number(), "prevhash", chain[i-1].Hash())
|
||||||
|
|
||||||
glog.V(logger.Error).Info(failure.Error())
|
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, chain[i-1].NumberU64(),
|
||||||
return 0, failure
|
chain[i-1].Hash().Bytes()[:4], i, chain[i].NumberU64(), chain[i].Hash().Bytes()[:4], chain[i].ParentHash().Bytes()[:4])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Pre-checks passed, start the full block imports
|
// Pre-checks passed, start the full block imports
|
||||||
|
|
@ -890,10 +915,9 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
|
|
||||||
for i, block := range chain {
|
for i, block := range chain {
|
||||||
if atomic.LoadInt32(&self.procInterrupt) == 1 {
|
if atomic.LoadInt32(&self.procInterrupt) == 1 {
|
||||||
glog.V(logger.Debug).Infoln("Premature abort during block chain processing")
|
log.Debug("Premature abort during blocks processing")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
bstart := time.Now()
|
bstart := time.Now()
|
||||||
// Wait for block i's nonce to be verified before processing
|
// Wait for block i's nonce to be verified before processing
|
||||||
// its state transition.
|
// its state transition.
|
||||||
|
|
@ -901,8 +925,8 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
r := <-nonceResults
|
r := <-nonceResults
|
||||||
nonceChecked[r.index] = true
|
nonceChecked[r.index] = true
|
||||||
if !r.valid {
|
if !r.valid {
|
||||||
block := chain[r.index]
|
invalid := chain[r.index]
|
||||||
return r.index, &BlockNonceErr{Hash: block.Hash(), Number: block.Number(), Nonce: block.Nonce()}
|
return r.index, &BlockNonceErr{Hash: invalid.Hash(), Number: invalid.Number(), Nonce: invalid.Nonce()}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -976,7 +1000,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
// coalesce logs for later processing
|
// coalesce logs for later processing
|
||||||
coalescedLogs = append(coalescedLogs, logs...)
|
coalescedLogs = append(coalescedLogs, logs...)
|
||||||
|
|
||||||
if err := WriteBlockReceipts(self.chainDb, block.Hash(), block.NumberU64(), receipts); err != nil {
|
if err = WriteBlockReceipts(self.chainDb, block.Hash(), block.NumberU64(), receipts); err != nil {
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -988,9 +1012,9 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
|
|
||||||
switch status {
|
switch status {
|
||||||
case CanonStatTy:
|
case CanonStatTy:
|
||||||
if glog.V(logger.Debug) {
|
log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()),
|
||||||
glog.Infof("inserted block #%d [%x…] in %9v: %3d txs %7v gas %d uncles.", block.Number(), block.Hash().Bytes()[0:4], common.PrettyDuration(time.Since(bstart)), len(block.Transactions()), block.GasUsed(), len(block.Uncles()))
|
"txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(bstart)))
|
||||||
}
|
|
||||||
blockInsertTimer.UpdateSince(bstart)
|
blockInsertTimer.UpdateSince(bstart)
|
||||||
events = append(events, ChainEvent{block, block.Hash(), logs})
|
events = append(events, ChainEvent{block, block.Hash(), logs})
|
||||||
|
|
||||||
|
|
@ -1011,23 +1035,19 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
case SideStatTy:
|
case SideStatTy:
|
||||||
if glog.V(logger.Detail) {
|
log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), "elapsed",
|
||||||
glog.Infof("inserted forked block #%d [%x…] (TD=%v) in %9v: %3d txs %d uncles.", block.Number(), block.Hash().Bytes()[0:4], block.Difficulty(), common.PrettyDuration(time.Since(bstart)), len(block.Transactions()), len(block.Uncles()))
|
common.PrettyDuration(time.Since(bstart)), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()))
|
||||||
}
|
|
||||||
blockInsertTimer.UpdateSince(bstart)
|
blockInsertTimer.UpdateSince(bstart)
|
||||||
events = append(events, ChainSideEvent{block})
|
events = append(events, ChainSideEvent{block})
|
||||||
|
|
||||||
case SplitStatTy:
|
case SplitStatTy:
|
||||||
events = append(events, ChainSplitEvent{block, logs})
|
events = append(events, ChainSplitEvent{block, logs})
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.processed++
|
stats.processed++
|
||||||
if glog.V(logger.Info) {
|
|
||||||
stats.usedGas += usedGas.Uint64()
|
stats.usedGas += usedGas.Uint64()
|
||||||
stats.report(chain, i)
|
stats.report(chain, i)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
go self.postChainEvents(events, coalescedLogs)
|
go self.postChainEvents(events, coalescedLogs)
|
||||||
|
|
||||||
return 0, nil
|
return 0, nil
|
||||||
|
|
@ -1055,19 +1075,22 @@ func (st *insertStats) report(chain []*types.Block, index int) {
|
||||||
)
|
)
|
||||||
// If we're at the last block of the batch or report period reached, log
|
// If we're at the last block of the batch or report period reached, log
|
||||||
if index == len(chain)-1 || elapsed >= statsReportLimit {
|
if index == len(chain)-1 || elapsed >= statsReportLimit {
|
||||||
start, end := chain[st.lastIndex], chain[index]
|
var (
|
||||||
txcount := countTransactions(chain[st.lastIndex : index+1])
|
end = chain[index]
|
||||||
|
txs = countTransactions(chain[st.lastIndex : index+1])
|
||||||
var hashes, extra string
|
)
|
||||||
if st.queued > 0 || st.ignored > 0 {
|
context := []interface{}{
|
||||||
extra = fmt.Sprintf(" (%d queued %d ignored)", st.queued, st.ignored)
|
"blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000,
|
||||||
|
"elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed),
|
||||||
|
"number", end.Number(), "hash", end.Hash(),
|
||||||
}
|
}
|
||||||
if st.processed > 1 {
|
if st.queued > 0 {
|
||||||
hashes = fmt.Sprintf("%x… / %x…", start.Hash().Bytes()[:4], end.Hash().Bytes()[:4])
|
context = append(context, []interface{}{"queued", st.queued}...)
|
||||||
} else {
|
|
||||||
hashes = fmt.Sprintf("%x…", end.Hash().Bytes()[:4])
|
|
||||||
}
|
}
|
||||||
glog.Infof("imported %4d blocks, %5d txs (%7.3f Mg) in %9v (%6.3f Mg/s). #%v [%s]%s", st.processed, txcount, float64(st.usedGas)/1000000, common.PrettyDuration(elapsed), float64(st.usedGas)*1000/float64(elapsed), end.Number(), hashes, extra)
|
if st.ignored > 0 {
|
||||||
|
context = append(context, []interface{}{"ignored", st.ignored}...)
|
||||||
|
}
|
||||||
|
log.Info("Imported new chain segment", context...)
|
||||||
|
|
||||||
*st = insertStats{startTime: now, lastIndex: index}
|
*st = insertStats{startTime: now, lastIndex: index}
|
||||||
}
|
}
|
||||||
|
|
@ -1147,22 +1170,17 @@ func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
||||||
return fmt.Errorf("Invalid new chain")
|
return fmt.Errorf("Invalid new chain")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Ensure the user sees large reorgs
|
||||||
if oldLen := len(oldChain); oldLen > 63 || glog.V(logger.Debug) {
|
if len(oldChain) > 0 && len(newChain) > 0 {
|
||||||
newLen := len(newChain)
|
logFn := log.Debug
|
||||||
newLast := newChain[0]
|
if len(oldChain) > 63 {
|
||||||
newFirst := newChain[newLen-1]
|
logFn = log.Warn
|
||||||
oldLast := oldChain[0]
|
}
|
||||||
oldFirst := oldChain[oldLen-1]
|
logFn("Chain split detected", "number", commonBlock.Number(), "hash", commonBlock.Hash(),
|
||||||
glog.Infof("Chain split detected after #%v [%x…]. Reorganising chain (-%v +%v blocks), rejecting #%v-#%v [%x…/%x…] in favour of #%v-#%v [%x…/%x…]",
|
"drop", len(oldChain), "dropfrom", oldChain[0].Hash(), "add", len(newChain), "addfrom", newChain[0].Hash())
|
||||||
commonBlock.Number(), commonBlock.Hash().Bytes()[:4],
|
} else {
|
||||||
oldLen, newLen,
|
log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "newnum", newBlock.Number(), "newhash", newBlock.Hash())
|
||||||
oldFirst.Number(), oldLast.Number(),
|
|
||||||
oldFirst.Hash().Bytes()[:4], oldLast.Hash().Bytes()[:4],
|
|
||||||
newFirst.Number(), newLast.Number(),
|
|
||||||
newFirst.Hash().Bytes()[:4], newLast.Hash().Bytes()[:4])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var addedTxs types.Transactions
|
var addedTxs types.Transactions
|
||||||
// insert blocks. Order does not matter. Last block will be written in ImportChain itself which creates the new head properly
|
// insert blocks. Order does not matter. Last block will be written in ImportChain itself which creates the new head properly
|
||||||
for _, block := range newChain {
|
for _, block := range newChain {
|
||||||
|
|
@ -1242,14 +1260,38 @@ func (self *BlockChain) update() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BadBlockArgs represents the entries in the list returned when bad blocks are queried.
|
||||||
|
type BadBlockArgs struct {
|
||||||
|
Hash common.Hash `json:"hash"`
|
||||||
|
Header *types.Header `json:"header"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BadBlocks returns a list of the last 'bad blocks' that the client has seen on the network
|
||||||
|
func (bc *BlockChain) BadBlocks() ([]BadBlockArgs, error) {
|
||||||
|
headers := make([]BadBlockArgs, 0, bc.badBlocks.Len())
|
||||||
|
for _, hash := range bc.badBlocks.Keys() {
|
||||||
|
if hdr, exist := bc.badBlocks.Peek(hash); exist {
|
||||||
|
header := hdr.(*types.Header)
|
||||||
|
headers = append(headers, BadBlockArgs{header.Hash(), header})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return headers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// addBadBlock adds a bad block to the bad-block LRU cache
|
||||||
|
func (bc *BlockChain) addBadBlock(block *types.Block) {
|
||||||
|
bc.badBlocks.Add(block.Header().Hash(), block.Header())
|
||||||
|
}
|
||||||
|
|
||||||
// reportBlock logs a bad block error.
|
// reportBlock logs a bad block error.
|
||||||
func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) {
|
func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) {
|
||||||
if glog.V(logger.Error) {
|
bc.addBadBlock(block)
|
||||||
|
|
||||||
var receiptString string
|
var receiptString string
|
||||||
for _, receipt := range receipts {
|
for _, receipt := range receipts {
|
||||||
receiptString += fmt.Sprintf("\t%v\n", receipt)
|
receiptString += fmt.Sprintf("\t%v\n", receipt)
|
||||||
}
|
}
|
||||||
glog.Errorf(`
|
log.Error(fmt.Sprintf(`
|
||||||
########## BAD BLOCK #########
|
########## BAD BLOCK #########
|
||||||
Chain config: %v
|
Chain config: %v
|
||||||
|
|
||||||
|
|
@ -1259,8 +1301,7 @@ Hash: 0x%x
|
||||||
|
|
||||||
Error: %v
|
Error: %v
|
||||||
##############################
|
##############################
|
||||||
`, bc.config, block.Number(), block.Hash(), receiptString, err)
|
`, bc.config, block.Number(), block.Hash(), receiptString, err))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// InsertHeaderChain attempts to insert the given header chain in to the local
|
// InsertHeaderChain attempts to insert the given header chain in to the local
|
||||||
|
|
@ -1272,6 +1313,11 @@ Error: %v
|
||||||
// of the header retrieval mechanisms already need to verify nonces, as well as
|
// of the header retrieval mechanisms already need to verify nonces, as well as
|
||||||
// because nonces can be verified sparsely, not needing to check each.
|
// because nonces can be verified sparsely, not needing to check each.
|
||||||
func (self *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) {
|
func (self *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) {
|
||||||
|
start := time.Now()
|
||||||
|
if i, err := self.hc.ValidateHeaderChain(chain, checkFreq); err != nil {
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure only one thread manipulates the chain at once
|
// Make sure only one thread manipulates the chain at once
|
||||||
self.chainmu.Lock()
|
self.chainmu.Lock()
|
||||||
defer self.chainmu.Unlock()
|
defer self.chainmu.Unlock()
|
||||||
|
|
@ -1287,7 +1333,7 @@ func (self *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.hc.InsertHeaderChain(chain, checkFreq, whFunc)
|
return self.hc.InsertHeaderChain(chain, whFunc, start)
|
||||||
}
|
}
|
||||||
|
|
||||||
// writeHeader writes a header into the local chain, given that its parent is
|
// writeHeader writes a header into the local chain, given that its parent is
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/ethash"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
|
@ -37,29 +32,21 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/pow"
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
|
||||||
"github.com/hashicorp/golang-lru"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
// newTestBlockChain creates a blockchain without validation.
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
func newTestBlockChain() *BlockChain {
|
||||||
}
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
gspec := &Genesis{
|
||||||
func thePow() pow.PoW {
|
Config: params.TestChainConfig,
|
||||||
pow, _ := ethash.NewForTesting()
|
Difficulty: big.NewInt(1),
|
||||||
return pow
|
|
||||||
}
|
|
||||||
|
|
||||||
func theBlockChain(db ethdb.Database, t *testing.T) *BlockChain {
|
|
||||||
var eventMux event.TypeMux
|
|
||||||
WriteTestNetGenesisBlock(db)
|
|
||||||
blockchain, err := NewBlockChain(db, testChainConfig(), thePow(), &eventMux, vm.Config{})
|
|
||||||
if err != nil {
|
|
||||||
t.Error("failed creating blockchain:", err)
|
|
||||||
t.FailNow()
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
gspec.MustCommit(db)
|
||||||
|
blockchain, err := NewBlockChain(db, gspec.Config, pow.FakePow{}, new(event.TypeMux), vm.Config{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
blockchain.SetValidator(bproc{})
|
||||||
return blockchain
|
return blockchain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,21 +164,6 @@ func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadChain(fn string, t *testing.T) (types.Blocks, error) {
|
|
||||||
fh, err := os.OpenFile(filepath.Join("..", "_data", fn), os.O_RDONLY, os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer fh.Close()
|
|
||||||
|
|
||||||
var chain types.Blocks
|
|
||||||
if err := rlp.Decode(fh, &chain); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return chain, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func insertChain(done chan bool, blockchain *BlockChain, chain types.Blocks, t *testing.T) {
|
func insertChain(done chan bool, blockchain *BlockChain, chain types.Blocks, t *testing.T) {
|
||||||
_, err := blockchain.InsertChain(chain)
|
_, err := blockchain.InsertChain(chain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -202,12 +174,10 @@ func insertChain(done chan bool, blockchain *BlockChain, chain types.Blocks, t *
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLastBlock(t *testing.T) {
|
func TestLastBlock(t *testing.T) {
|
||||||
db, _ := ethdb.NewMemDatabase()
|
bchain := newTestBlockChain()
|
||||||
|
block := makeBlockChain(bchain.CurrentBlock(), 1, bchain.chainDb, 0)[0]
|
||||||
bchain := theBlockChain(db, t)
|
|
||||||
block := makeBlockChain(bchain.CurrentBlock(), 1, db, 0)[0]
|
|
||||||
bchain.insert(block)
|
bchain.insert(block)
|
||||||
if block.Hash() != GetHeadBlockHash(db) {
|
if block.Hash() != GetHeadBlockHash(bchain.chainDb) {
|
||||||
t.Errorf("Write/Get HeadBlockHash failed")
|
t.Errorf("Write/Get HeadBlockHash failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -346,88 +316,6 @@ func testBrokenChain(t *testing.T, full bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestChainInsertions(t *testing.T) {
|
|
||||||
t.Skip("Skipped: outdated test files")
|
|
||||||
|
|
||||||
db, _ := ethdb.NewMemDatabase()
|
|
||||||
|
|
||||||
chain1, err := loadChain("valid1", t)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
chain2, err := loadChain("valid2", t)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
|
|
||||||
blockchain := theBlockChain(db, t)
|
|
||||||
|
|
||||||
const max = 2
|
|
||||||
done := make(chan bool, max)
|
|
||||||
|
|
||||||
go insertChain(done, blockchain, chain1, t)
|
|
||||||
go insertChain(done, blockchain, chain2, t)
|
|
||||||
|
|
||||||
for i := 0; i < max; i++ {
|
|
||||||
<-done
|
|
||||||
}
|
|
||||||
|
|
||||||
if chain2[len(chain2)-1].Hash() != blockchain.CurrentBlock().Hash() {
|
|
||||||
t.Error("chain2 is canonical and shouldn't be")
|
|
||||||
}
|
|
||||||
|
|
||||||
if chain1[len(chain1)-1].Hash() != blockchain.CurrentBlock().Hash() {
|
|
||||||
t.Error("chain1 isn't canonical and should be")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestChainMultipleInsertions(t *testing.T) {
|
|
||||||
t.Skip("Skipped: outdated test files")
|
|
||||||
|
|
||||||
db, _ := ethdb.NewMemDatabase()
|
|
||||||
|
|
||||||
const max = 4
|
|
||||||
chains := make([]types.Blocks, max)
|
|
||||||
var longest int
|
|
||||||
for i := 0; i < max; i++ {
|
|
||||||
var err error
|
|
||||||
name := "valid" + strconv.Itoa(i+1)
|
|
||||||
chains[i], err = loadChain(name, t)
|
|
||||||
if len(chains[i]) >= len(chains[longest]) {
|
|
||||||
longest = i
|
|
||||||
}
|
|
||||||
fmt.Println("loaded", name, "with a length of", len(chains[i]))
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
t.FailNow()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
blockchain := theBlockChain(db, t)
|
|
||||||
|
|
||||||
done := make(chan bool, max)
|
|
||||||
for i, chain := range chains {
|
|
||||||
// XXX the go routine would otherwise reference the same (chain[3]) variable and fail
|
|
||||||
i := i
|
|
||||||
chain := chain
|
|
||||||
go func() {
|
|
||||||
insertChain(done, blockchain, chain, t)
|
|
||||||
fmt.Println(i, "done")
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < max; i++ {
|
|
||||||
<-done
|
|
||||||
}
|
|
||||||
|
|
||||||
if chains[longest][len(chains[longest])-1].Hash() != blockchain.CurrentBlock().Hash() {
|
|
||||||
t.Error("Invalid canonical chain")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type bproc struct{}
|
type bproc struct{}
|
||||||
|
|
||||||
func (bproc) ValidateBlock(*types.Block) error { return nil }
|
func (bproc) ValidateBlock(*types.Block) error { return nil }
|
||||||
|
|
@ -458,6 +346,7 @@ func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.B
|
||||||
UncleHash: types.EmptyUncleHash,
|
UncleHash: types.EmptyUncleHash,
|
||||||
TxHash: types.EmptyRootHash,
|
TxHash: types.EmptyRootHash,
|
||||||
ReceiptHash: types.EmptyRootHash,
|
ReceiptHash: types.EmptyRootHash,
|
||||||
|
Time: big.NewInt(int64(i) + 1),
|
||||||
}
|
}
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
header.ParentHash = genesis.Hash()
|
header.ParentHash = genesis.Hash()
|
||||||
|
|
@ -470,28 +359,6 @@ func makeBlockChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.B
|
||||||
return chain
|
return chain
|
||||||
}
|
}
|
||||||
|
|
||||||
func chm(genesis *types.Block, db ethdb.Database) *BlockChain {
|
|
||||||
var eventMux event.TypeMux
|
|
||||||
bc := &BlockChain{
|
|
||||||
chainDb: db,
|
|
||||||
genesisBlock: genesis,
|
|
||||||
eventMux: &eventMux,
|
|
||||||
pow: FakePow{},
|
|
||||||
config: testChainConfig(),
|
|
||||||
}
|
|
||||||
valFn := func() HeaderValidator { return bc.Validator() }
|
|
||||||
bc.hc, _ = NewHeaderChain(db, testChainConfig(), valFn, bc.getProcInterrupt)
|
|
||||||
bc.bodyCache, _ = lru.New(100)
|
|
||||||
bc.bodyRLPCache, _ = lru.New(100)
|
|
||||||
bc.blockCache, _ = lru.New(100)
|
|
||||||
bc.futureBlocks, _ = lru.New(100)
|
|
||||||
bc.SetValidator(bproc{})
|
|
||||||
bc.SetProcessor(bproc{})
|
|
||||||
bc.ResetWithGenesisBlock(genesis)
|
|
||||||
|
|
||||||
return bc
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that reorganising a long difficult chain after a short easy one
|
// Tests that reorganising a long difficult chain after a short easy one
|
||||||
// overwrites the canonical numbers and links in the database.
|
// overwrites the canonical numbers and links in the database.
|
||||||
func TestReorgLongHeaders(t *testing.T) { testReorgLong(t, false) }
|
func TestReorgLongHeaders(t *testing.T) { testReorgLong(t, false) }
|
||||||
|
|
@ -511,18 +378,15 @@ func testReorgShort(t *testing.T, full bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testReorg(t *testing.T, first, second []int, td int64, full bool) {
|
func testReorg(t *testing.T, first, second []int, td int64, full bool) {
|
||||||
// Create a pristine block chain
|
bc := newTestBlockChain()
|
||||||
db, _ := ethdb.NewMemDatabase()
|
|
||||||
genesis, _ := WriteTestNetGenesisBlock(db)
|
|
||||||
bc := chm(genesis, db)
|
|
||||||
|
|
||||||
// Insert an easy and a difficult chain afterwards
|
// Insert an easy and a difficult chain afterwards
|
||||||
if full {
|
if full {
|
||||||
bc.InsertChain(makeBlockChainWithDiff(genesis, first, 11))
|
bc.InsertChain(makeBlockChainWithDiff(bc.genesisBlock, first, 11))
|
||||||
bc.InsertChain(makeBlockChainWithDiff(genesis, second, 22))
|
bc.InsertChain(makeBlockChainWithDiff(bc.genesisBlock, second, 22))
|
||||||
} else {
|
} else {
|
||||||
bc.InsertHeaderChain(makeHeaderChainWithDiff(genesis, first, 11), 1)
|
bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, first, 11), 1)
|
||||||
bc.InsertHeaderChain(makeHeaderChainWithDiff(genesis, second, 22), 1)
|
bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, second, 22), 1)
|
||||||
}
|
}
|
||||||
// Check that the chain is valid number and link wise
|
// Check that the chain is valid number and link wise
|
||||||
if full {
|
if full {
|
||||||
|
|
@ -541,7 +405,7 @@ func testReorg(t *testing.T, first, second []int, td int64, full bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Make sure the chain total difficulty is the correct one
|
// Make sure the chain total difficulty is the correct one
|
||||||
want := new(big.Int).Add(genesis.Difficulty(), big.NewInt(td))
|
want := new(big.Int).Add(bc.genesisBlock.Difficulty(), big.NewInt(td))
|
||||||
if full {
|
if full {
|
||||||
if have := bc.GetTdByHash(bc.CurrentBlock().Hash()); have.Cmp(want) != 0 {
|
if have := bc.GetTdByHash(bc.CurrentBlock().Hash()); have.Cmp(want) != 0 {
|
||||||
t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
|
t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
|
||||||
|
|
@ -558,19 +422,16 @@ func TestBadHeaderHashes(t *testing.T) { testBadHashes(t, false) }
|
||||||
func TestBadBlockHashes(t *testing.T) { testBadHashes(t, true) }
|
func TestBadBlockHashes(t *testing.T) { testBadHashes(t, true) }
|
||||||
|
|
||||||
func testBadHashes(t *testing.T, full bool) {
|
func testBadHashes(t *testing.T, full bool) {
|
||||||
// Create a pristine block chain
|
bc := newTestBlockChain()
|
||||||
db, _ := ethdb.NewMemDatabase()
|
|
||||||
genesis, _ := WriteTestNetGenesisBlock(db)
|
|
||||||
bc := chm(genesis, db)
|
|
||||||
|
|
||||||
// Create a chain, ban a hash and try to import
|
// Create a chain, ban a hash and try to import
|
||||||
var err error
|
var err error
|
||||||
if full {
|
if full {
|
||||||
blocks := makeBlockChainWithDiff(genesis, []int{1, 2, 4}, 10)
|
blocks := makeBlockChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10)
|
||||||
BadHashes[blocks[2].Header().Hash()] = true
|
BadHashes[blocks[2].Header().Hash()] = true
|
||||||
_, err = bc.InsertChain(blocks)
|
_, err = bc.InsertChain(blocks)
|
||||||
} else {
|
} else {
|
||||||
headers := makeHeaderChainWithDiff(genesis, []int{1, 2, 4}, 10)
|
headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10)
|
||||||
BadHashes[headers[2].Hash()] = true
|
BadHashes[headers[2].Hash()] = true
|
||||||
_, err = bc.InsertHeaderChain(headers, 1)
|
_, err = bc.InsertHeaderChain(headers, 1)
|
||||||
}
|
}
|
||||||
|
|
@ -585,14 +446,11 @@ func TestReorgBadHeaderHashes(t *testing.T) { testReorgBadHashes(t, false) }
|
||||||
func TestReorgBadBlockHashes(t *testing.T) { testReorgBadHashes(t, true) }
|
func TestReorgBadBlockHashes(t *testing.T) { testReorgBadHashes(t, true) }
|
||||||
|
|
||||||
func testReorgBadHashes(t *testing.T, full bool) {
|
func testReorgBadHashes(t *testing.T, full bool) {
|
||||||
// Create a pristine block chain
|
bc := newTestBlockChain()
|
||||||
db, _ := ethdb.NewMemDatabase()
|
|
||||||
genesis, _ := WriteTestNetGenesisBlock(db)
|
|
||||||
bc := chm(genesis, db)
|
|
||||||
|
|
||||||
// Create a chain, import and ban afterwards
|
// Create a chain, import and ban afterwards
|
||||||
headers := makeHeaderChainWithDiff(genesis, []int{1, 2, 3, 4}, 10)
|
headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10)
|
||||||
blocks := makeBlockChainWithDiff(genesis, []int{1, 2, 3, 4}, 10)
|
blocks := makeBlockChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10)
|
||||||
|
|
||||||
if full {
|
if full {
|
||||||
if _, err := bc.InsertChain(blocks); err != nil {
|
if _, err := bc.InsertChain(blocks); err != nil {
|
||||||
|
|
@ -613,8 +471,9 @@ func testReorgBadHashes(t *testing.T, full bool) {
|
||||||
BadHashes[headers[3].Hash()] = true
|
BadHashes[headers[3].Hash()] = true
|
||||||
defer func() { delete(BadHashes, headers[3].Hash()) }()
|
defer func() { delete(BadHashes, headers[3].Hash()) }()
|
||||||
}
|
}
|
||||||
// Create a new chain manager and check it rolled back the state
|
|
||||||
ncm, err := NewBlockChain(db, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{})
|
// Create a new BlockChain and check that it rolled back the state.
|
||||||
|
ncm, err := NewBlockChain(bc.chainDb, bc.config, pow.FakePow{}, new(event.TypeMux), vm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create new chain manager: %v", err)
|
t.Fatalf("failed to create new chain manager: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -668,7 +527,7 @@ func testInsertNonceError(t *testing.T, full bool) {
|
||||||
failHash = headers[failAt].Hash()
|
failHash = headers[failAt].Hash()
|
||||||
|
|
||||||
blockchain.pow = failPow{failNum}
|
blockchain.pow = failPow{failNum}
|
||||||
blockchain.validator = NewBlockValidator(testChainConfig(), blockchain, failPow{failNum})
|
blockchain.validator = NewBlockValidator(params.TestChainConfig, blockchain, failPow{failNum})
|
||||||
|
|
||||||
failRes, err = blockchain.InsertHeaderChain(headers, 1)
|
failRes, err = blockchain.InsertHeaderChain(headers, 1)
|
||||||
}
|
}
|
||||||
|
|
@ -710,16 +569,17 @@ func TestFastVsFullChains(t *testing.T) {
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
funds = big.NewInt(1000000000)
|
funds = big.NewInt(1000000000)
|
||||||
genesis = GenesisBlockForTesting(gendb, address, funds)
|
gspec = testGenesis(address, funds)
|
||||||
signer = types.NewEIP155Signer(big.NewInt(1))
|
genesis = gspec.MustCommit(gendb)
|
||||||
|
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||||
)
|
)
|
||||||
blocks, receipts := GenerateChain(params.TestChainConfig, genesis, gendb, 1024, func(i int, block *BlockGen) {
|
blocks, receipts := GenerateChain(gspec.Config, 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
|
||||||
if i%3 == 2 {
|
if i%3 == 2 {
|
||||||
for j := 0; j < i%4+1; j++ {
|
for j := 0; j < i%4+1; j++ {
|
||||||
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), params.TxGas, nil, nil), signer, key)
|
tx, err := types.SignTx(types.NewTransaction(block.TxNonce(address), common.Address{0x00}, big.NewInt(1000), bigTxGas, nil, nil), signer, key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -733,17 +593,17 @@ func TestFastVsFullChains(t *testing.T) {
|
||||||
})
|
})
|
||||||
// Import the chain as an archive node for the comparison baseline
|
// Import the chain as an archive node for the comparison baseline
|
||||||
archiveDb, _ := ethdb.NewMemDatabase()
|
archiveDb, _ := ethdb.NewMemDatabase()
|
||||||
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
|
gspec.MustCommit(archiveDb)
|
||||||
|
archive, _ := NewBlockChain(archiveDb, gspec.Config, pow.FakePow{}, new(event.TypeMux), vm.Config{})
|
||||||
archive, _ := NewBlockChain(archiveDb, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{})
|
|
||||||
|
|
||||||
if n, err := archive.InsertChain(blocks); err != nil {
|
if n, err := archive.InsertChain(blocks); err != nil {
|
||||||
t.Fatalf("failed to process block %d: %v", n, err)
|
t.Fatalf("failed to process block %d: %v", n, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fast import the chain as a non-archive node to test
|
// Fast import the chain as a non-archive node to test
|
||||||
fastDb, _ := ethdb.NewMemDatabase()
|
fastDb, _ := ethdb.NewMemDatabase()
|
||||||
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
|
gspec.MustCommit(fastDb)
|
||||||
fast, _ := NewBlockChain(fastDb, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{})
|
fast, _ := NewBlockChain(fastDb, gspec.Config, pow.FakePow{}, new(event.TypeMux), vm.Config{})
|
||||||
|
|
||||||
headers := make([]*types.Header, len(blocks))
|
headers := make([]*types.Header, len(blocks))
|
||||||
for i, block := range blocks {
|
for i, block := range blocks {
|
||||||
|
|
@ -793,10 +653,11 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
funds = big.NewInt(1000000000)
|
funds = big.NewInt(1000000000)
|
||||||
genesis = GenesisBlockForTesting(gendb, address, funds)
|
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}}
|
||||||
|
genesis = gspec.MustCommit(gendb)
|
||||||
)
|
)
|
||||||
height := uint64(1024)
|
height := uint64(1024)
|
||||||
blocks, receipts := GenerateChain(params.TestChainConfig, genesis, gendb, int(height), nil)
|
blocks, receipts := GenerateChain(gspec.Config, genesis, gendb, int(height), nil)
|
||||||
|
|
||||||
// Configure a subchain to roll back
|
// Configure a subchain to roll back
|
||||||
remove := []common.Hash{}
|
remove := []common.Hash{}
|
||||||
|
|
@ -817,9 +678,9 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||||
}
|
}
|
||||||
// Import the chain as an archive node and ensure all pointers are updated
|
// Import the chain as an archive node and ensure all pointers are updated
|
||||||
archiveDb, _ := ethdb.NewMemDatabase()
|
archiveDb, _ := ethdb.NewMemDatabase()
|
||||||
WriteGenesisBlockForTesting(archiveDb, GenesisAccount{address, funds})
|
gspec.MustCommit(archiveDb)
|
||||||
|
|
||||||
archive, _ := NewBlockChain(archiveDb, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{})
|
archive, _ := NewBlockChain(archiveDb, gspec.Config, pow.FakePow{}, new(event.TypeMux), vm.Config{})
|
||||||
|
|
||||||
if n, err := archive.InsertChain(blocks); err != nil {
|
if n, err := archive.InsertChain(blocks); err != nil {
|
||||||
t.Fatalf("failed to process block %d: %v", n, err)
|
t.Fatalf("failed to process block %d: %v", n, err)
|
||||||
|
|
@ -830,8 +691,8 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||||
|
|
||||||
// Import the chain as a non-archive node and ensure all pointers are updated
|
// Import the chain as a non-archive node and ensure all pointers are updated
|
||||||
fastDb, _ := ethdb.NewMemDatabase()
|
fastDb, _ := ethdb.NewMemDatabase()
|
||||||
WriteGenesisBlockForTesting(fastDb, GenesisAccount{address, funds})
|
gspec.MustCommit(fastDb)
|
||||||
fast, _ := NewBlockChain(fastDb, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{})
|
fast, _ := NewBlockChain(fastDb, gspec.Config, pow.FakePow{}, new(event.TypeMux), vm.Config{})
|
||||||
|
|
||||||
headers := make([]*types.Header, len(blocks))
|
headers := make([]*types.Header, len(blocks))
|
||||||
for i, block := range blocks {
|
for i, block := range blocks {
|
||||||
|
|
@ -849,8 +710,8 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||||
|
|
||||||
// Import the chain as a light node and ensure all pointers are updated
|
// Import the chain as a light node and ensure all pointers are updated
|
||||||
lightDb, _ := ethdb.NewMemDatabase()
|
lightDb, _ := ethdb.NewMemDatabase()
|
||||||
WriteGenesisBlockForTesting(lightDb, GenesisAccount{address, funds})
|
gspec.MustCommit(lightDb)
|
||||||
light, _ := NewBlockChain(lightDb, testChainConfig(), FakePow{}, new(event.TypeMux), vm.Config{})
|
light, _ := NewBlockChain(lightDb, gspec.Config, pow.FakePow{}, new(event.TypeMux), vm.Config{})
|
||||||
|
|
||||||
if n, err := light.InsertHeaderChain(headers, 1); err != nil {
|
if n, err := light.InsertHeaderChain(headers, 1); err != nil {
|
||||||
t.Fatalf("failed to insert header %d: %v", n, err)
|
t.Fatalf("failed to insert header %d: %v", n, err)
|
||||||
|
|
@ -862,9 +723,6 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||||
|
|
||||||
// Tests that chain reorganisations handle transaction removals and reinsertions.
|
// Tests that chain reorganisations handle transaction removals and reinsertions.
|
||||||
func TestChainTxReorgs(t *testing.T) {
|
func TestChainTxReorgs(t *testing.T) {
|
||||||
params.MinGasLimit = big.NewInt(125000) // Minimum the gas limit may ever be.
|
|
||||||
params.GenesisGasLimit = big.NewInt(3141592) // Gas limit of the Genesis block.
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||||
|
|
@ -873,18 +731,24 @@ func TestChainTxReorgs(t *testing.T) {
|
||||||
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||||
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
|
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
|
||||||
db, _ = ethdb.NewMemDatabase()
|
db, _ = ethdb.NewMemDatabase()
|
||||||
signer = types.NewEIP155Signer(big.NewInt(1))
|
gspec = &Genesis{
|
||||||
)
|
Config: params.TestChainConfig,
|
||||||
genesis := WriteGenesisBlockForTesting(db,
|
GasLimit: 3141592,
|
||||||
GenesisAccount{addr1, big.NewInt(1000000)},
|
Alloc: GenesisAlloc{
|
||||||
GenesisAccount{addr2, big.NewInt(1000000)},
|
addr1: {Balance: big.NewInt(1000000)},
|
||||||
GenesisAccount{addr3, big.NewInt(1000000)},
|
addr2: {Balance: big.NewInt(1000000)},
|
||||||
|
addr3: {Balance: big.NewInt(1000000)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
genesis = gspec.MustCommit(db)
|
||||||
|
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create two transactions shared between the chains:
|
// Create two transactions shared between the chains:
|
||||||
// - postponed: transaction included at a later block in the forked chain
|
// - postponed: transaction included at a later block in the forked chain
|
||||||
// - swapped: transaction included at the same block number in the forked chain
|
// - swapped: transaction included at the same block number in the forked chain
|
||||||
postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
|
postponed, _ := types.SignTx(types.NewTransaction(0, addr1, big.NewInt(1000), bigTxGas, nil, nil), signer, key1)
|
||||||
swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
|
swapped, _ := types.SignTx(types.NewTransaction(1, addr1, big.NewInt(1000), bigTxGas, nil, nil), signer, key1)
|
||||||
|
|
||||||
// Create two transactions that will be dropped by the forked chain:
|
// Create two transactions that will be dropped by the forked chain:
|
||||||
// - pastDrop: transaction dropped retroactively from a past block
|
// - pastDrop: transaction dropped retroactively from a past block
|
||||||
|
|
@ -897,16 +761,16 @@ 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(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {
|
chain, _ := GenerateChain(gspec.Config, genesis, db, 3, func(i int, gen *BlockGen) {
|
||||||
switch i {
|
switch i {
|
||||||
case 0:
|
case 0:
|
||||||
pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
|
pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key2)
|
||||||
|
|
||||||
gen.AddTx(pastDrop) // This transaction will be dropped in the fork from below the split point
|
gen.AddTx(pastDrop) // This transaction will be dropped in the fork from below the split point
|
||||||
gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork
|
gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
|
freshDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key2)
|
||||||
|
|
||||||
gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point
|
gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point
|
||||||
gen.AddTx(swapped) // This transaction will be swapped out at the exact height
|
gen.AddTx(swapped) // This transaction will be swapped out at the exact height
|
||||||
|
|
@ -916,27 +780,27 @@ func TestChainTxReorgs(t *testing.T) {
|
||||||
})
|
})
|
||||||
// Import the chain. This runs all block validation rules.
|
// Import the chain. This runs all block validation rules.
|
||||||
evmux := &event.TypeMux{}
|
evmux := &event.TypeMux{}
|
||||||
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux, vm.Config{})
|
blockchain, _ := NewBlockChain(db, gspec.Config, pow.FakePow{}, evmux, vm.Config{})
|
||||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||||
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
|
t.Fatalf("failed to insert original chain[%d]: %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// overwrite the old chain
|
// overwrite the old chain
|
||||||
chain, _ = GenerateChain(params.TestChainConfig, genesis, db, 5, func(i int, gen *BlockGen) {
|
chain, _ = GenerateChain(gspec.Config, genesis, db, 5, func(i int, gen *BlockGen) {
|
||||||
switch i {
|
switch i {
|
||||||
case 0:
|
case 0:
|
||||||
pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
|
pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3)
|
||||||
gen.AddTx(pastAdd) // This transaction needs to be injected during reorg
|
gen.AddTx(pastAdd) // This transaction needs to be injected during reorg
|
||||||
|
|
||||||
case 2:
|
case 2:
|
||||||
gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain
|
gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain
|
||||||
gen.AddTx(swapped) // This transaction was swapped from the exact current spot in the original chain
|
gen.AddTx(swapped) // This transaction was swapped from the exact current spot in the original chain
|
||||||
|
|
||||||
freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
|
freshAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3)
|
||||||
gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time
|
gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time
|
||||||
|
|
||||||
case 3:
|
case 3:
|
||||||
futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key3)
|
futureAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3)
|
||||||
gen.AddTx(futureAdd) // This transaction will be added after a full reorg
|
gen.AddTx(futureAdd) // This transaction will be added after a full reorg
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -974,8 +838,6 @@ func TestChainTxReorgs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLogReorgs(t *testing.T) {
|
func TestLogReorgs(t *testing.T) {
|
||||||
params.MinGasLimit = big.NewInt(125000) // Minimum the gas limit may ever be.
|
|
||||||
params.GenesisGasLimit = big.NewInt(3141592) // Gas limit of the Genesis block.
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
|
@ -983,14 +845,13 @@ func TestLogReorgs(t *testing.T) {
|
||||||
db, _ = ethdb.NewMemDatabase()
|
db, _ = ethdb.NewMemDatabase()
|
||||||
// this code generates a log
|
// this code generates a log
|
||||||
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
||||||
signer = types.NewEIP155Signer(big.NewInt(1))
|
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
||||||
)
|
genesis = gspec.MustCommit(db)
|
||||||
genesis := WriteGenesisBlockForTesting(db,
|
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||||
GenesisAccount{addr1, big.NewInt(10000000000000)},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
evmux := &event.TypeMux{}
|
var evmux event.TypeMux
|
||||||
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux, vm.Config{})
|
blockchain, _ := NewBlockChain(db, gspec.Config, pow.FakePow{}, &evmux, vm.Config{})
|
||||||
|
|
||||||
subs := evmux.Subscribe(RemovedLogsEvent{})
|
subs := evmux.Subscribe(RemovedLogsEvent{})
|
||||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 2, func(i int, gen *BlockGen) {
|
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 2, func(i int, gen *BlockGen) {
|
||||||
|
|
@ -1022,19 +883,20 @@ func TestReorgSideEvent(t *testing.T) {
|
||||||
db, _ = ethdb.NewMemDatabase()
|
db, _ = ethdb.NewMemDatabase()
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
genesis = WriteGenesisBlockForTesting(db, GenesisAccount{addr1, big.NewInt(10000000000000)})
|
gspec = testGenesis(addr1, big.NewInt(10000000000000))
|
||||||
signer = types.NewEIP155Signer(big.NewInt(1))
|
genesis = gspec.MustCommit(db)
|
||||||
|
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||||
)
|
)
|
||||||
|
|
||||||
evmux := &event.TypeMux{}
|
evmux := &event.TypeMux{}
|
||||||
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux, vm.Config{})
|
blockchain, _ := NewBlockChain(db, gspec.Config, pow.FakePow{}, evmux, vm.Config{})
|
||||||
|
|
||||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {})
|
chain, _ := GenerateChain(gspec.Config, 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(params.TestChainConfig, genesis, db, 4, func(i int, gen *BlockGen) {
|
replacementBlocks, _ := GenerateChain(gspec.Config, genesis, db, 4, func(i int, gen *BlockGen) {
|
||||||
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), nil), signer, key1)
|
tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), nil), signer, key1)
|
||||||
if i == 2 {
|
if i == 2 {
|
||||||
gen.OffsetTime(-1)
|
gen.OffsetTime(-1)
|
||||||
|
|
@ -1097,28 +959,21 @@ done:
|
||||||
|
|
||||||
// Tests if the canonical block can be fetched from the database during chain insertion.
|
// Tests if the canonical block can be fetched from the database during chain insertion.
|
||||||
func TestCanonicalBlockRetrieval(t *testing.T) {
|
func TestCanonicalBlockRetrieval(t *testing.T) {
|
||||||
var (
|
bc := newTestBlockChain()
|
||||||
db, _ = ethdb.NewMemDatabase()
|
chain, _ := GenerateChain(bc.config, bc.genesisBlock, bc.chainDb, 10, func(i int, gen *BlockGen) {})
|
||||||
genesis = WriteGenesisBlockForTesting(db)
|
|
||||||
)
|
|
||||||
|
|
||||||
evmux := &event.TypeMux{}
|
|
||||||
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux, vm.Config{})
|
|
||||||
|
|
||||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *BlockGen) {})
|
|
||||||
|
|
||||||
for i := range chain {
|
for i := range chain {
|
||||||
go func(block *types.Block) {
|
go func(block *types.Block) {
|
||||||
// try to retrieve a block by its canonical hash and see if the block data can be retrieved.
|
// try to retrieve a block by its canonical hash and see if the block data can be retrieved.
|
||||||
for {
|
for {
|
||||||
ch := GetCanonicalHash(db, block.NumberU64())
|
ch := GetCanonicalHash(bc.chainDb, block.NumberU64())
|
||||||
if ch == (common.Hash{}) {
|
if ch == (common.Hash{}) {
|
||||||
continue // busy wait for canonical hash to be written
|
continue // busy wait for canonical hash to be written
|
||||||
}
|
}
|
||||||
if ch != block.Hash() {
|
if ch != block.Hash() {
|
||||||
t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex())
|
t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex())
|
||||||
}
|
}
|
||||||
fb := GetBlock(db, ch, block.NumberU64())
|
fb := GetBlock(bc.chainDb, ch, block.NumberU64())
|
||||||
if fb == nil {
|
if fb == nil {
|
||||||
t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
|
t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
|
||||||
}
|
}
|
||||||
|
|
@ -1129,7 +984,7 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
|
||||||
}
|
}
|
||||||
}(chain[i])
|
}(chain[i])
|
||||||
|
|
||||||
blockchain.InsertChain(types.Blocks{chain[i]})
|
bc.InsertChain(types.Blocks{chain[i]})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1141,13 +996,16 @@ func TestEIP155Transition(t *testing.T) {
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
funds = big.NewInt(1000000000)
|
funds = big.NewInt(1000000000)
|
||||||
deleteAddr = common.Address{1}
|
deleteAddr = common.Address{1}
|
||||||
genesis = WriteGenesisBlockForTesting(db, GenesisAccount{address, funds}, GenesisAccount{deleteAddr, new(big.Int)})
|
gspec = &Genesis{
|
||||||
config = ¶ms.ChainConfig{ChainId: big.NewInt(1), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
|
Config: ¶ms.ChainConfig{ChainId: big.NewInt(1), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)},
|
||||||
|
Alloc: GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: new(big.Int)}},
|
||||||
|
}
|
||||||
|
genesis = gspec.MustCommit(db)
|
||||||
mux event.TypeMux
|
mux event.TypeMux
|
||||||
)
|
)
|
||||||
|
|
||||||
blockchain, _ := NewBlockChain(db, config, FakePow{}, &mux, vm.Config{})
|
blockchain, _ := NewBlockChain(db, gspec.Config, pow.FakePow{}, &mux, vm.Config{})
|
||||||
blocks, _ := GenerateChain(config, genesis, db, 4, func(i int, block *BlockGen) {
|
blocks, _ := GenerateChain(gspec.Config, genesis, db, 4, func(i int, block *BlockGen) {
|
||||||
var (
|
var (
|
||||||
tx *types.Transaction
|
tx *types.Transaction
|
||||||
err error
|
err error
|
||||||
|
|
@ -1169,7 +1027,7 @@ func TestEIP155Transition(t *testing.T) {
|
||||||
}
|
}
|
||||||
block.AddTx(tx)
|
block.AddTx(tx)
|
||||||
|
|
||||||
tx, err = basicTx(types.NewEIP155Signer(config.ChainId))
|
tx, err = basicTx(types.NewEIP155Signer(gspec.Config.ChainId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -1181,7 +1039,7 @@ func TestEIP155Transition(t *testing.T) {
|
||||||
}
|
}
|
||||||
block.AddTx(tx)
|
block.AddTx(tx)
|
||||||
|
|
||||||
tx, err = basicTx(types.NewEIP155Signer(config.ChainId))
|
tx, err = basicTx(types.NewEIP155Signer(gspec.Config.ChainId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -1209,7 +1067,7 @@ func TestEIP155Transition(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate an invalid chain id transaction
|
// generate an invalid chain id transaction
|
||||||
config = ¶ms.ChainConfig{ChainId: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
|
config := ¶ms.ChainConfig{ChainId: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
|
||||||
blocks, _ = GenerateChain(config, blocks[len(blocks)-1], db, 4, func(i int, block *BlockGen) {
|
blocks, _ = GenerateChain(config, blocks[len(blocks)-1], db, 4, func(i int, block *BlockGen) {
|
||||||
var (
|
var (
|
||||||
tx *types.Transaction
|
tx *types.Transaction
|
||||||
|
|
@ -1241,22 +1099,24 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
||||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||||
funds = big.NewInt(1000000000)
|
funds = big.NewInt(1000000000)
|
||||||
theAddr = common.Address{1}
|
theAddr = common.Address{1}
|
||||||
genesis = WriteGenesisBlockForTesting(db, GenesisAccount{address, funds})
|
gspec = &Genesis{
|
||||||
config = ¶ms.ChainConfig{
|
Config: ¶ms.ChainConfig{
|
||||||
ChainId: big.NewInt(1),
|
ChainId: big.NewInt(1),
|
||||||
HomesteadBlock: new(big.Int),
|
HomesteadBlock: new(big.Int),
|
||||||
EIP155Block: new(big.Int),
|
EIP155Block: new(big.Int),
|
||||||
EIP158Block: big.NewInt(2),
|
EIP158Block: big.NewInt(2),
|
||||||
|
},
|
||||||
|
Alloc: GenesisAlloc{address: {Balance: funds}},
|
||||||
}
|
}
|
||||||
|
genesis = gspec.MustCommit(db)
|
||||||
mux event.TypeMux
|
mux event.TypeMux
|
||||||
|
blockchain, _ = NewBlockChain(db, gspec.Config, pow.FakePow{}, &mux, vm.Config{})
|
||||||
blockchain, _ = NewBlockChain(db, config, FakePow{}, &mux, vm.Config{})
|
|
||||||
)
|
)
|
||||||
blocks, _ := GenerateChain(config, genesis, db, 3, func(i int, block *BlockGen) {
|
blocks, _ := GenerateChain(gspec.Config, genesis, db, 3, func(i int, block *BlockGen) {
|
||||||
var (
|
var (
|
||||||
tx *types.Transaction
|
tx *types.Transaction
|
||||||
err error
|
err error
|
||||||
signer = types.NewEIP155Signer(config.ChainId)
|
signer = types.NewEIP155Signer(gspec.Config.ChainId)
|
||||||
)
|
)
|
||||||
switch i {
|
switch i {
|
||||||
case 0:
|
case 0:
|
||||||
|
|
@ -1284,7 +1144,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if blockchain.stateCache.Exist(theAddr) {
|
if blockchain.stateCache.Exist(theAddr) {
|
||||||
t.Error("account should not expect")
|
t.Error("account should not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
// account musn't be created post eip 161
|
// account musn't be created post eip 161
|
||||||
|
|
@ -1292,6 +1152,6 @@ func TestEIP161AccountRemoval(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if blockchain.stateCache.Exist(theAddr) {
|
if blockchain.stateCache.Exist(theAddr) {
|
||||||
t.Error("account should not expect")
|
t.Error("account should not exist")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,30 +30,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/pow"
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO: move this to another package.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// MakeChainConfig returns a new ChainConfig with the ethereum default chain settings.
|
|
||||||
func MakeChainConfig() *params.ChainConfig {
|
|
||||||
return ¶ms.ChainConfig{
|
|
||||||
HomesteadBlock: big.NewInt(0),
|
|
||||||
DAOForkBlock: nil,
|
|
||||||
DAOForkSupport: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FakePow is a non-validating proof of work implementation.
|
|
||||||
// It returns true from Verify for any block.
|
|
||||||
type FakePow struct{}
|
|
||||||
|
|
||||||
func (f FakePow) Search(block pow.Block, stop <-chan struct{}, index int) (uint64, []byte) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
func (f FakePow) Verify(block pow.Block) bool { return true }
|
|
||||||
func (f FakePow) GetHashrate() int64 { return 0 }
|
|
||||||
func (f FakePow) Turbo(bool) {}
|
|
||||||
|
|
||||||
// So we can deterministically seed different blockchains
|
// So we can deterministically seed different blockchains
|
||||||
var (
|
var (
|
||||||
canonicalSeed = 1
|
canonicalSeed = 1
|
||||||
|
|
@ -165,7 +141,7 @@ func (b *BlockGen) OffsetTime(seconds int64) {
|
||||||
if b.header.Time.Cmp(b.parent.Header().Time) <= 0 {
|
if b.header.Time.Cmp(b.parent.Header().Time) <= 0 {
|
||||||
panic("block time out of range")
|
panic("block time out of range")
|
||||||
}
|
}
|
||||||
b.header.Difficulty = CalcDifficulty(MakeChainConfig(), b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty())
|
b.header.Difficulty = CalcDifficulty(b.config, b.header.Time.Uint64(), b.parent.Time().Uint64(), b.parent.Number(), b.parent.Difficulty())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenerateChain creates a chain of n blocks. The first block's
|
// GenerateChain creates a chain of n blocks. The first block's
|
||||||
|
|
@ -181,14 +157,13 @@ func (b *BlockGen) OffsetTime(seconds int64) {
|
||||||
// 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(config *params.ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
|
func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
|
||||||
|
if config == nil {
|
||||||
|
config = params.TestChainConfig
|
||||||
|
}
|
||||||
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, config: config}
|
b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb, config: config}
|
||||||
|
|
||||||
// Mutate the state and block according to any hard-fork specs
|
// Mutate the state and block according to any hard-fork specs
|
||||||
if config == nil {
|
|
||||||
config = MakeChainConfig()
|
|
||||||
}
|
|
||||||
if daoBlock := config.DAOForkBlock; daoBlock != nil {
|
if daoBlock := config.DAOForkBlock; daoBlock != nil {
|
||||||
limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange)
|
limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange)
|
||||||
if h.Number.Cmp(daoBlock) >= 0 && h.Number.Cmp(limit) < 0 {
|
if h.Number.Cmp(daoBlock) >= 0 && h.Number.Cmp(limit) < 0 {
|
||||||
|
|
@ -237,7 +212,7 @@ func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.St
|
||||||
Root: state.IntermediateRoot(config.IsEIP158(parent.Number())),
|
Root: state.IntermediateRoot(config.IsEIP158(parent.Number())),
|
||||||
ParentHash: parent.Hash(),
|
ParentHash: parent.Hash(),
|
||||||
Coinbase: parent.Coinbase(),
|
Coinbase: parent.Coinbase(),
|
||||||
Difficulty: CalcDifficulty(MakeChainConfig(), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()),
|
Difficulty: CalcDifficulty(config, time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()),
|
||||||
GasLimit: CalcGasLimit(parent),
|
GasLimit: CalcGasLimit(parent),
|
||||||
GasUsed: new(big.Int),
|
GasUsed: new(big.Int),
|
||||||
Number: new(big.Int).Add(parent.Number(), common.Big1),
|
Number: new(big.Int).Add(parent.Number(), common.Big1),
|
||||||
|
|
@ -249,14 +224,12 @@ func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.St
|
||||||
// chain. Depending on the full flag, if creates either a full block chain or a
|
// chain. Depending on the full flag, if creates either a full block chain or a
|
||||||
// header only chain.
|
// header only chain.
|
||||||
func newCanonical(n int, full bool) (ethdb.Database, *BlockChain, error) {
|
func newCanonical(n int, full bool) (ethdb.Database, *BlockChain, error) {
|
||||||
// Create the new chain database
|
|
||||||
db, _ := ethdb.NewMemDatabase()
|
|
||||||
evmux := &event.TypeMux{}
|
|
||||||
|
|
||||||
// Initialize a fresh chain with only a genesis block
|
// Initialize a fresh chain with only a genesis block
|
||||||
genesis, _ := WriteTestNetGenesisBlock(db)
|
gspec := new(Genesis)
|
||||||
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
genesis := gspec.MustCommit(db)
|
||||||
|
|
||||||
blockchain, _ := NewBlockChain(db, MakeChainConfig(), FakePow{}, evmux, vm.Config{})
|
blockchain, _ := NewBlockChain(db, params.AllProtocolChanges, pow.FakePow{}, new(event.TypeMux), vm.Config{})
|
||||||
// Create and inject the requested chain
|
// Create and inject the requested chain
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return db, blockchain, nil
|
return db, blockchain, nil
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,10 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/pow"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExampleGenerateChain() {
|
func ExampleGenerateChain() {
|
||||||
params.MinGasLimit = big.NewInt(125000) // Minimum the gas limit may ever be.
|
|
||||||
params.GenesisGasLimit = big.NewInt(3141592) // Gas limit of the Genesis block.
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||||
|
|
@ -40,29 +38,30 @@ func ExampleGenerateChain() {
|
||||||
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
addr2 = crypto.PubkeyToAddress(key2.PublicKey)
|
||||||
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
|
addr3 = crypto.PubkeyToAddress(key3.PublicKey)
|
||||||
db, _ = ethdb.NewMemDatabase()
|
db, _ = ethdb.NewMemDatabase()
|
||||||
signer = types.HomesteadSigner{}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
chainConfig := ¶ms.ChainConfig{
|
|
||||||
HomesteadBlock: new(big.Int),
|
|
||||||
}
|
|
||||||
// Ensure that key1 has some funds in the genesis block.
|
// Ensure that key1 has some funds in the genesis block.
|
||||||
genesis := WriteGenesisBlockForTesting(db, GenesisAccount{addr1, big.NewInt(1000000)})
|
gspec := &Genesis{
|
||||||
|
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
|
||||||
|
Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}},
|
||||||
|
}
|
||||||
|
genesis := gspec.MustCommit(db)
|
||||||
|
|
||||||
// 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(chainConfig, genesis, db, 5, func(i int, gen *BlockGen) {
|
signer := types.HomesteadSigner{}
|
||||||
|
chain, _ := GenerateChain(gspec.Config, genesis, db, 5, func(i int, gen *BlockGen) {
|
||||||
switch i {
|
switch i {
|
||||||
case 0:
|
case 0:
|
||||||
// In block 1, addr1 sends addr2 some ether.
|
// In block 1, addr1 sends addr2 some ether.
|
||||||
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil), signer, key1)
|
tx, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), bigTxGas, nil, nil), signer, key1)
|
||||||
gen.AddTx(tx)
|
gen.AddTx(tx)
|
||||||
case 1:
|
case 1:
|
||||||
// In block 2, addr1 sends some more ether to addr2.
|
// In block 2, addr1 sends some more ether to addr2.
|
||||||
// addr2 passes it on to addr3.
|
// addr2 passes it on to addr3.
|
||||||
tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil), signer, key1)
|
tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key1)
|
||||||
tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil), signer, key2)
|
tx2, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key2)
|
||||||
gen.AddTx(tx1)
|
gen.AddTx(tx1)
|
||||||
gen.AddTx(tx2)
|
gen.AddTx(tx2)
|
||||||
case 2:
|
case 2:
|
||||||
|
|
@ -82,7 +81,7 @@ func ExampleGenerateChain() {
|
||||||
|
|
||||||
// Import the chain. This runs all block validation rules.
|
// Import the chain. This runs all block validation rules.
|
||||||
evmux := &event.TypeMux{}
|
evmux := &event.TypeMux{}
|
||||||
blockchain, _ := NewBlockChain(db, chainConfig, FakePow{}, evmux, vm.Config{})
|
blockchain, _ := NewBlockChain(db, gspec.Config, pow.FakePow{}, evmux, vm.Config{})
|
||||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||||
fmt.Printf("insert error (block %d): %v\n", chain[i].NumberU64(), err)
|
fmt.Printf("insert error (block %d): %v\n", chain[i].NumberU64(), err)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ func verifyNonces(checker pow.PoW, items []pow.Block) (chan<- struct{}, <-chan n
|
||||||
for i := 0; i < workers; i++ {
|
for i := 0; i < workers; i++ {
|
||||||
go func() {
|
go func() {
|
||||||
for index := range tasks {
|
for index := range tasks {
|
||||||
results <- nonceCheckResult{index: index, valid: checker.Verify(items[index])}
|
results <- nonceCheckResult{index: index, valid: checker.Verify(items[index]) == nil}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue