Merge remote-tracking branch 'refs/remotes/ethereum/master' into rebase-1.5.9

This commit is contained in:
Christopher Franko 2017-03-26 11:40:56 -04:00
commit 5822644626
435 changed files with 22107 additions and 7895 deletions

View file

@ -5,29 +5,38 @@ matrix:
include: include:
- os: linux - os: linux
dist: trusty dist: trusty
go: 1.5.4 sudo: required
env:
- GO15VENDOREXPERIMENT=1
- os: linux
dist: trusty
go: 1.6.2
- os: linux
dist: trusty
go: 1.7.5 go: 1.7.5
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, only run go vet and misspell on these # These are the latest Go versions.
- os: linux - os: linux
dist: trusty dist: trusty
sudo: required
go: 1.8 go: 1.8
script: 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 install
- go run build/ci.go test -coverage -vet -misspell - go run build/ci.go test -coverage -misspell
- os: osx - os: osx
go: 1.8 go: 1.8
sudo: required
script: script:
- brew update
- brew install caskroom/cask/brew-cask
- brew cask install osxfuse
- 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 -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
@ -66,6 +75,32 @@ matrix:
# - 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
# 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 # This builder is a temporary fallback for building ARM64 while Go 1.8 is fixed
- os: linux - os: linux
dist: trusty dist: trusty
@ -131,7 +166,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

View file

@ -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)
gexp-cross: gexp-linux gexp-darwin gexp-windows gexp-android gexp-ios gexp-cross: gexp-linux gexp-darwin gexp-windows gexp-android gexp-ios
@ -51,12 +60,12 @@ gexp-linux: gexp-linux-386 gexp-linux-amd64 gexp-linux-arm gexp-linux-mips64 gex
@ls -ld $(GOBIN)/gexp-linux-* @ls -ld $(GOBIN)/gexp-linux-*
gexp-linux-386: gexp-linux-386:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/386 -v ./cmd/gexp build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/386 -v ./cmd/gexp
@echo "Linux 386 cross compilation done:" @echo "Linux 386 cross compilation done:"
@ls -ld $(GOBIN)/gexp-linux-* | grep 386 @ls -ld $(GOBIN)/gexp-linux-* | grep 386
gexp-linux-amd64: gexp-linux-amd64:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/amd64 -v ./cmd/gexp build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/amd64 -v ./cmd/gexp
@echo "Linux amd64 cross compilation done:" @echo "Linux amd64 cross compilation done:"
@ls -ld $(GOBIN)/gexp-linux-* | grep amd64 @ls -ld $(GOBIN)/gexp-linux-* | grep amd64
@ -65,32 +74,42 @@ gexp-linux-arm: gexp-linux-arm-5 gexp-linux-arm-6 gexp-linux-arm-7 gexp-linux-ar
@ls -ld $(GOBIN)/gexp-linux-* | grep arm @ls -ld $(GOBIN)/gexp-linux-* | grep arm
gexp-linux-arm-5: gexp-linux-arm-5:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-5 -v ./cmd/gexp build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/arm-5 -v ./cmd/gexp
@echo "Linux ARMv5 cross compilation done:" @echo "Linux ARMv5 cross compilation done:"
@ls -ld $(GOBIN)/gexp-linux-* | grep arm-5 @ls -ld $(GOBIN)/gexp-linux-* | grep arm-5
gexp-linux-arm-6: gexp-linux-arm-6:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-6 -v ./cmd/gexp build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/arm-6 -v ./cmd/gexp
@echo "Linux ARMv6 cross compilation done:" @echo "Linux ARMv6 cross compilation done:"
@ls -ld $(GOBIN)/gexp-linux-* | grep arm-6 @ls -ld $(GOBIN)/gexp-linux-* | grep arm-6
gexp-linux-arm-7: gexp-linux-arm-7:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-7 -v ./cmd/gexp build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/arm-7 -v ./cmd/gexp
@echo "Linux ARMv7 cross compilation done:" @echo "Linux ARMv7 cross compilation done:"
@ls -ld $(GOBIN)/gexp-linux-* | grep arm-7 @ls -ld $(GOBIN)/gexp-linux-* | grep arm-7
gexp-linux-arm64: gexp-linux-arm64:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm64 -v ./cmd/gexp build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/arm64 -v ./cmd/gexp
@echo "Linux ARM64 cross compilation done:" @echo "Linux ARM64 cross compilation done:"
@ls -ld $(GOBIN)/gexp-linux-* | grep arm64 @ls -ld $(GOBIN)/gexp-linux-* | grep arm64
gexp-linux-mips:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/mips --ldflags '-extldflags "-static"' -v ./cmd/gexp
@echo "Linux MIPS cross compilation done:"
@ls -ld $(GOBIN)/gexp-linux-* | grep mips
gexp-linux-mipsle:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/mipsle --ldflags '-extldflags "-static"' -v ./cmd/gexp
@echo "Linux MIPSle cross compilation done:"
@ls -ld $(GOBIN)/gexp-linux-* | grep mipsle
gexp-linux-mips64: gexp-linux-mips64:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64 -v ./cmd/gexp build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/mips64 --ldflags '-extldflags "-static"' -v ./cmd/gexp
@echo "Linux MIPS64 cross compilation done:" @echo "Linux MIPS64 cross compilation done:"
@ls -ld $(GOBIN)/gexp-linux-* | grep mips64 @ls -ld $(GOBIN)/gexp-linux-* | grep mips64
gexp-linux-mips64le: gexp-linux-mips64le:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64le -v ./cmd/gexp build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/mips64le --ldflags '-extldflags "-static"' -v ./cmd/gexp
@echo "Linux MIPS64le cross compilation done:" @echo "Linux MIPS64le cross compilation done:"
@ls -ld $(GOBIN)/gexp-linux-* | grep mips64le @ls -ld $(GOBIN)/gexp-linux-* | grep mips64le
@ -99,12 +118,12 @@ gexp-darwin: gexp-darwin-386 gexp-darwin-amd64
@ls -ld $(GOBIN)/gexp-darwin-* @ls -ld $(GOBIN)/gexp-darwin-*
gexp-darwin-386: gexp-darwin-386:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=darwin/386 -v ./cmd/gexp build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=darwin/386 -v ./cmd/gexp
@echo "Darwin 386 cross compilation done:" @echo "Darwin 386 cross compilation done:"
@ls -ld $(GOBIN)/gexp-darwin-* | grep 386 @ls -ld $(GOBIN)/gexp-darwin-* | grep 386
gexp-darwin-amd64: gexp-darwin-amd64:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=darwin/amd64 -v ./cmd/gexp build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=darwin/amd64 -v ./cmd/gexp
@echo "Darwin amd64 cross compilation done:" @echo "Darwin amd64 cross compilation done:"
@ls -ld $(GOBIN)/gexp-darwin-* | grep amd64 @ls -ld $(GOBIN)/gexp-darwin-* | grep amd64
@ -113,11 +132,11 @@ gexp-windows: gexp-windows-386 gexp-windows-amd64
@ls -ld $(GOBIN)/gexp-windows-* @ls -ld $(GOBIN)/gexp-windows-*
gexp-windows-386: gexp-windows-386:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=windows/386 -v ./cmd/gexp build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=windows/386 -v ./cmd/gexp
@echo "Windows 386 cross compilation done:" @echo "Windows 386 cross compilation done:"
@ls -ld $(GOBIN)/gexp-windows-* | grep 386 @ls -ld $(GOBIN)/gexp-windows-* | grep 386
gexp-windows-amd64: gexp-windows-amd64:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=windows/amd64 -v ./cmd/gexp build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=windows/amd64 -v ./cmd/gexp
@echo "Windows amd64 cross compilation done:" @echo "Windows amd64 cross compilation done:"
@ls -ld $(GOBIN)/gexp-windows-* | grep amd64 @ls -ld $(GOBIN)/gexp-windows-* | grep amd64

View file

@ -16,7 +16,7 @@ For prerequisites and detailed build instructions please read the
[Installation Instructions](https://github.com/expanse-org/go-expanse/wiki/Building-Expanse) [Installation Instructions](https://github.com/expanse-org/go-expanse/wiki/Building-Expanse)
on the wiki. on the wiki.
Building gexp requires both a Go and a C compiler. Building gexp 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
@ -112,7 +112,7 @@ docker run -d --name expanse-node -v /Users/alice/expanse:/root \
This will start gexp in fast sync mode with a DB memory allowance of 512MB just as the above command does. It will also create a persistent volume in your home directory for saving your blockchain as well as map the default ports. There is also an `alpine` tag available for a slim version of the image. This will start gexp in fast sync mode with a DB memory allowance of 512MB just as the above command does. It will also create a persistent volume in your home directory for saving your blockchain as well as map the default ports. There is also an `alpine` tag available for a slim version of the image.
### Programatically interfacing Gexp nodes ### Pragmatically interfacing Gexp nodes
As a developer, sooner rather than later you'll want to start interacting with Gexp and the Expanse As a developer, sooner rather than later you'll want to start interacting with Gexp and the Expanse
network via your own programs and not manually through the console. To aid this, Gexp has built in network via your own programs and not manually through the console. To aid this, Gexp has built in
@ -120,6 +120,7 @@ support for a JSON-RPC based APIs ([standard APIs](https://github.com/expanse-or
[Gexp specific APIs](https://github.com/expanse-org/go-expanse/wiki/Management-APIs)). These can be [Gexp specific APIs](https://github.com/expanse-org/go-expanse/wiki/Management-APIs)). These can be
exposed via HTTP, WebSockets and IPC (unix sockets on unix based platroms, and named pipes on Windows). exposed via HTTP, WebSockets and IPC (unix sockets on unix based platroms, and named pipes on Windows).
The IPC interface is enabled by default and exposes all the APIs supported by Gexp, whereas the HTTP The IPC interface is enabled by default and exposes all the APIs supported by Gexp, 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.
These can be turned on/off and configured as you'd expect. These can be turned on/off and configured as you'd expect.

View file

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

View file

@ -17,13 +17,13 @@
package bind package bind
import ( import (
"context"
"errors" "errors"
"math/big" "math/big"
"github.com/expanse-org/go-expanse" "github.com/expanse-org/go-expanse"
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/core/types" "github.com/expanse-org/go-expanse/core/types"
"golang.org/x/net/context"
) )
var ( var (

View file

@ -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/expanse-org/go-expanse" "github.com/expanse-org/go-expanse"
"github.com/expanse-org/go-expanse/accounts/abi/bind" "github.com/expanse-org/go-expanse/accounts/abi/bind"
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/common/math"
"github.com/expanse-org/go-expanse/core" "github.com/expanse-org/go-expanse/core"
"github.com/expanse-org/go-expanse/core/state" "github.com/expanse-org/go-expanse/core/state"
"github.com/expanse-org/go-expanse/core/types" "github.com/expanse-org/go-expanse/core/types"
@ -32,12 +34,9 @@ import (
"github.com/expanse-org/go-expanse/ethdb" "github.com/expanse-org/go-expanse/ethdb"
"github.com/expanse-org/go-expanse/event" "github.com/expanse-org/go-expanse/event"
"github.com/expanse-org/go-expanse/params" "github.com/expanse-org/go-expanse/params"
"golang.org/x/net/context" "github.com/expanse-org/go-expanse/pow"
) )
// Default chain configuration which sets homestead phase at block 0 (i.e. no frontier)
var chainConfig = &params.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)
} }

View file

@ -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/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/core/types" "github.com/expanse-org/go-expanse/core/types"
"github.com/expanse-org/go-expanse/crypto" "github.com/expanse-org/go-expanse/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

View file

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

View file

@ -17,31 +17,31 @@
package bind package bind
import ( import (
"context"
"fmt" "fmt"
"time" "time"
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/core/types" "github.com/expanse-org/go-expanse/core/types"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/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 {

View file

@ -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/expanse-org/go-expanse/core" "github.com/expanse-org/go-expanse/core"
"github.com/expanse-org/go-expanse/core/types" "github.com/expanse-org/go-expanse/core/types"
"github.com/expanse-org/go-expanse/crypto" "github.com/expanse-org/go-expanse/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.

View file

@ -21,6 +21,7 @@ import (
"reflect" "reflect"
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/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

View file

@ -20,6 +20,7 @@ import (
"reflect" "reflect"
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/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 {

View file

@ -30,8 +30,7 @@ import (
"github.com/expanse-org/go-expanse/accounts" "github.com/expanse-org/go-expanse/accounts"
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/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}})
} }

View file

@ -36,6 +36,7 @@ import (
"path/filepath" "path/filepath"
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/common/math"
"github.com/expanse-org/go-expanse/crypto" "github.com/expanse-org/go-expanse/crypto"
"github.com/expanse-org/go-expanse/crypto/randentropy" "github.com/expanse-org/go-expanse/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)

View file

@ -21,8 +21,7 @@ package keystore
import ( import (
"time" "time"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/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

View file

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

View file

@ -27,6 +27,7 @@ import (
"github.com/expanse-org/go-expanse/accounts" "github.com/expanse-org/go-expanse/accounts"
"github.com/expanse-org/go-expanse/event" "github.com/expanse-org/go-expanse/event"
"github.com/expanse-org/go-expanse/log"
"github.com/karalabe/hid" "github.com/karalabe/hid"
) )
@ -120,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{url: &url, info: ledger} 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)

View file

@ -21,6 +21,7 @@
package usbwallet package usbwallet
import ( import (
"context"
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"errors" "errors"
@ -33,12 +34,11 @@ import (
ethereum "github.com/expanse-org/go-expanse" ethereum "github.com/expanse-org/go-expanse"
"github.com/expanse-org/go-expanse/accounts" "github.com/expanse-org/go-expanse/accounts"
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/common/hexutil"
"github.com/expanse-org/go-expanse/core/types" "github.com/expanse-org/go-expanse/core/types"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/logger/glog"
"github.com/expanse-org/go-expanse/rlp" "github.com/expanse-org/go-expanse/rlp"
"github.com/karalabe/hid" "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.
@ -123,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.
@ -220,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 (
@ -260,7 +262,7 @@ func (w *ledgerWallet) heartbeat() {
} }
// 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
@ -348,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 (
@ -394,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
} }
} }
@ -405,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
@ -430,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 {
@ -469,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
@ -849,9 +851,7 @@ 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("-> %s: %x", w.device.Path, chunk)
}
if _, err := w.device.Write(chunk); err != nil { if _, err := w.device.Write(chunk); err != nil {
return nil, err return nil, err
} }
@ -864,9 +864,8 @@ func (w *ledgerWallet) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 l
if _, err := io.ReadFull(w.device, 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("<- %s: %x", w.device.Path, 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

View file

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

View file

@ -1,18 +1,3 @@
# Vendored Dependencies
Dependencies are almost all vendored in at the standard Go `/vendor` path. This allows
people to build go-expanse 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-expanse 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

View file

@ -24,14 +24,14 @@ 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("gexp"), executablePath("gexp"),
executablePath("swarm"), executablePath("swarm"),
@ -82,6 +83,10 @@ var (
Name: "gexp", Name: "gexp",
Description: "Expanse CLI client.", Description: "Expanse 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-expanse requires at least Go version 1.4 and cannot") log.Println("go-expanse 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)
} }

View file

@ -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-expanse" cd "$ethdir/go-expanse"

View file

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

View file

@ -25,7 +25,7 @@ import (
"github.com/expanse-org/go-expanse/cmd/utils" "github.com/expanse-org/go-expanse/cmd/utils"
"github.com/expanse-org/go-expanse/crypto" "github.com/expanse-org/go-expanse/crypto"
"github.com/expanse-org/go-expanse/logger/glog" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/p2p/discover" "github.com/expanse-org/go-expanse/p2p/discover"
"github.com/expanse-org/go-expanse/p2p/discv5" "github.com/expanse-org/go-expanse/p2p/discv5"
"github.com/expanse-org/go-expanse/p2p/nat" "github.com/expanse-org/go-expanse/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)

55
cmd/evm/compiler.go Normal file
View 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/expanse-org/go-expanse/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
View 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/expanse-org/go-expanse/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
}

View 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/expanse-org/go-expanse/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
}

View file

@ -19,19 +19,10 @@ package main
import ( import (
"fmt" "fmt"
"io/ioutil" "math/big"
"os" "os"
goruntime "runtime"
"time"
"github.com/expanse-org/go-expanse/cmd/utils" "github.com/expanse-org/go-expanse/cmd/utils"
"github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/core/state"
"github.com/expanse-org/go-expanse/core/vm"
"github.com/expanse-org/go-expanse/core/vm/runtime"
"github.com/expanse-org/go-expanse/crypto"
"github.com/expanse-org/go-expanse/ethdb"
"github.com/expanse-org/go-expanse/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,
runCommand,
} }
func run(ctx *cli.Context) error {
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)).Uint64(),
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)).Uint64(),
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
View 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/expanse-org/go-expanse/cmd/evm/internal/compiler"
"github.com/expanse-org/go-expanse/cmd/utils"
"github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/core/state"
"github.com/expanse-org/go-expanse/core/vm"
"github.com/expanse-org/go-expanse/core/vm/runtime"
"github.com/expanse-org/go-expanse/ethdb"
"github.com/expanse-org/go-expanse/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
}

View file

@ -25,8 +25,7 @@ import (
"github.com/expanse-org/go-expanse/cmd/utils" "github.com/expanse-org/go-expanse/cmd/utils"
"github.com/expanse-org/go-expanse/console" "github.com/expanse-org/go-expanse/console"
"github.com/expanse-org/go-expanse/crypto" "github.com/expanse-org/go-expanse/crypto"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/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{}, ""
} }

View file

@ -145,7 +145,8 @@ Passphrase: {{.InputLine "foobar"}}
gexp.expectExit() gexp.expectExit()
wantMessages := []string{ wantMessages := []string{
"Unlocked account f466859ead1932d743d622cb74fc058882e8648a", "Unlocked account",
"=0xf466859ead1932d743d622cb74fc058882e8648a",
} }
for _, m := range wantMessages { for _, m := range wantMessages {
if !strings.Contains(gexp.stderrText(), m) { if !strings.Contains(gexp.stderrText(), m) {
@ -189,8 +190,9 @@ Passphrase: {{.InputLine "foobar"}}
gexp.expectExit() gexp.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(gexp.stderrText(), m) { if !strings.Contains(gexp.stderrText(), m) {
@ -208,8 +210,9 @@ func TestUnlockFlagPasswordFile(t *testing.T) {
gexp.expectExit() gexp.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(gexp.stderrText(), m) { if !strings.Contains(gexp.stderrText(), m) {
@ -257,7 +260,8 @@ In order to avoid this warning, you need to remove the following duplicate key f
gexp.expectExit() gexp.expectExit()
wantMessages := []string{ wantMessages := []string{
"Unlocked account f466859ead1932d743d622cb74fc058882e8648a", "Unlocked account",
"=0xf466859ead1932d743d622cb74fc058882e8648a",
} }
for _, m := range wantMessages { for _, m := range wantMessages {
if !strings.Contains(gexp.stderrText(), m) { if !strings.Contains(gexp.stderrText(), m) {

View file

@ -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/expanse-org/go-expanse/core/state" "github.com/expanse-org/go-expanse/core/state"
"github.com/expanse-org/go-expanse/core/types" "github.com/expanse-org/go-expanse/core/types"
"github.com/expanse-org/go-expanse/ethdb" "github.com/expanse-org/go-expanse/ethdb"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/logger/glog"
"github.com/expanse-org/go-expanse/trie" "github.com/expanse-org/go-expanse/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()
}
}

View file

@ -29,13 +29,13 @@ import (
"github.com/expanse-org/go-expanse/accounts/keystore" "github.com/expanse-org/go-expanse/accounts/keystore"
"github.com/expanse-org/go-expanse/cmd/utils" "github.com/expanse-org/go-expanse/cmd/utils"
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/common/hexutil"
"github.com/expanse-org/go-expanse/console" "github.com/expanse-org/go-expanse/console"
"github.com/expanse-org/go-expanse/contracts/release" "github.com/expanse-org/go-expanse/contracts/release"
"github.com/expanse-org/go-expanse/eth" "github.com/expanse-org/go-expanse/eth"
"github.com/expanse-org/go-expanse/ethclient" "github.com/expanse-org/go-expanse/ethclient"
"github.com/expanse-org/go-expanse/internal/debug" "github.com/expanse-org/go-expanse/internal/debug"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/logger/glog"
"github.com/expanse-org/go-expanse/metrics" "github.com/expanse-org/go-expanse/metrics"
"github.com/expanse-org/go-expanse/node" "github.com/expanse-org/go-expanse/node"
"github.com/expanse-org/go-expanse/params" "github.com/expanse-org/go-expanse/params"
@ -66,7 +66,6 @@ func init() {
initCommand, initCommand,
importCommand, importCommand,
exportCommand, exportCommand,
upgradedbCommand,
removedbCommand, removedbCommand,
dumpCommand, dumpCommand,
// See monitorcmd.go: // See monitorcmd.go:
@ -92,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,
@ -107,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,
@ -141,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,
@ -204,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 { 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)
@ -273,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)
} }
@ -282,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()
} }
} }

View file

@ -25,10 +25,10 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/ethereum/ethash"
"github.com/expanse-org/go-expanse/cmd/utils" "github.com/expanse-org/go-expanse/cmd/utils"
"github.com/expanse-org/go-expanse/eth" "github.com/expanse-org/go-expanse/eth"
"github.com/expanse-org/go-expanse/params" "github.com/expanse-org/go-expanse/params"
"github.com/expanse-org/go-expanse/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()

View file

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

View file

@ -17,8 +17,7 @@
package main package main
import ( import (
"log" "github.com/expanse-org/go-expanse/cmd/utils"
"github.com/expanse-org/go-expanse/swarm/storage" "github.com/expanse-org/go-expanse/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()
} }

View file

@ -19,9 +19,9 @@ package main
import ( import (
"fmt" "fmt"
"log"
"os" "os"
"github.com/expanse-org/go-expanse/cmd/utils"
"github.com/expanse-org/go-expanse/swarm/storage" "github.com/expanse-org/go-expanse/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)
} }

View file

@ -35,8 +35,7 @@ import (
"github.com/expanse-org/go-expanse/crypto" "github.com/expanse-org/go-expanse/crypto"
"github.com/expanse-org/go-expanse/ethclient" "github.com/expanse-org/go-expanse/ethclient"
"github.com/expanse-org/go-expanse/internal/debug" "github.com/expanse-org/go-expanse/internal/debug"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/logger/glog"
"github.com/expanse-org/go-expanse/node" "github.com/expanse-org/go-expanse/node"
"github.com/expanse-org/go-expanse/p2p" "github.com/expanse-org/go-expanse/p2p"
"github.com/expanse-org/go-expanse/p2p/discover" "github.com/expanse-org/go-expanse/p2p/discover"
@ -123,7 +122,7 @@ func init() {
// Override flag defaults so bzzd can run alongside gexp. // Override flag defaults so bzzd can run alongside gexp.
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)

View file

@ -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/expanse-org/go-expanse/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,7 +44,6 @@ 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 {
@ -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 (
@ -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 (
@ -134,20 +133,19 @@ 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)
@ -182,15 +180,12 @@ 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 {
@ -212,12 +207,11 @@ 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 {
@ -233,7 +227,7 @@ 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 != "" {
@ -271,10 +265,9 @@ 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
} }
@ -298,11 +291,9 @@ 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 {
@ -318,7 +309,7 @@ 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 != "" {
@ -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
} }

View file

@ -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/expanse-org/go-expanse/cmd/utils"
"github.com/expanse-org/go-expanse/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

View file

@ -23,16 +23,13 @@ import (
"io" "io"
"os" "os"
"os/signal" "os/signal"
"regexp"
"runtime" "runtime"
"strings" "strings"
"github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/core" "github.com/expanse-org/go-expanse/core"
"github.com/expanse-org/go-expanse/core/types" "github.com/expanse-org/go-expanse/core/types"
"github.com/expanse-org/go-expanse/internal/debug" "github.com/expanse-org/go-expanse/internal/debug"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/logger/glog"
"github.com/expanse-org/go-expanse/node" "github.com/expanse-org/go-expanse/node"
"github.com/expanse-org/go-expanse/rlp" "github.com/expanse-org/go-expanse/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
} }

View file

@ -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/expanse-org/go-expanse/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
} }

View file

@ -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/expanse-org/go-expanse/accounts" "github.com/expanse-org/go-expanse/accounts"
"github.com/expanse-org/go-expanse/accounts/keystore" "github.com/expanse-org/go-expanse/accounts/keystore"
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
@ -41,8 +41,7 @@ import (
"github.com/expanse-org/go-expanse/ethstats" "github.com/expanse-org/go-expanse/ethstats"
"github.com/expanse-org/go-expanse/event" "github.com/expanse-org/go-expanse/event"
"github.com/expanse-org/go-expanse/les" "github.com/expanse-org/go-expanse/les"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/logger/glog"
"github.com/expanse-org/go-expanse/metrics" "github.com/expanse-org/go-expanse/metrics"
"github.com/expanse-org/go-expanse/node" "github.com/expanse-org/go-expanse/node"
"github.com/expanse-org/go-expanse/p2p/discover" "github.com/expanse-org/go-expanse/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 := &eth.Config{ ethConf := &eth.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 `gexp 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
} }

View file

@ -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/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/console" "github.com/expanse-org/go-expanse/console"
"github.com/expanse-org/go-expanse/crypto" "github.com/expanse-org/go-expanse/crypto"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/logger/glog"
"github.com/expanse-org/go-expanse/p2p" "github.com/expanse-org/go-expanse/p2p"
"github.com/expanse-org/go-expanse/p2p/discover" "github.com/expanse-org/go-expanse/p2p/discover"
"github.com/expanse-org/go-expanse/p2p/nat" "github.com/expanse-org/go-expanse/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:42786)") argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:42786)")
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"
} }
@ -286,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))
} }
} }
@ -315,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]
} }
} }
@ -379,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
View file

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

View file

@ -1,3 +0,0 @@
language: go
go:
- 1.2

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,26 +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 secp256k1 package hexutil_test
import ( import (
"bytes" "encoding/json"
"encoding/hex" "fmt"
"math/big"
"testing" "github.com/expanse-org/go-expanse/common/hexutil"
) )
func TestReadBits(t *testing.T) { type MyType [5]byte
check := func(input string) {
want, _ := hex.DecodeString(input) func (v *MyType) UnmarshalText(input []byte) error {
int, _ := new(big.Int).SetString(input, 16) return hexutil.UnmarshalFixedText("MyType", input, v[:])
buf := make([]byte, len(want))
readBits(buf, int)
if !bytes.Equal(buf, want) {
t.Errorf("have: %x\nwant: %x", buf, want)
} }
func (v MyType) String() string {
return hexutil.Bytes(v[:]).String()
} }
check("000000000000000000000000000000000000000000000000000000FEFCF3F8F0")
check("0000000000012345000000000000000000000000000000000000FEFCF3F8F0") func ExampleUnmarshalFixedText() {
check("18F8F8F1000111000110011100222004330052300000000000000000FEFCF3F8F0") 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
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,10 +1,84 @@
// 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 package math
import gmath "math" import (
"fmt"
"strconv"
)
/* const (
* NOTE: The following methods need to be optimised using either bit checking or asm // 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. // SafeSub returns subtraction result and whether overflow occurred.
func SafeSub(x, y uint64) (uint64, bool) { func SafeSub(x, y uint64) (uint64, bool) {
@ -13,7 +87,7 @@ func SafeSub(x, y uint64) (uint64, bool) {
// SafeAdd returns the result and whether overflow occurred. // SafeAdd returns the result and whether overflow occurred.
func SafeAdd(x, y uint64) (uint64, bool) { func SafeAdd(x, y uint64) (uint64, bool) {
return x + y, y > gmath.MaxUint64-x return x + y, y > MaxUint64-x
} }
// SafeMul returns multiplication result and whether overflow occurred. // SafeMul returns multiplication result and whether overflow occurred.
@ -21,5 +95,5 @@ func SafeMul(x, y uint64) (uint64, bool) {
if x == 0 || y == 0 { if x == 0 || y == 0 {
return 0, false return 0, false
} }
return x * y, y > gmath.MaxUint64/x return x * y, y > MaxUint64/x
} }

View file

@ -1,7 +1,22 @@
// 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 package math
import ( import (
gmath "math"
"testing" "testing"
) )
@ -21,17 +36,18 @@ func TestOverflow(t *testing.T) {
op operation op operation
}{ }{
// add operations // add operations
{gmath.MaxUint64, 1, true, add}, {MaxUint64, 1, true, add},
{gmath.MaxUint64 - 1, 1, false, add}, {MaxUint64 - 1, 1, false, add},
// sub operations // sub operations
{0, 1, true, sub}, {0, 1, true, sub},
{0, 0, false, sub}, {0, 0, false, sub},
// mul operations // mul operations
{0, 0, false, mul},
{10, 10, false, mul}, {10, 10, false, mul},
{gmath.MaxUint64, 2, true, mul}, {MaxUint64, 2, true, mul},
{gmath.MaxUint64, 1, false, mul}, {MaxUint64, 1, false, mul},
} { } {
var overflows bool var overflows bool
switch test.op { switch test.op {
@ -48,3 +64,53 @@ func TestOverflow(t *testing.T) {
} }
} }
} }
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")
}

View file

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

View file

@ -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 {
var _ = checker.Suite(&SizeSuite{}) size StorageSize
str string
func (s *SizeSuite) TestStorageSizeString(c *checker.C) { }{
data1 := 2381273 {2381273, "2.38 mB"},
data2 := 2192 {2192, "2.19 kB"},
data3 := 12 {12, "12.00 B"},
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) { for _, test := range tests {
ether := CurrencyToString(BigPow(10, 19)) if test.size.String() != test.str {
finney := CurrencyToString(BigPow(10, 16)) t.Errorf("%f: got %q, want %q", float64(test.size), test.size.String(), test.str)
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")
} }

View file

@ -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.
Hash [HashLength]byte type 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 { // UnmarshalText decodes the address from hex. The 0x prefix is optional.
return Bytes2Hex(value) func (a *UnprefixedAddress) UnmarshalText(input []byte) error {
return hexutil.UnmarshalFixedUnprefixedText("UnprefixedAddress", input, a[:])
} }
return fmt.Sprintf("%x...%x", value[:4], value[len(value)-4]) // MarshalText encodes the address as hex.
func (a UnprefixedAddress) MarshalText() ([]byte, error) {
return []byte(hex.EncodeToString(a[:])), nil
} }

View file

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

View file

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

View file

@ -22,8 +22,7 @@ import (
"io" "io"
"time" "time"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/logger/glog"
"github.com/expanse-org/go-expanse/rpc" "github.com/expanse-org/go-expanse/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)
} }

View file

@ -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/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/core"
"github.com/expanse-org/go-expanse/eth" "github.com/expanse-org/go-expanse/eth"
"github.com/expanse-org/go-expanse/internal/jsre" "github.com/expanse-org/go-expanse/internal/jsre"
"github.com/expanse-org/go-expanse/node" "github.com/expanse-org/go-expanse/node"
"github.com/expanse-org/go-expanse/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 := &eth.Config{ ethConf := &eth.Config{
ChainConfig: &params.ChainConfig{HomesteadBlock: new(big.Int), ChainId: new(big.Int)}, Genesis: core.DevGenesisBlock(),
Etherbase: common.HexToAddress(testAddress), Etherbase: common.HexToAddress(testAddress),
PowTest: true, PowTest: true,
} }

View file

@ -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/expanse-org/go-expanse/accounts/abi/bind" "github.com/expanse-org/go-expanse/accounts/abi/bind"
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/common/hexutil"
"github.com/expanse-org/go-expanse/contracts/chequebook/contract" "github.com/expanse-org/go-expanse/contracts/chequebook/contract"
"github.com/expanse-org/go-expanse/core/types" "github.com/expanse-org/go-expanse/core/types"
"github.com/expanse-org/go-expanse/crypto" "github.com/expanse-org/go-expanse/crypto"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/logger/glog"
"github.com/expanse-org/go-expanse/swarm/services/swap/swap" "github.com/expanse-org/go-expanse/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)
} }
} }

View file

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

View file

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

View file

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

View file

@ -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/expanse-org/go-expanse/eth" "github.com/expanse-org/go-expanse/eth"
"github.com/expanse-org/go-expanse/internal/ethapi" "github.com/expanse-org/go-expanse/internal/ethapi"
"github.com/expanse-org/go-expanse/les" "github.com/expanse-org/go-expanse/les"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/logger/glog"
"github.com/expanse-org/go-expanse/node" "github.com/expanse-org/go-expanse/node"
"github.com/expanse-org/go-expanse/p2p" "github.com/expanse-org/go-expanse/p2p"
"github.com/expanse-org/go-expanse/rpc" "github.com/expanse-org/go-expanse/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/expanse-org/go-expanse/releases for new releases") howtofix := fmt.Sprintf("Please check https://github.com/expanse-org/go-expanse/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
}
} }
} }

139
core/asm/asm.go Normal file
View 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/expanse-org/go-expanse/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
View 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
View 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/expanse-org/go-expanse/common/math"
"github.com/expanse-org/go-expanse/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,
}
}

View file

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

View file

@ -24,12 +24,14 @@ import (
"testing" "testing"
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/common/math"
"github.com/expanse-org/go-expanse/core/types" "github.com/expanse-org/go-expanse/core/types"
"github.com/expanse-org/go-expanse/core/vm" "github.com/expanse-org/go-expanse/core/vm"
"github.com/expanse-org/go-expanse/crypto" "github.com/expanse-org/go-expanse/crypto"
"github.com/expanse-org/go-expanse/ethdb" "github.com/expanse-org/go-expanse/ethdb"
"github.com/expanse-org/go-expanse/event" "github.com/expanse-org/go-expanse/event"
"github.com/expanse-org/go-expanse/params" "github.com/expanse-org/go-expanse/params"
"github.com/expanse-org/go-expanse/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
@ -164,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, &params.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()
@ -280,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)
} }

View file

@ -22,9 +22,10 @@ import (
"time" "time"
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/common/math"
"github.com/expanse-org/go-expanse/core/state" "github.com/expanse-org/go-expanse/core/state"
"github.com/expanse-org/go-expanse/core/types" "github.com/expanse-org/go-expanse/core/types"
"github.com/expanse-org/go-expanse/logger/glog" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/params" "github.com/expanse-org/go-expanse/params"
"github.com/expanse-org/go-expanse/pow" "github.com/expanse-org/go-expanse/pow"
"gopkg.in/fatih/set.v0" "gopkg.in/fatih/set.v0"
@ -170,7 +171,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])
} }
@ -210,7 +211,7 @@ func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Heade
} }
if uncle { if uncle {
if header.Time.Cmp(common.MaxBig) == 1 { if header.Time.Cmp(math.MaxBig256) == 1 {
return BlockTSTooBigErr return BlockTSTooBigErr
} }
} else { } else {
@ -244,7 +245,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()}
} }
} }
@ -340,7 +341,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
@ -368,13 +369,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
} }

View file

@ -17,50 +17,36 @@
package core package core
import ( import (
"fmt"
"math/big" "math/big"
"testing" "testing"
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/core/state" "github.com/expanse-org/go-expanse/core/state"
"github.com/expanse-org/go-expanse/core/types" "github.com/expanse-org/go-expanse/core/types"
"github.com/expanse-org/go-expanse/core/vm"
"github.com/expanse-org/go-expanse/ethdb" "github.com/expanse-org/go-expanse/ethdb"
"github.com/expanse-org/go-expanse/event"
"github.com/expanse-org/go-expanse/params" "github.com/expanse-org/go-expanse/params"
"github.com/expanse-org/go-expanse/pow"
) )
func testChainConfig() *params.ChainConfig { func testGenesis(account common.Address, balance *big.Int) *Genesis {
return params.TestChainConfig return &Genesis{
//return &params.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")
} }

View file

@ -36,8 +36,7 @@ import (
"github.com/expanse-org/go-expanse/crypto" "github.com/expanse-org/go-expanse/crypto"
"github.com/expanse-org/go-expanse/ethdb" "github.com/expanse-org/go-expanse/ethdb"
"github.com/expanse-org/go-expanse/event" "github.com/expanse-org/go-expanse/event"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/logger/glog"
"github.com/expanse-org/go-expanse/metrics" "github.com/expanse-org/go-expanse/metrics"
"github.com/expanse-org/go-expanse/params" "github.com/expanse-org/go-expanse/params"
"github.com/expanse-org/go-expanse/pow" "github.com/expanse-org/go-expanse/pow"
@ -161,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")
} }
} }
} }
@ -183,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{}) {
@ -201,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{}) {
@ -214,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
} }
@ -232,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()
@ -247,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
@ -288,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
} }
@ -377,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)
@ -403,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.
@ -418,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)
@ -447,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
@ -459,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
} }
@ -590,8 +612,7 @@ 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() {
@ -685,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
@ -736,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)
@ -786,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
} }
@ -831,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
@ -865,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
@ -895,7 +915,7 @@ 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()
@ -905,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()}
} }
} }
@ -980,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
} }
@ -992,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})
@ -1015,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
@ -1059,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}
} }
@ -1151,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 {
@ -1272,12 +1286,12 @@ func (bc *BlockChain) addBadBlock(block *types.Block) {
// 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) {
bc.addBadBlock(block) bc.addBadBlock(block)
if glog.V(logger.Error) {
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
@ -1287,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
@ -1300,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()
@ -1315,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

View file

@ -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/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/core/state" "github.com/expanse-org/go-expanse/core/state"
"github.com/expanse-org/go-expanse/core/types" "github.com/expanse-org/go-expanse/core/types"
@ -37,29 +32,21 @@ import (
"github.com/expanse-org/go-expanse/event" "github.com/expanse-org/go-expanse/event"
"github.com/expanse-org/go-expanse/params" "github.com/expanse-org/go-expanse/params"
"github.com/expanse-org/go-expanse/pow" "github.com/expanse-org/go-expanse/pow"
"github.com/expanse-org/go-expanse/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{
Config: params.TestChainConfig,
Difficulty: big.NewInt(1),
} }
gspec.MustCommit(db)
func thePow() pow.PoW { blockchain, err := NewBlockChain(db, gspec.Config, pow.FakePow{}, new(event.TypeMux), vm.Config{})
pow, _ := ethash.NewForTesting()
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 { if err != nil {
t.Error("failed creating blockchain:", err) panic(err)
t.FailNow()
return nil
} }
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,29 +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.badBlocks, _ = lru.New(10)
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) }
@ -512,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 {
@ -542,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)
@ -559,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)
} }
@ -586,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 {
@ -614,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)
} }
@ -669,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)
} }
@ -711,10 +569,11 @@ 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
@ -734,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 {
@ -794,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{}
@ -818,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)
@ -831,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 {
@ -850,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)
@ -863,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")
@ -874,13 +731,19 @@ 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
@ -898,7 +761,7 @@ func TestChainTxReorgs(t *testing.T) {
// - futureAdd: transaction added after the reorg has already finished // - futureAdd: transaction added after the reorg has already finished
var pastAdd, freshAdd, futureAdd *types.Transaction var pastAdd, freshAdd, futureAdd *types.Transaction
chain, _ := GenerateChain(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), bigTxGas, nil, nil), signer, key2) pastDrop, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), bigTxGas, nil, nil), signer, key2)
@ -917,13 +780,13 @@ 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), bigTxGas, nil, nil), signer, key3) pastAdd, _ = types.SignTx(types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), bigTxGas, nil, nil), signer, key3)
@ -975,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")
@ -984,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) {
@ -1023,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)
@ -1098,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())
} }
@ -1130,7 +984,7 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
} }
}(chain[i]) }(chain[i])
blockchain.InsertChain(types.Blocks{chain[i]}) bc.InsertChain(types.Blocks{chain[i]})
} }
} }
@ -1142,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 = &params.ChainConfig{ChainId: big.NewInt(1), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)} Config: &params.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
@ -1170,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)
} }
@ -1182,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)
} }
@ -1210,7 +1067,7 @@ func TestEIP155Transition(t *testing.T) {
} }
// generate an invalid chain id transaction // generate an invalid chain id transaction
config = &params.ChainConfig{ChainId: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)} config := &params.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
@ -1242,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 = &params.ChainConfig{ Config: &params.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:
@ -1285,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
@ -1293,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")
} }
} }

View file

@ -30,30 +30,6 @@ import (
"github.com/expanse-org/go-expanse/pow" "github.com/expanse-org/go-expanse/pow"
) )
/*
* TODO: move this to another package.
*/
// MakeChainConfig returns a new ChainConfig with the ethereum default chain settings.
func MakeChainConfig() *params.ChainConfig {
return &params.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

View file

@ -26,12 +26,10 @@ import (
"github.com/expanse-org/go-expanse/ethdb" "github.com/expanse-org/go-expanse/ethdb"
"github.com/expanse-org/go-expanse/event" "github.com/expanse-org/go-expanse/event"
"github.com/expanse-org/go-expanse/params" "github.com/expanse-org/go-expanse/params"
"github.com/expanse-org/go-expanse/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,19 +38,20 @@ 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 := &params.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: &params.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.
@ -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

View file

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

View file

@ -17,6 +17,7 @@
package core package core
import ( import (
"errors"
"math/big" "math/big"
"runtime" "runtime"
"testing" "testing"
@ -35,12 +36,16 @@ type failPow struct {
failing uint64 failing uint64
} }
func (pow failPow) Search(pow.Block, <-chan struct{}, int) (uint64, []byte) { func (pow failPow) Search(pow.Block, <-chan struct{}) (uint64, []byte) {
return 0, nil return 0, nil
} }
func (pow failPow) Verify(block pow.Block) bool { return block.NumberU64() != pow.failing } func (pow failPow) Verify(block pow.Block) error {
func (pow failPow) GetHashrate() int64 { return 0 } if block.NumberU64() == pow.failing {
func (pow failPow) Turbo(bool) {} return errors.New("failed")
}
return nil
}
func (pow failPow) Hashrate() float64 { return 0 }
// delayedPow is a non-validating proof of work implementation, that returns true // delayedPow is a non-validating proof of work implementation, that returns true
// from Verify for all blocks, but delays them the configured amount of time. // from Verify for all blocks, but delays them the configured amount of time.
@ -48,12 +53,11 @@ type delayedPow struct {
delay time.Duration delay time.Duration
} }
func (pow delayedPow) Search(pow.Block, <-chan struct{}, int) (uint64, []byte) { func (pow delayedPow) Search(pow.Block, <-chan struct{}) (uint64, []byte) {
return 0, nil return 0, nil
} }
func (pow delayedPow) Verify(block pow.Block) bool { time.Sleep(pow.delay); return true } func (pow delayedPow) Verify(block pow.Block) error { time.Sleep(pow.delay); return nil }
func (pow delayedPow) GetHashrate() int64 { return 0 } func (pow delayedPow) Hashrate() float64 { return 0 }
func (pow delayedPow) Turbo(bool) {}
// Tests that simple POW verification works, for both good and bad blocks. // Tests that simple POW verification works, for both good and bad blocks.
func TestPowVerification(t *testing.T) { func TestPowVerification(t *testing.T) {
@ -75,11 +79,11 @@ func TestPowVerification(t *testing.T) {
switch { switch {
case full && valid: case full && valid:
_, results = verifyNoncesFromBlocks(FakePow{}, []*types.Block{blocks[i]}) _, results = verifyNoncesFromBlocks(pow.FakePow{}, []*types.Block{blocks[i]})
case full && !valid: case full && !valid:
_, results = verifyNoncesFromBlocks(failPow{blocks[i].NumberU64()}, []*types.Block{blocks[i]}) _, results = verifyNoncesFromBlocks(failPow{blocks[i].NumberU64()}, []*types.Block{blocks[i]})
case !full && valid: case !full && valid:
_, results = verifyNoncesFromHeaders(FakePow{}, []*types.Header{headers[i]}) _, results = verifyNoncesFromHeaders(pow.FakePow{}, []*types.Header{headers[i]})
case !full && !valid: case !full && !valid:
_, results = verifyNoncesFromHeaders(failPow{headers[i].Number.Uint64()}, []*types.Header{headers[i]}) _, results = verifyNoncesFromHeaders(failPow{headers[i].Number.Uint64()}, []*types.Header{headers[i]})
} }
@ -134,11 +138,11 @@ func testPowConcurrentVerification(t *testing.T, threads int) {
switch { switch {
case full && valid: case full && valid:
_, results = verifyNoncesFromBlocks(FakePow{}, blocks) _, results = verifyNoncesFromBlocks(pow.FakePow{}, blocks)
case full && !valid: case full && !valid:
_, results = verifyNoncesFromBlocks(failPow{uint64(len(blocks) - 1)}, blocks) _, results = verifyNoncesFromBlocks(failPow{uint64(len(blocks) - 1)}, blocks)
case !full && valid: case !full && valid:
_, results = verifyNoncesFromHeaders(FakePow{}, headers) _, results = verifyNoncesFromHeaders(pow.FakePow{}, headers)
case !full && !valid: case !full && !valid:
_, results = verifyNoncesFromHeaders(failPow{uint64(len(headers) - 1)}, headers) _, results = verifyNoncesFromHeaders(failPow{uint64(len(headers) - 1)}, headers)
} }

View file

@ -62,13 +62,13 @@ func ValidateDAOHeaderExtraData(config *params.ChainConfig, header *types.Header
// contract. // contract.
func ApplyDAOHardFork(statedb *state.StateDB) { func ApplyDAOHardFork(statedb *state.StateDB) {
// Retrieve the contract to refund balances into // Retrieve the contract to refund balances into
refund := statedb.GetOrNewStateObject(params.DAORefundContract) if !statedb.Exist(params.DAORefundContract) {
statedb.CreateAccount(params.DAORefundContract)
}
// Move every DAO account and extra-balance account funds into the refund contract // Move every DAO account and extra-balance account funds into the refund contract
for _, addr := range params.DAODrainList { for _, addr := range params.DAODrainList() {
if account := statedb.GetStateObject(addr); account != nil { statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr))
refund.AddBalance(account.Balance()) statedb.SetBalance(addr, new(big.Int))
account.SetBalance(new(big.Int))
}
} }
} }

View file

@ -24,6 +24,7 @@ import (
"github.com/expanse-org/go-expanse/ethdb" "github.com/expanse-org/go-expanse/ethdb"
"github.com/expanse-org/go-expanse/event" "github.com/expanse-org/go-expanse/event"
"github.com/expanse-org/go-expanse/params" "github.com/expanse-org/go-expanse/params"
"github.com/expanse-org/go-expanse/pow"
) )
// Tests that DAO-fork enabled clients can properly filter out fork-commencing // Tests that DAO-fork enabled clients can properly filter out fork-commencing
@ -33,19 +34,20 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Generate a common prefix for both pro-forkers and non-forkers // Generate a common prefix for both pro-forkers and non-forkers
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
genesis := WriteGenesisBlockForTesting(db) gspec := new(Genesis)
genesis := gspec.MustCommit(db)
prefix, _ := GenerateChain(params.TestChainConfig, genesis, db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {}) prefix, _ := GenerateChain(params.TestChainConfig, genesis, db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
// Create the concurrent, conflicting two nodes // Create the concurrent, conflicting two nodes
proDb, _ := ethdb.NewMemDatabase() proDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(proDb) gspec.MustCommit(proDb)
proConf := &params.ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: true} proConf := &params.ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: true}
proBc, _ := NewBlockChain(proDb, proConf, new(FakePow), new(event.TypeMux), vm.Config{}) proBc, _ := NewBlockChain(proDb, proConf, new(pow.FakePow), new(event.TypeMux), vm.Config{})
conDb, _ := ethdb.NewMemDatabase() conDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(conDb) gspec.MustCommit(conDb)
conConf := &params.ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: false} conConf := &params.ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: false}
conBc, _ := NewBlockChain(conDb, conConf, new(FakePow), new(event.TypeMux), vm.Config{}) conBc, _ := NewBlockChain(conDb, conConf, new(pow.FakePow), new(event.TypeMux), vm.Config{})
if _, err := proBc.InsertChain(prefix); err != nil { if _, err := proBc.InsertChain(prefix); err != nil {
t.Fatalf("pro-fork: failed to import chain prefix: %v", err) t.Fatalf("pro-fork: failed to import chain prefix: %v", err)
@ -57,8 +59,8 @@ func TestDAOForkRangeExtradata(t *testing.T) {
for i := int64(0); i < params.DAOForkExtraRange.Int64(); i++ { for i := int64(0); i < params.DAOForkExtraRange.Int64(); i++ {
// Create a pro-fork block, and try to feed into the no-fork chain // Create a pro-fork block, and try to feed into the no-fork chain
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(db) gspec.MustCommit(db)
bc, _ := NewBlockChain(db, conConf, new(FakePow), new(event.TypeMux), vm.Config{}) bc, _ := NewBlockChain(db, conConf, new(pow.FakePow), new(event.TypeMux), vm.Config{})
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()+1)) blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()+1))
for j := 0; j < len(blocks)/2; j++ { for j := 0; j < len(blocks)/2; j++ {
@ -78,8 +80,8 @@ func TestDAOForkRangeExtradata(t *testing.T) {
} }
// Create a no-fork block, and try to feed into the pro-fork chain // Create a no-fork block, and try to feed into the pro-fork chain
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(db) gspec.MustCommit(db)
bc, _ = NewBlockChain(db, proConf, new(FakePow), new(event.TypeMux), vm.Config{}) bc, _ = NewBlockChain(db, proConf, new(pow.FakePow), new(event.TypeMux), vm.Config{})
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()+1)) blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()+1))
for j := 0; j < len(blocks)/2; j++ { for j := 0; j < len(blocks)/2; j++ {
@ -100,8 +102,8 @@ func TestDAOForkRangeExtradata(t *testing.T) {
} }
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes // Verify that contra-forkers accept pro-fork extra-datas after forking finishes
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(db) gspec.MustCommit(db)
bc, _ := NewBlockChain(db, conConf, new(FakePow), new(event.TypeMux), vm.Config{}) bc, _ := NewBlockChain(db, conConf, new(pow.FakePow), new(event.TypeMux), vm.Config{})
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()+1)) blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()+1))
for j := 0; j < len(blocks)/2; j++ { for j := 0; j < len(blocks)/2; j++ {
@ -116,8 +118,8 @@ func TestDAOForkRangeExtradata(t *testing.T) {
} }
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes // Verify that pro-forkers accept contra-fork extra-datas after forking finishes
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(db) gspec.MustCommit(db)
bc, _ = NewBlockChain(db, proConf, new(FakePow), new(event.TypeMux), vm.Config{}) bc, _ = NewBlockChain(db, proConf, new(pow.FakePow), new(event.TypeMux), vm.Config{})
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()+1)) blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()+1))
for j := 0; j < len(blocks)/2; j++ { for j := 0; j < len(blocks)/2; j++ {

View file

@ -28,8 +28,7 @@ import (
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/core/types" "github.com/expanse-org/go-expanse/core/types"
"github.com/expanse-org/go-expanse/ethdb" "github.com/expanse-org/go-expanse/ethdb"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/logger/glog"
"github.com/expanse-org/go-expanse/metrics" "github.com/expanse-org/go-expanse/metrics"
"github.com/expanse-org/go-expanse/params" "github.com/expanse-org/go-expanse/params"
"github.com/expanse-org/go-expanse/rlp" "github.com/expanse-org/go-expanse/rlp"
@ -107,7 +106,7 @@ func GetBlockNumber(db ethdb.Database, hash common.Hash) uint64 {
} }
header := new(types.Header) header := new(types.Header)
if err := rlp.Decode(bytes.NewReader(data), header); err != nil { if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
glog.Fatalf("failed to decode block header: %v", err) log.Crit("Failed to decode block header", "err", err)
} }
return header.Number.Uint64() return header.Number.Uint64()
} }
@ -167,7 +166,7 @@ func GetHeader(db ethdb.Database, hash common.Hash, number uint64) *types.Header
} }
header := new(types.Header) header := new(types.Header)
if err := rlp.Decode(bytes.NewReader(data), header); err != nil { if err := rlp.Decode(bytes.NewReader(data), header); err != nil {
glog.V(logger.Error).Infof("invalid block header RLP for hash %x: %v", hash, err) log.Error("Invalid block header RLP", "hash", hash, "err", err)
return nil return nil
} }
return header return header
@ -191,7 +190,7 @@ func GetBody(db ethdb.Database, hash common.Hash, number uint64) *types.Body {
} }
body := new(types.Body) body := new(types.Body)
if err := rlp.Decode(bytes.NewReader(data), body); err != nil { if err := rlp.Decode(bytes.NewReader(data), body); err != nil {
glog.V(logger.Error).Infof("invalid block body RLP for hash %x: %v", hash, err) log.Error("Invalid block body RLP", "hash", hash, "err", err)
return nil return nil
} }
return body return body
@ -209,7 +208,7 @@ func GetTd(db ethdb.Database, hash common.Hash, number uint64) *big.Int {
} }
td := new(big.Int) td := new(big.Int)
if err := rlp.Decode(bytes.NewReader(data), td); err != nil { if err := rlp.Decode(bytes.NewReader(data), td); err != nil {
glog.V(logger.Error).Infof("invalid block total difficulty RLP for hash %x: %v", hash, err) log.Error("Invalid block total difficulty RLP", "hash", hash, "err", err)
return nil return nil
} }
return td return td
@ -247,7 +246,7 @@ func GetBlockReceipts(db ethdb.Database, hash common.Hash, number uint64) types.
} }
storageReceipts := []*types.ReceiptForStorage{} storageReceipts := []*types.ReceiptForStorage{}
if err := rlp.DecodeBytes(data, &storageReceipts); err != nil { if err := rlp.DecodeBytes(data, &storageReceipts); err != nil {
glog.V(logger.Error).Infof("invalid receipt array RLP for hash %x: %v", hash, err) log.Error("Invalid receipt array RLP", "hash", hash, "err", err)
return nil return nil
} }
receipts := make(types.Receipts, len(storageReceipts)) receipts := make(types.Receipts, len(storageReceipts))
@ -286,15 +285,15 @@ func GetTransaction(db ethdb.Database, hash common.Hash) (*types.Transaction, co
} }
// GetReceipt returns a receipt by hash // GetReceipt returns a receipt by hash
func GetReceipt(db ethdb.Database, txHash common.Hash) *types.Receipt { func GetReceipt(db ethdb.Database, hash common.Hash) *types.Receipt {
data, _ := db.Get(append(receiptsPrefix, txHash[:]...)) data, _ := db.Get(append(receiptsPrefix, hash[:]...))
if len(data) == 0 { if len(data) == 0 {
return nil return nil
} }
var receipt types.ReceiptForStorage var receipt types.ReceiptForStorage
err := rlp.DecodeBytes(data, &receipt) err := rlp.DecodeBytes(data, &receipt)
if err != nil { if err != nil {
glog.V(logger.Debug).Infoln("GetReceipt err:", err) log.Error("Invalid receipt RLP", "hash", hash, "err", err)
} }
return (*types.Receipt)(&receipt) return (*types.Receipt)(&receipt)
} }
@ -303,7 +302,7 @@ func GetReceipt(db ethdb.Database, txHash common.Hash) *types.Receipt {
func WriteCanonicalHash(db ethdb.Database, hash common.Hash, number uint64) error { func WriteCanonicalHash(db ethdb.Database, hash common.Hash, number uint64) error {
key := append(append(headerPrefix, encodeBlockNumber(number)...), numSuffix...) key := append(append(headerPrefix, encodeBlockNumber(number)...), numSuffix...)
if err := db.Put(key, hash.Bytes()); err != nil { if err := db.Put(key, hash.Bytes()); err != nil {
glog.Fatalf("failed to store number to hash mapping into database: %v", err) log.Crit("Failed to store number to hash mapping", "err", err)
} }
return nil return nil
} }
@ -311,7 +310,7 @@ func WriteCanonicalHash(db ethdb.Database, hash common.Hash, number uint64) erro
// WriteHeadHeaderHash stores the head header's hash. // WriteHeadHeaderHash stores the head header's hash.
func WriteHeadHeaderHash(db ethdb.Database, hash common.Hash) error { func WriteHeadHeaderHash(db ethdb.Database, hash common.Hash) error {
if err := db.Put(headHeaderKey, hash.Bytes()); err != nil { if err := db.Put(headHeaderKey, hash.Bytes()); err != nil {
glog.Fatalf("failed to store last header's hash into database: %v", err) log.Crit("Failed to store last header's hash", "err", err)
} }
return nil return nil
} }
@ -319,7 +318,7 @@ func WriteHeadHeaderHash(db ethdb.Database, hash common.Hash) error {
// WriteHeadBlockHash stores the head block's hash. // WriteHeadBlockHash stores the head block's hash.
func WriteHeadBlockHash(db ethdb.Database, hash common.Hash) error { func WriteHeadBlockHash(db ethdb.Database, hash common.Hash) error {
if err := db.Put(headBlockKey, hash.Bytes()); err != nil { if err := db.Put(headBlockKey, hash.Bytes()); err != nil {
glog.Fatalf("failed to store last block's hash into database: %v", err) log.Crit("Failed to store last block's hash", "err", err)
} }
return nil return nil
} }
@ -327,7 +326,7 @@ func WriteHeadBlockHash(db ethdb.Database, hash common.Hash) error {
// WriteHeadFastBlockHash stores the fast head block's hash. // WriteHeadFastBlockHash stores the fast head block's hash.
func WriteHeadFastBlockHash(db ethdb.Database, hash common.Hash) error { func WriteHeadFastBlockHash(db ethdb.Database, hash common.Hash) error {
if err := db.Put(headFastKey, hash.Bytes()); err != nil { if err := db.Put(headFastKey, hash.Bytes()); err != nil {
glog.Fatalf("failed to store last fast block's hash into database: %v", err) log.Crit("Failed to store last fast block's hash", "err", err)
} }
return nil return nil
} }
@ -343,13 +342,12 @@ func WriteHeader(db ethdb.Database, header *types.Header) error {
encNum := encodeBlockNumber(num) encNum := encodeBlockNumber(num)
key := append(blockHashPrefix, hash...) key := append(blockHashPrefix, hash...)
if err := db.Put(key, encNum); err != nil { if err := db.Put(key, encNum); err != nil {
glog.Fatalf("failed to store hash to number mapping into database: %v", err) log.Crit("Failed to store hash to number mapping", "err", err)
} }
key = append(append(headerPrefix, encNum...), hash...) key = append(append(headerPrefix, encNum...), hash...)
if err := db.Put(key, data); err != nil { if err := db.Put(key, data); err != nil {
glog.Fatalf("failed to store header into database: %v", err) log.Crit("Failed to store header", "err", err)
} }
glog.V(logger.Debug).Infof("stored header #%v [%x…]", header.Number, hash[:4])
return nil return nil
} }
@ -366,9 +364,8 @@ func WriteBody(db ethdb.Database, hash common.Hash, number uint64, body *types.B
func WriteBodyRLP(db ethdb.Database, hash common.Hash, number uint64, rlp rlp.RawValue) error { func WriteBodyRLP(db ethdb.Database, hash common.Hash, number uint64, rlp rlp.RawValue) error {
key := append(append(bodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...) key := append(append(bodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
if err := db.Put(key, rlp); err != nil { if err := db.Put(key, rlp); err != nil {
glog.Fatalf("failed to store block body into database: %v", err) log.Crit("Failed to store block body", "err", err)
} }
glog.V(logger.Debug).Infof("stored block body [%x…]", hash.Bytes()[:4])
return nil return nil
} }
@ -380,9 +377,8 @@ func WriteTd(db ethdb.Database, hash common.Hash, number uint64, td *big.Int) er
} }
key := append(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...), tdSuffix...) key := append(append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...), tdSuffix...)
if err := db.Put(key, data); err != nil { if err := db.Put(key, data); err != nil {
glog.Fatalf("failed to store block total difficulty into database: %v", err) log.Crit("Failed to store block total difficulty", "err", err)
} }
glog.V(logger.Debug).Infof("stored block total difficulty [%x…]: %v", hash.Bytes()[:4], td)
return nil return nil
} }
@ -415,9 +411,8 @@ func WriteBlockReceipts(db ethdb.Database, hash common.Hash, number uint64, rece
// Store the flattened receipt slice // Store the flattened receipt slice
key := append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...) key := append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
if err := db.Put(key, bytes); err != nil { if err := db.Put(key, bytes); err != nil {
glog.Fatalf("failed to store block receipts into database: %v", err) log.Crit("Failed to store block receipts", "err", err)
} }
glog.V(logger.Debug).Infof("stored block receipts [%x…]", hash.Bytes()[:4])
return nil return nil
} }
@ -435,7 +430,7 @@ func WriteTransactions(db ethdb.Database, block *types.Block) error {
if err != nil { if err != nil {
return err return err
} }
if err := batch.Put(tx.Hash().Bytes(), data); err != nil { if err = batch.Put(tx.Hash().Bytes(), data); err != nil {
return err return err
} }
// Encode and queue up the transaction metadata for storage // Encode and queue up the transaction metadata for storage
@ -458,7 +453,7 @@ func WriteTransactions(db ethdb.Database, block *types.Block) error {
} }
// Write the scheduled data into the database // Write the scheduled data into the database
if err := batch.Write(); err != nil { if err := batch.Write(); err != nil {
glog.Fatalf("failed to store transactions into database: %v", err) log.Crit("Failed to store transactions", "err", err)
} }
return nil return nil
} }
@ -490,7 +485,7 @@ func WriteReceipts(db ethdb.Database, receipts types.Receipts) error {
} }
// Write the scheduled data into the database // Write the scheduled data into the database
if err := batch.Write(); err != nil { if err := batch.Write(); err != nil {
glog.Fatalf("failed to store receipts into database: %v", err) log.Crit("Failed to store receipts", "err", err)
} }
return nil return nil
} }
@ -540,24 +535,6 @@ func DeleteReceipt(db ethdb.Database, hash common.Hash) {
db.Delete(append(receiptsPrefix, hash.Bytes()...)) db.Delete(append(receiptsPrefix, hash.Bytes()...))
} }
// [deprecated by the header/block split, remove eventually]
// GetBlockByHashOld returns the old combined block corresponding to the hash
// or nil if not found. This method is only used by the upgrade mechanism to
// access the old combined block representation. It will be dropped after the
// network transitions to eth/63.
func GetBlockByHashOld(db ethdb.Database, hash common.Hash) *types.Block {
data, _ := db.Get(append(oldBlockHashPrefix, hash[:]...))
if len(data) == 0 {
return nil
}
var block types.StorageBlock
if err := rlp.Decode(bytes.NewReader(data), &block); err != nil {
glog.V(logger.Error).Infof("invalid block RLP for hash %x: %v", hash, err)
return nil
}
return (*types.Block)(&block)
}
// returns a formatted MIP mapped key by adding prefix, canonical number and level // returns a formatted MIP mapped key by adding prefix, canonical number and level
// //
// ex. fn(98, 1000) = (prefix || 1000 || 0) // ex. fn(98, 1000) = (prefix || 1000 || 0)
@ -614,7 +591,7 @@ func WritePreimages(db ethdb.Database, number uint64, preimages map[common.Hash]
for hash, preimage := range preimages { for hash, preimage := range preimages {
if _, err := table.Get(hash.Bytes()); err != nil { if _, err := table.Get(hash.Bytes()); err != nil {
batch.Put(hash.Bytes(), preimage) batch.Put(hash.Bytes(), preimage)
hitCount += 1 hitCount++
} }
} }
preimageCounter.Inc(int64(len(preimages))) preimageCounter.Inc(int64(len(preimages)))
@ -623,7 +600,6 @@ func WritePreimages(db ethdb.Database, number uint64, preimages map[common.Hash]
if err := batch.Write(); err != nil { if err := batch.Write(); err != nil {
return fmt.Errorf("preimage write fail for block %d: %v", number, err) return fmt.Errorf("preimage write fail for block %d: %v", number, err)
} }
glog.V(logger.Debug).Infof("%d preimages in block %d, including %d new", len(preimages), number, hitCount)
} }
return nil return nil
} }

View file

@ -25,6 +25,7 @@ import (
"testing" "testing"
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/common/math"
"github.com/expanse-org/go-expanse/core/types" "github.com/expanse-org/go-expanse/core/types"
"github.com/expanse-org/go-expanse/crypto" "github.com/expanse-org/go-expanse/crypto"
"github.com/expanse-org/go-expanse/crypto/sha3" "github.com/expanse-org/go-expanse/crypto/sha3"
@ -53,11 +54,11 @@ func (d *diffTest) UnmarshalJSON(b []byte) (err error) {
return err return err
} }
d.ParentTimestamp = common.String2Big(ext.ParentTimestamp).Uint64() d.ParentTimestamp = math.MustParseUint64(ext.ParentTimestamp)
d.ParentDifficulty = common.String2Big(ext.ParentDifficulty) d.ParentDifficulty = math.MustParseBig256(ext.ParentDifficulty)
d.CurrentTimestamp = common.String2Big(ext.CurrentTimestamp).Uint64() d.CurrentTimestamp = math.MustParseUint64(ext.CurrentTimestamp)
d.CurrentBlocknumber = common.String2Big(ext.CurrentBlocknumber) d.CurrentBlocknumber = math.MustParseBig256(ext.CurrentBlocknumber)
d.CurrentDifficulty = common.String2Big(ext.CurrentDifficulty) d.CurrentDifficulty = math.MustParseBig256(ext.CurrentDifficulty)
return nil return nil
} }
@ -561,7 +562,7 @@ func TestMipmapChain(t *testing.T) {
) )
defer db.Close() defer db.Close()
genesis := WriteGenesisBlockForTesting(db, GenesisAccount{addr, big.NewInt(1000000)}) genesis := testGenesis(addr, big.NewInt(1000000)).MustCommit(db)
chain, receipts := GenerateChain(params.TestChainConfig, genesis, db, 1010, func(i int, gen *BlockGen) { chain, receipts := GenerateChain(params.TestChainConfig, genesis, db, 1010, func(i int, gen *BlockGen) {
var receipts types.Receipts var receipts types.Receipts
switch i { switch i {

106
core/gen_genesis.go Normal file
View file

@ -0,0 +1,106 @@
// generated by github.com/fjl/gencodec, do not edit.
package core
import (
"encoding/json"
"errors"
"math/big"
"github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/common/hexutil"
"github.com/expanse-org/go-expanse/common/math"
"github.com/expanse-org/go-expanse/params"
)
func (g *Genesis) MarshalJSON() ([]byte, error) {
type GenesisJSON struct {
ChainConfig *params.ChainConfig `json:"config" optional:"true"`
Nonce *math.HexOrDecimal64 `json:"nonce" optional:"true"`
Timestamp *math.HexOrDecimal64 `json:"timestamp" optional:"true"`
ParentHash *common.Hash `json:"parentHash" optional:"true"`
ExtraData hexutil.Bytes `json:"extraData" optional:"true"`
GasLimit *math.HexOrDecimal64 `json:"gasLimit"`
Difficulty *math.HexOrDecimal256 `json:"difficulty"`
Mixhash *common.Hash `json:"mixHash" optional:"true"`
Coinbase *common.Address `json:"coinbase" optional:"true"`
Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc"`
}
var enc GenesisJSON
enc.ChainConfig = g.Config
enc.Nonce = (*math.HexOrDecimal64)(&g.Nonce)
enc.Timestamp = (*math.HexOrDecimal64)(&g.Timestamp)
enc.ParentHash = &g.ParentHash
if g.ExtraData != nil {
enc.ExtraData = g.ExtraData
}
enc.GasLimit = (*math.HexOrDecimal64)(&g.GasLimit)
enc.Difficulty = (*math.HexOrDecimal256)(g.Difficulty)
enc.Mixhash = &g.Mixhash
enc.Coinbase = &g.Coinbase
if g.Alloc != nil {
enc.Alloc = make(map[common.UnprefixedAddress]GenesisAccount, len(g.Alloc))
for k, v := range g.Alloc {
enc.Alloc[common.UnprefixedAddress(k)] = v
}
}
return json.Marshal(&enc)
}
func (g *Genesis) UnmarshalJSON(input []byte) error {
type GenesisJSON struct {
ChainConfig *params.ChainConfig `json:"config" optional:"true"`
Nonce *math.HexOrDecimal64 `json:"nonce" optional:"true"`
Timestamp *math.HexOrDecimal64 `json:"timestamp" optional:"true"`
ParentHash *common.Hash `json:"parentHash" optional:"true"`
ExtraData hexutil.Bytes `json:"extraData" optional:"true"`
GasLimit *math.HexOrDecimal64 `json:"gasLimit"`
Difficulty *math.HexOrDecimal256 `json:"difficulty"`
Mixhash *common.Hash `json:"mixHash" optional:"true"`
Coinbase *common.Address `json:"coinbase" optional:"true"`
Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc"`
}
var dec GenesisJSON
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
var x Genesis
if dec.ChainConfig != nil {
x.Config = dec.ChainConfig
}
if dec.Nonce != nil {
x.Nonce = uint64(*dec.Nonce)
}
if dec.Timestamp != nil {
x.Timestamp = uint64(*dec.Timestamp)
}
if dec.ParentHash != nil {
x.ParentHash = *dec.ParentHash
}
if dec.ExtraData != nil {
x.ExtraData = dec.ExtraData
}
if dec.GasLimit == nil {
return errors.New("missing required field 'gasLimit' for Genesis")
}
x.GasLimit = uint64(*dec.GasLimit)
if dec.Difficulty == nil {
return errors.New("missing required field 'difficulty' for Genesis")
}
x.Difficulty = (*big.Int)(dec.Difficulty)
if dec.Mixhash != nil {
x.Mixhash = *dec.Mixhash
}
if dec.Coinbase != nil {
x.Coinbase = *dec.Coinbase
}
if dec.Alloc == nil {
return errors.New("missing required field 'alloc' for Genesis")
}
x.Alloc = make(GenesisAlloc, len(dec.Alloc))
for k, v := range dec.Alloc {
x.Alloc[common.Address(k)] = v
}
*g = x
return nil
}

View file

@ -0,0 +1,61 @@
// generated by github.com/fjl/gencodec, do not edit.
package core
import (
"encoding/json"
"errors"
"math/big"
"github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/common/hexutil"
"github.com/expanse-org/go-expanse/common/math"
)
func (g *GenesisAccount) MarshalJSON() ([]byte, error) {
type GenesisAccountJSON struct {
Code hexutil.Bytes `json:"code" optional:"true"`
Storage map[common.Hash]common.Hash `json:"storage" optional:"true"`
Balance *math.HexOrDecimal256 `json:"balance"`
Nonce *math.HexOrDecimal64 `json:"nonce" optional:"true"`
}
var enc GenesisAccountJSON
if g.Code != nil {
enc.Code = g.Code
}
if g.Storage != nil {
enc.Storage = g.Storage
}
enc.Balance = (*math.HexOrDecimal256)(g.Balance)
enc.Nonce = (*math.HexOrDecimal64)(&g.Nonce)
return json.Marshal(&enc)
}
func (g *GenesisAccount) UnmarshalJSON(input []byte) error {
type GenesisAccountJSON struct {
Code hexutil.Bytes `json:"code" optional:"true"`
Storage map[common.Hash]common.Hash `json:"storage" optional:"true"`
Balance *math.HexOrDecimal256 `json:"balance"`
Nonce *math.HexOrDecimal64 `json:"nonce" optional:"true"`
}
var dec GenesisAccountJSON
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
var x GenesisAccount
if dec.Code != nil {
x.Code = dec.Code
}
if dec.Storage != nil {
x.Storage = dec.Storage
}
if dec.Balance == nil {
return errors.New("missing required field 'balance' for GenesisAccount")
}
x.Balance = (*big.Int)(dec.Balance)
if dec.Nonce != nil {
x.Nonce = uint64(*dec.Nonce)
}
*g = x
return nil
}

View file

@ -17,200 +17,286 @@
package core package core
import ( import (
"compress/bzip2" "errors"
"compress/gzip"
"encoding/base64"
"encoding/json"
"fmt" "fmt"
"io"
"io/ioutil"
"math/big" "math/big"
"strings" "strings"
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/common/hexutil"
"github.com/expanse-org/go-expanse/common/math"
"github.com/expanse-org/go-expanse/core/state" "github.com/expanse-org/go-expanse/core/state"
"github.com/expanse-org/go-expanse/core/types" "github.com/expanse-org/go-expanse/core/types"
"github.com/expanse-org/go-expanse/ethdb" "github.com/expanse-org/go-expanse/ethdb"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/logger/glog"
"github.com/expanse-org/go-expanse/params" "github.com/expanse-org/go-expanse/params"
"github.com/expanse-org/go-expanse/rlp"
) )
// WriteGenesisBlock writes the genesis block to the database as block number 0 //go:generate gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go
func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, error) { //go:generate gencodec -type GenesisAccount -field-override genesisAccountMarshaling -out gen_genesis_account.go
contents, err := ioutil.ReadAll(reader)
var errGenesisNoConfig = errors.New("genesis has no chain configuration")
// Genesis specifies the header fields, state of a genesis block. It also defines hard
// fork switch-over blocks through the chain configuration.
type Genesis struct {
Config *params.ChainConfig `json:"config" optional:"true"`
Nonce uint64 `json:"nonce" optional:"true"`
Timestamp uint64 `json:"timestamp" optional:"true"`
ParentHash common.Hash `json:"parentHash" optional:"true"`
ExtraData []byte `json:"extraData" optional:"true"`
GasLimit uint64 `json:"gasLimit"`
Difficulty *big.Int `json:"difficulty"`
Mixhash common.Hash `json:"mixHash" optional:"true"`
Coinbase common.Address `json:"coinbase" optional:"true"`
Alloc GenesisAlloc `json:"alloc"`
}
// GenesisAlloc specifies the initial state that is part of the genesis block.
type GenesisAlloc map[common.Address]GenesisAccount
// GenesisAccount is an account in the state of the genesis block.
type GenesisAccount struct {
Code []byte `json:"code" optional:"true"`
Storage map[common.Hash]common.Hash `json:"storage" optional:"true"`
Balance *big.Int `json:"balance"`
Nonce uint64 `json:"nonce" optional:"true"`
}
// field type overrides for gencodec
type genesisSpecMarshaling struct {
Nonce math.HexOrDecimal64
Timestamp math.HexOrDecimal64
ExtraData hexutil.Bytes
GasLimit math.HexOrDecimal64
Difficulty *math.HexOrDecimal256
Alloc map[common.UnprefixedAddress]GenesisAccount
}
type genesisAccountMarshaling struct {
Code hexutil.Bytes
Balance *math.HexOrDecimal256
Nonce math.HexOrDecimal64
}
// GenesisMismatchError is raised when trying to overwrite an existing
// genesis block with an incompatible one.
type GenesisMismatchError struct {
Stored, New common.Hash
}
func (e *GenesisMismatchError) Error() string {
return fmt.Sprintf("wrong genesis block in database (have %x, new %x)", e.Stored[:8], e.New[:8])
}
// SetupGenesisBlock writes or updates the genesis block in db.
// The block that will be used is:
//
// genesis == nil genesis != nil
// +------------------------------------------
// db has no genesis | main-net default | genesis
// db has genesis | from DB | genesis (if compatible)
//
// The stored chain configuration will be updated if it is compatible (i.e. does not
// specify a fork block below the local head block). In case of a conflict, the
// error is a *params.ConfigCompatError and the new, unwritten config is returned.
//
// The returned chain configuration is never nil.
func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) {
if genesis != nil && genesis.Config == nil {
return params.AllProtocolChanges, common.Hash{}, errGenesisNoConfig
}
// Just commit the new block if there is no stored genesis block.
stored := GetCanonicalHash(db, 0)
if (stored == common.Hash{}) {
if genesis == nil {
log.Info("Writing default main-net genesis block")
genesis = DefaultGenesisBlock()
} else {
log.Info("Writing custom genesis block")
}
block, err := genesis.Commit(db)
return genesis.Config, block.Hash(), err
}
// Check whether the genesis block is already written.
if genesis != nil {
block, _ := genesis.ToBlock()
hash := block.Hash()
if hash != stored {
return genesis.Config, block.Hash(), &GenesisMismatchError{stored, hash}
}
}
// Get the existing chain configuration.
newcfg := genesis.configOrDefault(stored)
storedcfg, err := GetChainConfig(db, stored)
if err != nil { if err != nil {
return nil, err if err == ChainConfigNotFoundErr {
// This case happens if a genesis write was interrupted.
log.Warn("Found genesis block without chain config")
err = WriteChainConfig(db, stored, newcfg)
}
return newcfg, stored, err
}
// Special case: don't change the existing config of a non-mainnet chain if no new
// config is supplied. These chains would get AllProtocolChanges (and a compat error)
// if we just continued here.
if genesis == nil && stored != params.MainNetGenesisHash {
return storedcfg, stored, nil
} }
var genesis struct { // Check config compatibility and write the config. Compatibility errors
ChainConfig *params.ChainConfig `json:"config"` // are returned to the caller unless we're already at block zero.
Nonce string height := GetBlockNumber(db, GetHeadHeaderHash(db))
Timestamp string if height == missingNumber {
ParentHash string return newcfg, stored, fmt.Errorf("missing block number for head header hash")
ExtraData string }
GasLimit string compatErr := storedcfg.CheckCompatible(newcfg, height)
Difficulty string if compatErr != nil && height != 0 && compatErr.RewindTo != 0 {
Mixhash string return newcfg, stored, compatErr
Coinbase string }
Alloc map[string]struct { return newcfg, stored, WriteChainConfig(db, stored, newcfg)
Code string }
Storage map[string]string
Balance string func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
Nonce string switch {
case g != nil:
return g.Config
case ghash == params.MainNetGenesisHash:
return params.MainnetChainConfig
case ghash == params.TestNetGenesisHash:
return params.TestnetChainConfig
default:
return params.AllProtocolChanges
} }
} }
if err := json.Unmarshal(contents, &genesis); err != nil { // ToBlock creates the block and state of a genesis specification.
return nil, err func (g *Genesis) ToBlock() (*types.Block, *state.StateDB) {
} db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db)
// creating with empty hash always works for addr, account := range g.Alloc {
statedb, _ := state.New(common.Hash{}, chainDb) statedb.AddBalance(addr, account.Balance)
for addr, account := range genesis.Alloc { statedb.SetCode(addr, account.Code)
address := common.HexToAddress(addr) statedb.SetNonce(addr, account.Nonce)
statedb.AddBalance(address, common.String2Big(account.Balance))
statedb.SetCode(address, common.FromHex(account.Code))
statedb.SetNonce(address, common.String2Big(account.Nonce).Uint64())
for key, value := range account.Storage { for key, value := range account.Storage {
statedb.SetState(address, common.HexToHash(key), common.HexToHash(value)) statedb.SetState(addr, key, value)
} }
} }
root, stateBatch := statedb.CommitBatch(false) root := statedb.IntermediateRoot(false)
head := &types.Header{
difficulty := common.String2Big(genesis.Difficulty) Nonce: types.EncodeNonce(g.Nonce),
block := types.NewBlock(&types.Header{ Time: new(big.Int).SetUint64(g.Timestamp),
Nonce: types.EncodeNonce(common.String2Big(genesis.Nonce).Uint64()), ParentHash: g.ParentHash,
Time: common.String2Big(genesis.Timestamp), Extra: g.ExtraData,
ParentHash: common.HexToHash(genesis.ParentHash), GasLimit: new(big.Int).SetUint64(g.GasLimit),
Extra: common.FromHex(genesis.ExtraData), Difficulty: g.Difficulty,
GasLimit: common.String2Big(genesis.GasLimit), MixDigest: g.Mixhash,
Difficulty: difficulty, Coinbase: g.Coinbase,
MixDigest: common.HexToHash(genesis.Mixhash),
Coinbase: common.HexToAddress(genesis.Coinbase),
Root: root, Root: root,
}, nil, nil, nil)
if block := GetBlock(chainDb, block.Hash(), block.NumberU64()); block != nil {
glog.V(logger.Info).Infoln("Genesis block already in chain. Writing canonical number")
err := WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())
if err != nil {
return nil, err
} }
return block, nil if g.GasLimit == 0 {
head.GasLimit = params.GenesisGasLimit
}
if g.Difficulty == nil {
head.Difficulty = params.GenesisDifficulty
}
return types.NewBlock(head, nil, nil, nil), statedb
} }
if err := stateBatch.Write(); err != nil { // Commit writes the block and state of a genesis specification to the database.
// The block is committed as the canonical head block.
func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) {
block, statedb := g.ToBlock()
if _, err := statedb.CommitTo(db, false); err != nil {
return nil, fmt.Errorf("cannot write state: %v", err) return nil, fmt.Errorf("cannot write state: %v", err)
} }
if err := WriteTd(chainDb, block.Hash(), block.NumberU64(), difficulty); err != nil { if err := WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty); err != nil {
return nil, err return nil, err
} }
if err := WriteBlock(chainDb, block); err != nil { if err := WriteBlock(db, block); err != nil {
return nil, err return nil, err
} }
if err := WriteBlockReceipts(chainDb, block.Hash(), block.NumberU64(), nil); err != nil { if err := WriteBlockReceipts(db, block.Hash(), block.NumberU64(), nil); err != nil {
return nil, err return nil, err
} }
if err := WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()); err != nil { if err := WriteCanonicalHash(db, block.Hash(), block.NumberU64()); err != nil {
return nil, err return nil, err
} }
if err := WriteHeadBlockHash(chainDb, block.Hash()); err != nil { if err := WriteHeadBlockHash(db, block.Hash()); err != nil {
return nil, err return nil, err
} }
if err := WriteChainConfig(chainDb, block.Hash(), genesis.ChainConfig); err != nil { if err := WriteHeadHeaderHash(db, block.Hash()); err != nil {
return nil, err return nil, err
} }
config := g.Config
if config == nil {
config = params.AllProtocolChanges
}
return block, WriteChainConfig(db, block.Hash(), config)
}
return block, nil // MustCommit writes the genesis block and state to db, panicking on error.
// The block is committed as the canonical head block.
func (g *Genesis) MustCommit(db ethdb.Database) *types.Block {
block, err := g.Commit(db)
if err != nil {
panic(err)
}
return block
} }
// GenesisBlockForTesting creates a block in which addr has the given wei balance. // GenesisBlockForTesting creates and writes a block in which addr has the given wei balance.
// The state trie of the block is written to db. the passed db needs to contain a state root
func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block { func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block {
statedb, _ := state.New(common.Hash{}, db) g := Genesis{Alloc: GenesisAlloc{addr: {Balance: balance}}}
obj := statedb.GetOrNewStateObject(addr) return g.MustCommit(db)
obj.SetBalance(balance)
root, err := statedb.Commit(false)
if err != nil {
panic(fmt.Sprintf("cannot write state: %v", err))
}
block := types.NewBlock(&types.Header{
Difficulty: params.GenesisDifficulty,
GasLimit: params.GenesisGasLimit,
Root: root,
}, nil, nil, nil)
return block
} }
type GenesisAccount struct { // DefaultGenesisBlock returns the Ethereum main net genesis block.
Address common.Address func DefaultGenesisBlock() *Genesis {
Balance *big.Int return &Genesis{
Config: params.MainnetChainConfig,
Nonce: 66,
ExtraData: hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"),
GasLimit: 5000,
Difficulty: big.NewInt(17179869184),
Alloc: decodePrealloc(mainnetAllocData),
}
} }
func WriteGenesisBlockForTesting(db ethdb.Database, accounts ...GenesisAccount) *types.Block { // DefaultTestnetGenesisBlock returns the Ropsten network genesis block.
accountJson := "{" func DefaultTestnetGenesisBlock() *Genesis {
for i, account := range accounts { return &Genesis{
if i != 0 { Config: params.TestnetChainConfig,
accountJson += "," Nonce: 66,
ExtraData: hexutil.MustDecode("0x3535353535353535353535353535353535353535353535353535353535353535"),
GasLimit: 16777216,
Difficulty: big.NewInt(1048576),
Alloc: decodePrealloc(testnetAllocData),
} }
accountJson += fmt.Sprintf(`"0x%x":{"balance":"0x%x"}`, account.Address, account.Balance.Bytes())
}
accountJson += "}"
testGenesis := fmt.Sprintf(`{
"nonce":"0x%x",
"gasLimit":"0x%x",
"difficulty":"0x%x",
"alloc": %s
}`, types.EncodeNonce(0), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes(), accountJson)
block, _ := WriteGenesisBlock(db, strings.NewReader(testGenesis))
return block
} }
// WriteDefaultGenesisBlock assembles the official Ethereum genesis block and // DevGenesisBlock returns the 'geth --dev' genesis block.
// writes it - along with all associated state - into a chain database. func DevGenesisBlock() *Genesis {
func WriteDefaultGenesisBlock(chainDb ethdb.Database) (*types.Block, error) { return &Genesis{
return WriteGenesisBlock(chainDb, strings.NewReader(DefaultGenesisBlock())) Config: params.AllProtocolChanges,
Nonce: 42,
GasLimit: 4712388,
Difficulty: big.NewInt(131072),
Alloc: decodePrealloc(devAllocData),
}
} }
// WriteTestNetGenesisBlock assembles the test network genesis block and func decodePrealloc(data string) GenesisAlloc {
// writes it - along with all associated state - into a chain database. var p []struct{ Addr, Balance *big.Int }
func WriteTestNetGenesisBlock(chainDb ethdb.Database) (*types.Block, error) { if err := rlp.NewStream(strings.NewReader(data), 0).Decode(&p); err != nil {
return WriteGenesisBlock(chainDb, strings.NewReader(DefaultTestnetGenesisBlock())) panic(err)
} }
ga := make(GenesisAlloc, len(p))
// DefaultGenesisBlock assembles a JSON string representing the default Ethereum for _, account := range p {
// genesis block. ga[common.BigToAddress(account.Addr)] = GenesisAccount{Balance: account.Balance}
func DefaultGenesisBlock() string {
reader, err := gzip.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultGenesisBlock)))
if err != nil {
panic(fmt.Sprintf("failed to access default genesis: %v", err))
} }
blob, err := ioutil.ReadAll(reader) return ga
if err != nil {
panic(fmt.Sprintf("failed to load default genesis: %v", err))
}
return string(blob)
}
// DefaultTestnetGenesisBlock assembles a JSON string representing the default Ethereum
// test network genesis block.
func DefaultTestnetGenesisBlock() string {
reader := bzip2.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultTestnetGenesisBlock)))
blob, err := ioutil.ReadAll(reader)
if err != nil {
panic(fmt.Sprintf("failed to load default genesis: %v", err))
}
return string(blob)
}
// DevGenesisBlock assembles a JSON string representing a local dev genesis block.
func DevGenesisBlock() string {
reader := bzip2.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultDevnetGenesisBlock)))
blob, err := ioutil.ReadAll(reader)
if err != nil {
panic(fmt.Sprintf("failed to load dev genesis: %v", err))
}
return string(blob)
} }

25
core/genesis_alloc.go Normal file

File diff suppressed because one or more lines are too long

161
core/genesis_test.go Normal file
View file

@ -0,0 +1,161 @@
// 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 core
import (
"math/big"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/core/vm"
"github.com/expanse-org/go-expanse/ethdb"
"github.com/expanse-org/go-expanse/event"
"github.com/expanse-org/go-expanse/params"
"github.com/expanse-org/go-expanse/pow"
)
func TestDefaultGenesisBlock(t *testing.T) {
block, _ := DefaultGenesisBlock().ToBlock()
if block.Hash() != params.MainNetGenesisHash {
t.Errorf("wrong mainnet genesis hash, got %v, want %v", block.Hash(), params.MainNetGenesisHash)
}
block, _ = DefaultTestnetGenesisBlock().ToBlock()
if block.Hash() != params.TestNetGenesisHash {
t.Errorf("wrong testnet genesis hash, got %v, want %v", block.Hash(), params.TestNetGenesisHash)
}
}
func TestSetupGenesis(t *testing.T) {
var (
customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50")
customg = Genesis{
Config: &params.ChainConfig{HomesteadBlock: big.NewInt(3)},
Alloc: GenesisAlloc{
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
},
}
oldcustomg = customg
)
oldcustomg.Config = &params.ChainConfig{HomesteadBlock: big.NewInt(2)}
tests := []struct {
name string
fn func(ethdb.Database) (*params.ChainConfig, common.Hash, error)
wantConfig *params.ChainConfig
wantHash common.Hash
wantErr error
}{
{
name: "genesis without ChainConfig",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
return SetupGenesisBlock(db, new(Genesis))
},
wantErr: errGenesisNoConfig,
wantConfig: params.AllProtocolChanges,
},
{
name: "no block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
return SetupGenesisBlock(db, nil)
},
wantHash: params.MainNetGenesisHash,
wantConfig: params.MainnetChainConfig,
},
{
name: "mainnet block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
DefaultGenesisBlock().MustCommit(db)
return SetupGenesisBlock(db, nil)
},
wantHash: params.MainNetGenesisHash,
wantConfig: params.MainnetChainConfig,
},
{
name: "custom block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
customg.MustCommit(db)
return SetupGenesisBlock(db, nil)
},
wantHash: customghash,
wantConfig: customg.Config,
},
{
name: "custom block in DB, genesis == testnet",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
customg.MustCommit(db)
return SetupGenesisBlock(db, DefaultTestnetGenesisBlock())
},
wantErr: &GenesisMismatchError{Stored: customghash, New: params.TestNetGenesisHash},
wantHash: params.TestNetGenesisHash,
wantConfig: params.TestnetChainConfig,
},
{
name: "compatible config in DB",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
oldcustomg.MustCommit(db)
return SetupGenesisBlock(db, &customg)
},
wantHash: customghash,
wantConfig: customg.Config,
},
{
name: "incompatible config in DB",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
// Commit the 'old' genesis block with Homestead transition at #2.
// Advance to block #4, past the homestead transition block of customg.
genesis := oldcustomg.MustCommit(db)
bc, _ := NewBlockChain(db, oldcustomg.Config, pow.FakePow{}, new(event.TypeMux), vm.Config{})
bc.SetValidator(bproc{})
bc.InsertChain(makeBlockChainWithDiff(genesis, []int{2, 3, 4, 5}, 0))
bc.CurrentBlock()
// This should return a compatibility error.
return SetupGenesisBlock(db, &customg)
},
wantHash: customghash,
wantConfig: customg.Config,
wantErr: &params.ConfigCompatError{
What: "Homestead fork block",
StoredConfig: big.NewInt(2),
NewConfig: big.NewInt(3),
RewindTo: 1,
},
},
}
for _, test := range tests {
db, _ := ethdb.NewMemDatabase()
config, hash, err := test.fn(db)
// Check the return values.
if !reflect.DeepEqual(err, test.wantErr) {
spew := spew.ConfigState{DisablePointerAddresses: true, DisableCapacities: true}
t.Errorf("%s: returned error %#v, want %#v", test.name, spew.NewFormatter(err), spew.NewFormatter(test.wantErr))
}
if !reflect.DeepEqual(config, test.wantConfig) {
t.Errorf("%s:\nreturned %v\nwant %v", test.name, config, test.wantConfig)
}
if hash != test.wantHash {
t.Errorf("%s: returned hash %s, want %s", test.name, hash.Hex(), test.wantHash.Hex())
} else if err == nil {
// Check database content.
stored := GetBlock(db, test.wantHash, 0)
if stored.Hash() != test.wantHash {
t.Errorf("%s: block in DB has hash %s, want %s", test.name, stored.Hash(), test.wantHash)
}
}
}
}

View file

@ -30,8 +30,7 @@ import (
"github.com/expanse-org/go-expanse/common" "github.com/expanse-org/go-expanse/common"
"github.com/expanse-org/go-expanse/core/types" "github.com/expanse-org/go-expanse/core/types"
"github.com/expanse-org/go-expanse/ethdb" "github.com/expanse-org/go-expanse/ethdb"
"github.com/expanse-org/go-expanse/logger" "github.com/expanse-org/go-expanse/log"
"github.com/expanse-org/go-expanse/logger/glog"
"github.com/expanse-org/go-expanse/params" "github.com/expanse-org/go-expanse/params"
"github.com/expanse-org/go-expanse/pow" "github.com/expanse-org/go-expanse/pow"
"github.com/hashicorp/golang-lru" "github.com/hashicorp/golang-lru"
@ -98,12 +97,7 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, getValid
hc.genesisHeader = hc.GetHeaderByNumber(0) hc.genesisHeader = hc.GetHeaderByNumber(0)
if hc.genesisHeader == nil { if hc.genesisHeader == nil {
genesisBlock, err := WriteDefaultGenesisBlock(chainDb) return nil, ErrNoGenesis
if err != nil {
return nil, err
}
glog.V(logger.Info).Infoln("WARNING: Wrote default expanse genesis block")
hc.genesisHeader = genesisBlock.Header()
} }
hc.currentHeader = hc.genesisHeader hc.currentHeader = hc.genesisHeader
@ -155,12 +149,11 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
// Irrelevant of the canonical status, write the td and header to the database // Irrelevant of the canonical status, write the td and header to the database
if err := hc.WriteTd(hash, number, externTd); err != nil { if err := hc.WriteTd(hash, number, externTd); err != nil {
glog.Fatalf("failed to write header total difficulty: %v", err) log.Crit("Failed to write header total difficulty", "err", err)
} }
if err := WriteHeader(hc.chainDb, header); err != nil { if err := WriteHeader(hc.chainDb, header); err != nil {
glog.Fatalf("failed to write header contents: %v", err) log.Crit("Failed to write header content", "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
// Second clause in the if statement reduces the vulnerability to selfish mining. // Second clause in the if statement reduces the vulnerability to selfish mining.
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
@ -186,15 +179,13 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
headNumber = headHeader.Number.Uint64() - 1 headNumber = headHeader.Number.Uint64() - 1
headHeader = hc.GetHeader(headHash, headNumber) headHeader = hc.GetHeader(headHash, headNumber)
} }
// Extend the canonical chain with the new header // Extend the canonical chain with the new header
if err := WriteCanonicalHash(hc.chainDb, hash, number); err != nil { if err := WriteCanonicalHash(hc.chainDb, hash, number); err != nil {
glog.Fatalf("failed to insert header number: %v", err) log.Crit("Failed to insert header number", "err", err)
} }
if err := WriteHeadHeaderHash(hc.chainDb, hash); err != nil { if err := WriteHeadHeaderHash(hc.chainDb, hash); err != nil {
glog.Fatalf("failed to insert head header hash: %v", err) log.Crit("Failed to insert head header hash", "err", err)
} }
hc.currentHeaderHash, hc.currentHeader = hash, types.CopyHeader(header) hc.currentHeaderHash, hc.currentHeader = hash, types.CopyHeader(header)
status = CanonStatTy status = CanonStatTy
@ -223,21 +214,19 @@ type WhCallback func(*types.Header) error
// should be done or not. The reason behind the optional check is because some // should be done or not. The reason behind the optional check is because some
// of the header retrieval mechanisms already need to verfy nonces, as well as // of the header retrieval mechanisms already need to verfy 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 (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, checkFreq int, writeHeader WhCallback) (int, error) {
func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int) (int, error) {
// Do a sanity check that the provided chain is actually ordered and linked // Do a sanity check that the provided chain is actually ordered and linked
for i := 1; i < len(chain); i++ { for i := 1; i < len(chain); i++ {
if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 || chain[i].ParentHash != chain[i-1].Hash() { if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+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 header insert", "number", chain[i].Number, "hash", chain[i].Hash(),
i-1, chain[i-1].Number.Uint64(), chain[i-1].Hash().Bytes()[:4], i, chain[i].Number.Uint64(), 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].Number,
return 0, failure chain[i-1].Hash().Bytes()[:4], i, chain[i].Number, chain[i].Hash().Bytes()[:4], chain[i].ParentHash[:4])
} }
} }
// Collect some import statistics to report on
stats := struct{ processed, ignored int }{}
start := time.Now()
// Generate the list of headers that should be POW verified // Generate the list of headers that should be POW verified
verify := make([]bool, len(chain)) verify := make([]bool, len(chain))
@ -313,11 +302,18 @@ func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, checkFreq int, w
} }
} }
} }
return 0, nil
}
func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, writeHeader WhCallback, start time.Time) (int, error) {
// Collect some import statistics to report on
stats := struct{ processed, ignored int }{}
// All headers passed verification, import them into the database // All headers passed verification, import them into the database
for i, header := range chain { for i, header := range chain {
// Short circuit insertion if shutting down // Short circuit insertion if shutting down
if hc.procInterrupt() { if hc.procInterrupt() {
glog.V(logger.Debug).Infoln("premature abort during header chain processing") log.Debug("Premature abort during headers processing")
break break
} }
hash := header.Hash() hash := header.Hash()
@ -333,13 +329,9 @@ func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, checkFreq int, w
stats.processed++ stats.processed++
} }
// 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 := chain[0], chain[len(chain)-1] last := chain[len(chain)-1]
log.Info("Imported new block headers", "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 headers%s in %9v. #%v [%x… / %x…]", stats.processed, ignored, common.PrettyDuration(time.Since(start)), last.Number, first.Hash().Bytes()[:4], last.Hash().Bytes()[:4])
return 0, nil return 0, nil
} }
@ -360,7 +352,7 @@ func (hc *HeaderChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []co
break break
} }
chain = append(chain, next) chain = append(chain, next)
if header.Number.Cmp(common.Big0) == 0 { if header.Number.Sign() == 0 {
break break
} }
} }
@ -446,7 +438,7 @@ func (hc *HeaderChain) CurrentHeader() *types.Header {
// SetCurrentHeader sets the current head header of the canonical chain. // SetCurrentHeader sets the current head header of the canonical chain.
func (hc *HeaderChain) SetCurrentHeader(head *types.Header) { func (hc *HeaderChain) SetCurrentHeader(head *types.Header) {
if err := WriteHeadHeaderHash(hc.chainDb, head.Hash()); err != nil { if err := WriteHeadHeaderHash(hc.chainDb, head.Hash()); err != nil {
glog.Fatalf("failed to insert head header hash: %v", err) log.Crit("Failed to insert head header hash", "err", err)
} }
hc.currentHeader = head hc.currentHeader = head
hc.currentHeaderHash = head.Hash() hc.currentHeaderHash = head.Hash()
@ -489,7 +481,7 @@ func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) {
hc.currentHeaderHash = hc.currentHeader.Hash() hc.currentHeaderHash = hc.currentHeader.Hash()
if err := WriteHeadHeaderHash(hc.chainDb, hc.currentHeaderHash); err != nil { if err := WriteHeadHeaderHash(hc.chainDb, hc.currentHeaderHash); err != nil {
glog.Fatalf("failed to reset head header hash: %v", err) log.Crit("Failed to reset head header hash", "err", err)
} }
} }

85
core/mkalloc.go Normal file
View file

@ -0,0 +1,85 @@
// 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/>.
// +build none
/*
The mkalloc tool creates the genesis allocation constants in genesis_alloc.go
It outputs a const declaration that contains an RLP-encoded list of (address, balance) tuples.
go run mkalloc.go genesis.json
*/
package main
import (
"encoding/json"
"fmt"
"math/big"
"os"
"sort"
"strconv"
"github.com/expanse-org/go-expanse/core"
"github.com/expanse-org/go-expanse/rlp"
)
type allocItem struct{ Addr, Balance *big.Int }
type allocList []allocItem
func (a allocList) Len() int { return len(a) }
func (a allocList) Less(i, j int) bool { return a[i].Addr.Cmp(a[j].Addr) < 0 }
func (a allocList) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func makelist(g *core.Genesis) allocList {
a := make(allocList, 0, len(g.Alloc))
for addr, account := range g.Alloc {
if len(account.Storage) > 0 || len(account.Code) > 0 || account.Nonce != 0 {
panic(fmt.Sprintf("can't encode account %x", addr))
}
a = append(a, allocItem{addr.Big(), account.Balance})
}
sort.Sort(a)
return a
}
func makealloc(g *core.Genesis) string {
a := makelist(g)
data, err := rlp.EncodeToBytes(a)
if err != nil {
panic(err)
}
return strconv.QuoteToASCII(string(data))
}
func main() {
if len(os.Args) != 2 {
fmt.Fprintln(os.Stderr, "Usage: mkalloc genesis.json")
os.Exit(1)
}
g := new(core.Genesis)
file, err := os.Open(os.Args[1])
if err != nil {
panic(err)
}
if err := json.NewDecoder(file).Decode(g); err != nil {
panic(err)
}
fmt.Println("const allocData =", makealloc(g))
}

Some files were not shown because too many files have changed in this diff Show more