aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Godeps/Godeps.json2
-rw-r--r--Godeps/_workspace/src/github.com/rjeczalik/notify/.travis.yml3
-rw-r--r--Godeps/_workspace/src/github.com/rjeczalik/notify/appveyor.yml1
-rw-r--r--Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_fsevents.go2
-rw-r--r--Makefile53
-rw-r--r--README.md2
-rw-r--r--build/ci.go32
-rwxr-xr-xbuild/flags.sh22
-rw-r--r--containers/docker/develop-alpine/Dockerfile11
-rw-r--r--containers/docker/master-alpine/Dockerfile10
-rw-r--r--core/vm/contracts.go4
-rw-r--r--eth/downloader/downloader.go26
-rw-r--r--eth/downloader/downloader_test.go17
-rw-r--r--eth/downloader/peer.go13
-rw-r--r--eth/handler.go31
-rw-r--r--eth/peer.go34
-rw-r--r--eth/sync.go8
-rw-r--r--internal/web3ext/web3ext.go5
-rw-r--r--node/api.go16
-rw-r--r--p2p/dial.go5
-rw-r--r--p2p/server.go20
-rw-r--r--p2p/server_test.go2
22 files changed, 197 insertions, 122 deletions
diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json
index edfa8ad1e..6c4e10412 100644
--- a/Godeps/Godeps.json
+++ b/Godeps/Godeps.json
@@ -112,7 +112,7 @@
},
{
"ImportPath": "github.com/rjeczalik/notify",
- "Rev": "5dd6205716539662f8f14ab513552b41eab69d5d"
+ "Rev": "f627deca7a510d96f0ef9388f2d0e8b16d21f87f"
},
{
"ImportPath": "github.com/robertkrimen/otto",
diff --git a/Godeps/_workspace/src/github.com/rjeczalik/notify/.travis.yml b/Godeps/_workspace/src/github.com/rjeczalik/notify/.travis.yml
index 4f1f5f25e..c92863d50 100644
--- a/Godeps/_workspace/src/github.com/rjeczalik/notify/.travis.yml
+++ b/Godeps/_workspace/src/github.com/rjeczalik/notify/.travis.yml
@@ -21,10 +21,9 @@ env:
- PATH=$HOME/bin:$PATH
install:
- - go get golang.org/x/tools/cmd/vet
- go get -t -v ./...
script:
- - go tool vet -all .
+ - "(go version | grep -q 1.4) || go tool vet -all ."
- go install $GOFLAGS ./...
- go test -v -race $GOFLAGS ./...
diff --git a/Godeps/_workspace/src/github.com/rjeczalik/notify/appveyor.yml b/Godeps/_workspace/src/github.com/rjeczalik/notify/appveyor.yml
index 16d09ac3b..8e762d05c 100644
--- a/Godeps/_workspace/src/github.com/rjeczalik/notify/appveyor.yml
+++ b/Godeps/_workspace/src/github.com/rjeczalik/notify/appveyor.yml
@@ -11,7 +11,6 @@ environment:
install:
- go version
- - go get golang.org/x/tools/cmd/vet
- go get -v -t ./...
build_script:
diff --git a/Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_fsevents.go b/Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_fsevents.go
index 54334912e..9062c17c7 100644
--- a/Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_fsevents.go
+++ b/Godeps/_workspace/src/github.com/rjeczalik/notify/watcher_fsevents.go
@@ -133,7 +133,7 @@ func (w *watch) Dispatch(ev []FSEvent) {
ev[i].Flags, ev[i].Path, i, ev[i].ID, len(ev))
if ev[i].Flags&failure != 0 {
// TODO(rjeczalik): missing error handling
- panic("unhandled error: " + Event(ev[i].Flags).String())
+ continue
}
if !strings.HasPrefix(ev[i].Path, w.path) {
continue
diff --git a/Makefile b/Makefile
index 148cb5758..4bcdab299 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
# with Go source code. If you know what GOPATH is then you probably
# don't need to bother with make.
-.PHONY: geth geth-cross evm all test xgo clean
+.PHONY: geth geth-cross evm all test clean
.PHONY: geth-linux geth-linux-386 geth-linux-amd64
.PHONY: geth-linux-arm geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-arm64
.PHONY: geth-darwin geth-darwin-386 geth-darwin-amd64
@@ -33,9 +33,6 @@ clean:
# Cross Compilation Targets (xgo)
-xgo:
- build/env.sh go get github.com/karalabe/xgo
-
geth-cross: geth-linux geth-darwin geth-windows geth-android geth-ios
@echo "Full cross compilation done:"
@ls -ld $(GOBIN)/geth-*
@@ -44,13 +41,13 @@ geth-linux: geth-linux-386 geth-linux-amd64 geth-linux-arm
@echo "Linux cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-*
-geth-linux-386: xgo
- build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/386 -v $(shell build/flags.sh) ./cmd/geth
+geth-linux-386:
+ build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/386 -v ./cmd/geth
@echo "Linux 386 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep 386
-geth-linux-amd64: xgo
- build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/amd64 -v $(shell build/flags.sh) ./cmd/geth
+geth-linux-amd64:
+ build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/amd64 -v ./cmd/geth
@echo "Linux amd64 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep amd64
@@ -58,23 +55,23 @@ geth-linux-arm: geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-ar
@echo "Linux ARM cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm
-geth-linux-arm-5: xgo
- build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-5 -v $(shell build/flags.sh) ./cmd/geth
+geth-linux-arm-5:
+ build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-5 -v ./cmd/geth
@echo "Linux ARMv5 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm-5
-geth-linux-arm-6: xgo
- build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-6 -v $(shell build/flags.sh) ./cmd/geth
+geth-linux-arm-6:
+ build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-6 -v ./cmd/geth
@echo "Linux ARMv6 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm-6
-geth-linux-arm-7: xgo
- build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-7 -v $(shell build/flags.sh) ./cmd/geth
+geth-linux-arm-7:
+ build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-7 -v ./cmd/geth
@echo "Linux ARMv7 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm-7
-geth-linux-arm64: xgo
- build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm64 -v $(shell build/flags.sh) ./cmd/geth
+geth-linux-arm64:
+ build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm64 -v ./cmd/geth
@echo "Linux ARM64 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm64
@@ -82,13 +79,13 @@ geth-darwin: geth-darwin-386 geth-darwin-amd64
@echo "Darwin cross compilation done:"
@ls -ld $(GOBIN)/geth-darwin-*
-geth-darwin-386: xgo
- build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=darwin/386 -v $(shell build/flags.sh) ./cmd/geth
+geth-darwin-386:
+ build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=darwin/386 -v ./cmd/geth
@echo "Darwin 386 cross compilation done:"
@ls -ld $(GOBIN)/geth-darwin-* | grep 386
-geth-darwin-amd64: xgo
- build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=darwin/amd64 -v $(shell build/flags.sh) ./cmd/geth
+geth-darwin-amd64:
+ build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=darwin/amd64 -v ./cmd/geth
@echo "Darwin amd64 cross compilation done:"
@ls -ld $(GOBIN)/geth-darwin-* | grep amd64
@@ -96,22 +93,22 @@ geth-windows: geth-windows-386 geth-windows-amd64
@echo "Windows cross compilation done:"
@ls -ld $(GOBIN)/geth-windows-*
-geth-windows-386: xgo
- build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=windows/386 -v $(shell build/flags.sh) ./cmd/geth
+geth-windows-386:
+ build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=windows/386 -v ./cmd/geth
@echo "Windows 386 cross compilation done:"
@ls -ld $(GOBIN)/geth-windows-* | grep 386
-geth-windows-amd64: xgo
- build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=windows/amd64 -v $(shell build/flags.sh) ./cmd/geth
+geth-windows-amd64:
+ build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=windows/amd64 -v ./cmd/geth
@echo "Windows amd64 cross compilation done:"
@ls -ld $(GOBIN)/geth-windows-* | grep amd64
-geth-android: xgo
- build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=android-21/aar -v $(shell build/flags.sh) ./cmd/geth
+geth-android:
+ build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=android-21/aar -v ./cmd/geth
@echo "Android cross compilation done:"
@ls -ld $(GOBIN)/geth-android-*
-geth-ios: xgo
- build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=ios-7.0/framework -v $(shell build/flags.sh) ./cmd/geth
+geth-ios:
+ build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=ios-7.0/framework -v ./cmd/geth
@echo "iOS framework cross compilation done:"
@ls -ld $(GOBIN)/geth-ios-*
diff --git a/README.md b/README.md
index 60ce814ba..94474dce3 100644
--- a/README.md
+++ b/README.md
@@ -50,7 +50,7 @@ The go-ethereum project comes with several wrappers/executables found in the `cm
| `abigen` | Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) with expanded functionality if the contract bytecode is also available. However it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts) wiki page for details. |
| `bootnode` | Stripped down version of our Ethereum client implementation that only takes part in the network node discovery protocol, but does not run any of the higher level application protocols. It can be used as a lightweight bootstrap node to aid in finding peers in private networks. |
| `disasm` | Bytecode disassembler to convert EVM (Ethereum Virtual Machine) bytecode into more user friendly assembly-like opcodes (e.g. `echo "6001" | disasm`). For details on the individual opcodes, please see pages 22-30 of the [Ethereum Yellow Paper](http://gavwood.com/paper.pdf). |
-| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow insolated, fine graned debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug`). |
+| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow insolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug`). |
| `gethrpctest` | Developer utility tool to support our [ethereum/rpc-test](https://github.com/ethereum/rpc-tests) test suite which validates baseline conformity to the [Ethereum JSON RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC) specs. Please see the [test suite's readme](https://github.com/ethereum/rpc-tests/blob/master/README.md) for details. |
| `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://github.com/ethereum/wiki/wiki/RLP)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). |
diff --git a/build/ci.go b/build/ci.go
index 33d97c182..3011a6976 100644
--- a/build/ci.go
+++ b/build/ci.go
@@ -28,6 +28,7 @@ Available commands are:
archive [ -type zip|tar ] -- archives build artefacts
importkeys -- imports signing keys from env
debsrc [ -sign key-id ] [ -upload dest ] -- creates a debian source package
+ xgo [ options ] -- cross builds according to options
For all commands, -n prevents execution of external programs (dry run mode).
@@ -121,6 +122,8 @@ func main() {
doDebianSource(os.Args[2:])
case "travis-debsrc":
doTravisDebianSource(os.Args[2:])
+ case "xgo":
+ doXgo(os.Args[2:])
default:
log.Fatal("unknown command ", os.Args[1])
}
@@ -463,3 +466,32 @@ func stageDebianSource(tmpdir string, meta debMetadata) (pkgdir string) {
return pkgdir
}
+
+// Cross compilation
+
+func doXgo(cmdline []string) {
+ // Make sure xgo is available for cross compilation
+ gogetxgo := goTool("get", "github.com/karalabe/xgo")
+ build.MustRun(gogetxgo)
+
+ // Execute the actual cross compilation
+ pkg := cmdline[len(cmdline)-1]
+ args := append(cmdline[:len(cmdline)-1], makeBuildFlags("")...)
+
+ build.MustRun(xgoTool(append(args, pkg)...))
+}
+
+func xgoTool(args ...string) *exec.Cmd {
+ cmd := exec.Command(filepath.Join(GOBIN, "xgo"), args...)
+ cmd.Env = []string{
+ "GOPATH=" + build.GOPATH(),
+ "GOBIN=" + GOBIN,
+ }
+ for _, e := range os.Environ() {
+ if strings.HasPrefix(e, "GOPATH=") || strings.HasPrefix(e, "GOBIN=") {
+ continue
+ }
+ cmd.Env = append(cmd.Env, e)
+ }
+ return cmd
+}
diff --git a/build/flags.sh b/build/flags.sh
deleted file mode 100755
index e021dbad4..000000000
--- a/build/flags.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/sh
-
-set -e
-
-if [ ! -f "build/env.sh" ]; then
- echo "$0 must be run from the root of the repository."
- exit 2
-fi
-
-# Since Go 1.5, the separator char for link time assignments
-# is '=' and using ' ' prints a warning. However, Go < 1.5 does
-# not support using '='.
-sep=$(go version | awk '{ if ($3 >= "go1.5" || index($3, "devel")) print "="; else print " "; }' -)
-
-# set gitCommit when running from a Git checkout.
-if [ -f ".git/HEAD" ]; then
- echo "-ldflags '-X main.gitCommit$sep$(git rev-parse HEAD)'"
-fi
-
-if [ ! -z "$GO_OPENCL" ]; then
- echo "-tags opencl"
-fi
diff --git a/containers/docker/develop-alpine/Dockerfile b/containers/docker/develop-alpine/Dockerfile
index 70aee9f0f..f3247d178 100644
--- a/containers/docker/develop-alpine/Dockerfile
+++ b/containers/docker/develop-alpine/Dockerfile
@@ -1,12 +1,11 @@
FROM alpine:3.4
RUN \
- apk add --update go git make gcc musl-dev && \
- git clone https://github.com/ethereum/go-ethereum && \
- (cd go-ethereum && git checkout develop) && \
- (cd go-ethereum && make geth) && \
- cp go-ethereum/build/bin/geth /geth && \
- apk del go git make gcc musl-dev && \
+ apk add --update go git make gcc musl-dev && \
+ git clone --depth 1 --branch develop https://github.com/ethereum/go-ethereum && \
+ (cd go-ethereum && make geth) && \
+ cp go-ethereum/build/bin/geth /geth && \
+ apk del go git make gcc musl-dev && \
rm -rf /go-ethereum && rm -rf /var/cache/apk/*
EXPOSE 8545
diff --git a/containers/docker/master-alpine/Dockerfile b/containers/docker/master-alpine/Dockerfile
index ffccd43e2..3393c4337 100644
--- a/containers/docker/master-alpine/Dockerfile
+++ b/containers/docker/master-alpine/Dockerfile
@@ -1,11 +1,11 @@
FROM alpine:3.4
RUN \
- apk add --update go git make gcc musl-dev && \
- git clone https://github.com/ethereum/go-ethereum && \
- (cd go-ethereum && make geth) && \
- cp go-ethereum/build/bin/geth /geth && \
- apk del go git make gcc musl-dev && \
+ apk add --update go git make gcc musl-dev && \
+ git clone --depth 1 https://github.com/ethereum/go-ethereum && \
+ (cd go-ethereum && make geth) && \
+ cp go-ethereum/build/bin/geth /geth && \
+ apk del go git make gcc musl-dev && \
rm -rf /go-ethereum && rm -rf /var/cache/apk/*
EXPOSE 8545
diff --git a/core/vm/contracts.go b/core/vm/contracts.go
index 5cc9f903b..b45f14724 100644
--- a/core/vm/contracts.go
+++ b/core/vm/contracts.go
@@ -95,7 +95,7 @@ func ecrecoverFunc(in []byte) []byte {
// tighter sig s values in homestead only apply to tx sigs
if !crypto.ValidateSignatureValues(v, r, s, false) {
- glog.V(logger.Debug).Infof("EC RECOVER FAIL: v, r or s value invalid")
+ glog.V(logger.Detail).Infof("ECRECOVER error: v, r or s value invalid")
return nil
}
@@ -106,7 +106,7 @@ func ecrecoverFunc(in []byte) []byte {
pubKey, err := crypto.Ecrecover(in[:32], rsv)
// make sure the public key is a valid one
if err != nil {
- glog.V(logger.Error).Infof("EC RECOVER FAIL: ", err)
+ glog.V(logger.Detail).Infoln("ECRECOVER error: ", err)
return nil
}
diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go
index aee21122a..b6b9d54f0 100644
--- a/eth/downloader/downloader.go
+++ b/eth/downloader/downloader.go
@@ -147,8 +147,10 @@ type Downloader struct {
stateWakeCh chan bool // [eth/63] Channel to signal the state fetcher of new tasks
headerProcCh chan []*types.Header // [eth/62] Channel to feed the header processor new tasks
+ // Cancellation and termination
+ cancelPeer string // Identifier of the peer currently being used as the master (cancel on drop)
cancelCh chan struct{} // Channel to cancel mid-flight syncs
- cancelLock sync.RWMutex // Lock to protect the cancel channel in delivers
+ cancelLock sync.RWMutex // Lock to protect the cancel channel and peer in delivers
quitCh chan struct{} // Quit channel to signal termination
quitLock sync.RWMutex // Lock to prevent double closes
@@ -236,12 +238,12 @@ func (d *Downloader) Synchronising() bool {
// RegisterPeer injects a new download peer into the set of block source to be
// used for fetching hashes and blocks from.
-func (d *Downloader) RegisterPeer(id string, version int, head common.Hash,
+func (d *Downloader) RegisterPeer(id string, version int, currentHead currentHeadRetrievalFn,
getRelHeaders relativeHeaderFetcherFn, getAbsHeaders absoluteHeaderFetcherFn, getBlockBodies blockBodyFetcherFn,
getReceipts receiptFetcherFn, getNodeData stateFetcherFn) error {
glog.V(logger.Detail).Infoln("Registering peer", id)
- if err := d.peers.Register(newPeer(id, version, head, getRelHeaders, getAbsHeaders, getBlockBodies, getReceipts, getNodeData)); err != nil {
+ if err := d.peers.Register(newPeer(id, version, currentHead, getRelHeaders, getAbsHeaders, getBlockBodies, getReceipts, getNodeData)); err != nil {
glog.V(logger.Error).Infoln("Register failed:", err)
return err
}
@@ -254,12 +256,22 @@ func (d *Downloader) RegisterPeer(id string, version int, head common.Hash,
// the specified peer. An effort is also made to return any pending fetches into
// the queue.
func (d *Downloader) UnregisterPeer(id string) error {
+ // Unregister the peer from the active peer set and revoke any fetch tasks
glog.V(logger.Detail).Infoln("Unregistering peer", id)
if err := d.peers.Unregister(id); err != nil {
glog.V(logger.Error).Infoln("Unregister failed:", err)
return err
}
d.queue.Revoke(id)
+
+ // If this peer was the master peer, abort sync immediately
+ d.cancelLock.RLock()
+ master := id == d.cancelPeer
+ d.cancelLock.RUnlock()
+
+ if master {
+ d.cancel()
+ }
return nil
}
@@ -332,9 +344,10 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode
empty = true
}
}
- // Create cancel channel for aborting mid-flight
+ // Create cancel channel for aborting mid-flight and mark the master peer
d.cancelLock.Lock()
d.cancelCh = make(chan struct{})
+ d.cancelPeer = id
d.cancelLock.Unlock()
defer d.cancel() // No matter what, we can't leave the cancel channel open
@@ -501,7 +514,8 @@ func (d *Downloader) fetchHeight(p *peer) (*types.Header, error) {
glog.V(logger.Debug).Infof("%v: retrieving remote chain height", p)
// Request the advertised remote head block and wait for the response
- go p.getRelHeaders(p.head, 1, 0, false)
+ head, _ := p.currentHead()
+ go p.getRelHeaders(head, 1, 0, false)
timeout := time.After(d.requestTTL())
for {
@@ -541,7 +555,7 @@ func (d *Downloader) fetchHeight(p *peer) (*types.Header, error) {
// In the rare scenario when we ended up on a long reorganisation (i.e. none of
// the head links match), we do a binary search to find the common ancestor.
func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
- glog.V(logger.Debug).Infof("%v: looking for common ancestor", p)
+ glog.V(logger.Debug).Infof("%v: looking for common ancestor (remote height %d)", p, height)
// Figure out the valid ancestor range to prevent rewrite attacks
floor, ceil := int64(-1), d.headHeader().Number.Uint64()
diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go
index 4ca28091c..a2efc7469 100644
--- a/eth/downloader/downloader_test.go
+++ b/eth/downloader/downloader_test.go
@@ -400,11 +400,11 @@ func (dl *downloadTester) newSlowPeer(id string, version int, hashes []common.Ha
var err error
switch version {
case 62:
- err = dl.downloader.RegisterPeer(id, version, hashes[0], dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), nil, nil)
+ err = dl.downloader.RegisterPeer(id, version, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), nil, nil)
case 63:
- err = dl.downloader.RegisterPeer(id, version, hashes[0], dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay))
+ err = dl.downloader.RegisterPeer(id, version, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay))
case 64:
- err = dl.downloader.RegisterPeer(id, version, hashes[0], dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay))
+ err = dl.downloader.RegisterPeer(id, version, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay))
}
if err == nil {
// Assign the owned hashes, headers and blocks to the peer (deep copy)
@@ -463,6 +463,17 @@ func (dl *downloadTester) dropPeer(id string) {
dl.downloader.UnregisterPeer(id)
}
+// peerCurrentHeadFn constructs a function to retrieve a peer's current head hash
+// and total difficulty.
+func (dl *downloadTester) peerCurrentHeadFn(id string) func() (common.Hash, *big.Int) {
+ return func() (common.Hash, *big.Int) {
+ dl.lock.RLock()
+ defer dl.lock.RUnlock()
+
+ return dl.peerHashes[id][0], nil
+ }
+}
+
// peerGetRelHeadersFn constructs a GetBlockHeaders function based on a hashed
// origin; associated with a particular peer in the download tester. The returned
// function can be used to retrieve batches of headers from the particular peer.
diff --git a/eth/downloader/peer.go b/eth/downloader/peer.go
index c2b7a52d0..b0bfc66c8 100644
--- a/eth/downloader/peer.go
+++ b/eth/downloader/peer.go
@@ -23,6 +23,7 @@ import (
"errors"
"fmt"
"math"
+ "math/big"
"sort"
"strings"
"sync"
@@ -37,6 +38,9 @@ const (
measurementImpact = 0.1 // The impact a single measurement has on a peer's final throughput value.
)
+// Head hash and total difficulty retriever for
+type currentHeadRetrievalFn func() (common.Hash, *big.Int)
+
// Block header and body fetchers belonging to eth/62 and above
type relativeHeaderFetcherFn func(common.Hash, int, int, bool) error
type absoluteHeaderFetcherFn func(uint64, int, int, bool) error
@@ -52,8 +56,7 @@ var (
// peer represents an active peer from which hashes and blocks are retrieved.
type peer struct {
- id string // Unique identifier of the peer
- head common.Hash // Hash of the peers latest known block
+ id string // Unique identifier of the peer
headerIdle int32 // Current header activity state of the peer (idle = 0, active = 1)
blockIdle int32 // Current block activity state of the peer (idle = 0, active = 1)
@@ -74,6 +77,8 @@ type peer struct {
lacking map[common.Hash]struct{} // Set of hashes not to request (didn't have previously)
+ currentHead currentHeadRetrievalFn // Method to fetch the currently known head of the peer
+
getRelHeaders relativeHeaderFetcherFn // [eth/62] Method to retrieve a batch of headers from an origin hash
getAbsHeaders absoluteHeaderFetcherFn // [eth/62] Method to retrieve a batch of headers from an absolute position
getBlockBodies blockBodyFetcherFn // [eth/62] Method to retrieve a batch of block bodies
@@ -87,14 +92,14 @@ type peer struct {
// newPeer create a new downloader peer, with specific hash and block retrieval
// mechanisms.
-func newPeer(id string, version int, head common.Hash,
+func newPeer(id string, version int, currentHead currentHeadRetrievalFn,
getRelHeaders relativeHeaderFetcherFn, getAbsHeaders absoluteHeaderFetcherFn, getBlockBodies blockBodyFetcherFn,
getReceipts receiptFetcherFn, getNodeData stateFetcherFn) *peer {
return &peer{
id: id,
- head: head,
lacking: make(map[common.Hash]struct{}),
+ currentHead: currentHead,
getRelHeaders: getRelHeaders,
getAbsHeaders: getAbsHeaders,
getBlockBodies: getBlockBodies,
diff --git a/eth/handler.go b/eth/handler.go
index 9ad430976..886d89fd1 100644
--- a/eth/handler.go
+++ b/eth/handler.go
@@ -272,11 +272,7 @@ func (pm *ProtocolManager) handle(p *peer) error {
defer pm.removePeer(p.id)
// Register the peer in the downloader. If the downloader considers it banned, we disconnect
- err := pm.downloader.RegisterPeer(p.id, p.version, p.Head(),
- p.RequestHeadersByHash, p.RequestHeadersByNumber,
- p.RequestBodies, p.RequestReceipts, p.RequestNodeData,
- )
- if err != nil {
+ if err := pm.downloader.RegisterPeer(p.id, p.version, p.Head, p.RequestHeadersByHash, p.RequestHeadersByNumber, p.RequestBodies, p.RequestReceipts, p.RequestNodeData); err != nil {
return err
}
// Propagate existing transactions. new transactions appearing
@@ -413,7 +409,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
// If we already have a DAO header, we can check the peer's TD against it. If
// the peer's ahead of this, it too must have a reply to the DAO check
if daoHeader := pm.blockchain.GetHeaderByNumber(pm.chainconfig.DAOForkBlock.Uint64()); daoHeader != nil {
- if p.Td().Cmp(pm.blockchain.GetTd(daoHeader.Hash(), daoHeader.Number.Uint64())) >= 0 {
+ if _, td := p.Head(); td.Cmp(pm.blockchain.GetTd(daoHeader.Hash(), daoHeader.Number.Uint64())) >= 0 {
verifyDAO = false
}
}
@@ -440,6 +436,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
return err
}
glog.V(logger.Debug).Infof("%v: verified to be on the same side of the DAO fork", p)
+ return nil
}
// Irrelevant of the fork checks, send the header to the fetcher just in case
headers = pm.fetcher.FilterHeaders(headers, time.Now())
@@ -619,7 +616,6 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
// Mark the hashes as present at the remote node
for _, block := range announces {
p.MarkBlock(block.Hash)
- p.SetHead(block.Hash)
}
// Schedule all the unknown hashes for retrieval
unknown := make([]announce, 0, len(announces))
@@ -646,16 +642,23 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
// Mark the peer as owning the block and schedule it for import
p.MarkBlock(request.Block.Hash())
- p.SetHead(request.Block.Hash())
-
pm.fetcher.Enqueue(p.id, request.Block)
- // Update the peers total difficulty if needed, schedule a download if gapped
- if request.TD.Cmp(p.Td()) > 0 {
- p.SetTd(request.TD)
+ // Assuming the block is importable by the peer, but possibly not yet done so,
+ // calculate the head hash and TD that the peer truly must have.
+ var (
+ trueHead = request.Block.ParentHash()
+ trueTD = new(big.Int).Sub(request.TD, request.Block.Difficulty())
+ )
+ // Update the peers total difficulty if better than the previous
+ if _, td := p.Head(); trueTD.Cmp(td) > 0 {
+ p.SetHead(trueHead, trueTD)
+
+ // Schedule a sync if above ours. Note, this will not fire a sync for a gap of
+ // a singe block (as the true TD is below the propagated block), however this
+ // scenario should easily be covered by the fetcher.
currentBlock := pm.blockchain.CurrentBlock()
- td := pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
- if request.TD.Cmp(new(big.Int).Add(td, request.Block.Difficulty())) > 0 {
+ if trueTD.Cmp(pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64())) > 0 {
go pm.synchronise(p)
}
}
diff --git a/eth/peer.go b/eth/peer.go
index c8c207ecb..aa85631ea 100644
--- a/eth/peer.go
+++ b/eth/peer.go
@@ -84,43 +84,31 @@ func newPeer(version int, p *p2p.Peer, rw p2p.MsgReadWriter) *peer {
// Info gathers and returns a collection of metadata known about a peer.
func (p *peer) Info() *PeerInfo {
+ hash, td := p.Head()
+
return &PeerInfo{
Version: p.version,
- Difficulty: p.Td(),
- Head: fmt.Sprintf("%x", p.Head()),
+ Difficulty: td,
+ Head: hash.Hex(),
}
}
-// Head retrieves a copy of the current head (most recent) hash of the peer.
-func (p *peer) Head() (hash common.Hash) {
+// Head retrieves a copy of the current head hash and total difficulty of the
+// peer.
+func (p *peer) Head() (hash common.Hash, td *big.Int) {
p.lock.RLock()
defer p.lock.RUnlock()
copy(hash[:], p.head[:])
- return hash
+ return hash, new(big.Int).Set(p.td)
}
-// SetHead updates the head (most recent) hash of the peer.
-func (p *peer) SetHead(hash common.Hash) {
+// SetHead updates the head hash and total difficulty of the peer.
+func (p *peer) SetHead(hash common.Hash, td *big.Int) {
p.lock.Lock()
defer p.lock.Unlock()
copy(p.head[:], hash[:])
-}
-
-// Td retrieves the current total difficulty of a peer.
-func (p *peer) Td() *big.Int {
- p.lock.RLock()
- defer p.lock.RUnlock()
-
- return new(big.Int).Set(p.td)
-}
-
-// SetTd updates the current total difficulty of a peer.
-func (p *peer) SetTd(td *big.Int) {
- p.lock.Lock()
- defer p.lock.Unlock()
-
p.td.Set(td)
}
@@ -411,7 +399,7 @@ func (ps *peerSet) BestPeer() *peer {
bestTd *big.Int
)
for _, p := range ps.peers {
- if td := p.Td(); bestPeer == nil || td.Cmp(bestTd) > 0 {
+ if _, td := p.Head(); bestPeer == nil || td.Cmp(bestTd) > 0 {
bestPeer, bestTd = p, td
}
}
diff --git a/eth/sync.go b/eth/sync.go
index 23cf18c8d..e1946edda 100644
--- a/eth/sync.go
+++ b/eth/sync.go
@@ -161,10 +161,12 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
if peer == nil {
return
}
- // Make sure the peer's TD is higher than our own. If not drop.
+ // Make sure the peer's TD is higher than our own
currentBlock := pm.blockchain.CurrentBlock()
td := pm.blockchain.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
- if peer.Td().Cmp(td) <= 0 {
+
+ pHead, pTd := peer.Head()
+ if pTd.Cmp(td) <= 0 {
return
}
// Otherwise try to sync with the downloader
@@ -172,7 +174,7 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
if atomic.LoadUint32(&pm.fastSync) == 1 {
mode = downloader.FastSync
}
- if err := pm.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), mode); err != nil {
+ if err := pm.downloader.Synchronise(peer.id, pHead, pTd, mode); err != nil {
return
}
atomic.StoreUint32(&pm.synced, 1) // Mark initial sync done
diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go
index 190fd57d2..e76e15177 100644
--- a/internal/web3ext/web3ext.go
+++ b/internal/web3ext/web3ext.go
@@ -40,6 +40,11 @@ web3._extend({
params: 1
}),
new web3._extend.Method({
+ name: 'removePeer',
+ call: 'admin_removePeer',
+ params: 1
+ }),
+ new web3._extend.Method({
name: 'exportChain',
call: 'admin_exportChain',
params: 1,
diff --git a/node/api.go b/node/api.go
index 9b2be9c2e..3523874ab 100644
--- a/node/api.go
+++ b/node/api.go
@@ -58,6 +58,22 @@ func (api *PrivateAdminAPI) AddPeer(url string) (bool, error) {
return true, nil
}
+// RemovePeer disconnects from a a remote node if the connection exists
+func (api *PrivateAdminAPI) RemovePeer(url string) (bool, error) {
+ // Make sure the server is running, fail otherwise
+ server := api.node.Server()
+ if server == nil {
+ return false, ErrNodeStopped
+ }
+ // Try to remove the url as a static peer and return
+ node, err := discover.ParseNode(url)
+ if err != nil {
+ return false, fmt.Errorf("invalid enode: %v", err)
+ }
+ server.RemovePeer(node)
+ return true, nil
+}
+
// StartRPC starts the HTTP RPC API server.
func (api *PrivateAdminAPI) StartRPC(host *string, port *rpc.HexNumber, cors *string, apis *string) (bool, error) {
api.node.lock.Lock()
diff --git a/p2p/dial.go b/p2p/dial.go
index c0e703d7d..691b8539e 100644
--- a/p2p/dial.go
+++ b/p2p/dial.go
@@ -121,6 +121,11 @@ func (s *dialstate) addStatic(n *discover.Node) {
s.static[n.ID] = &dialTask{flags: staticDialedConn, dest: n}
}
+func (s *dialstate) removeStatic(n *discover.Node) {
+ // This removes a task so future attempts to connect will not be made.
+ delete(s.static, n.ID)
+}
+
func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task {
var newtasks []task
isDialing := func(id discover.NodeID) bool {
diff --git a/p2p/server.go b/p2p/server.go
index 880aa7cf1..8e3cd93f9 100644
--- a/p2p/server.go
+++ b/p2p/server.go
@@ -142,6 +142,7 @@ type Server struct {
quit chan struct{}
addstatic chan *discover.Node
+ removestatic chan *discover.Node
posthandshake chan *conn
addpeer chan *conn
delpeer chan *Peer
@@ -257,6 +258,14 @@ func (srv *Server) AddPeer(node *discover.Node) {
}
}
+// RemovePeer disconnects from the given node
+func (srv *Server) RemovePeer(node *discover.Node) {
+ select {
+ case srv.removestatic <- node:
+ case <-srv.quit:
+ }
+}
+
// Self returns the local node's endpoint information.
func (srv *Server) Self() *discover.Node {
srv.lock.Lock()
@@ -327,6 +336,7 @@ func (srv *Server) Start() (err error) {
srv.delpeer = make(chan *Peer)
srv.posthandshake = make(chan *conn)
srv.addstatic = make(chan *discover.Node)
+ srv.removestatic = make(chan *discover.Node)
srv.peerOp = make(chan peerOpFunc)
srv.peerOpDone = make(chan struct{})
@@ -395,6 +405,7 @@ type dialer interface {
newTasks(running int, peers map[discover.NodeID]*Peer, now time.Time) []task
taskDone(task, time.Time)
addStatic(*discover.Node)
+ removeStatic(*discover.Node)
}
func (srv *Server) run(dialstate dialer) {
@@ -458,6 +469,15 @@ running:
// it will keep the node connected.
glog.V(logger.Detail).Infoln("<-addstatic:", n)
dialstate.addStatic(n)
+ case n := <-srv.removestatic:
+ // This channel is used by RemovePeer to send a
+ // disconnect request to a peer and begin the
+ // stop keeping the node connected
+ glog.V(logger.Detail).Infoln("<-removestatic:", n)
+ dialstate.removeStatic(n)
+ if p, ok := peers[n.ID]; ok {
+ p.Disconnect(DiscRequested)
+ }
case op := <-srv.peerOp:
// This channel is used by Peers and PeerCount.
op(peers)
diff --git a/p2p/server_test.go b/p2p/server_test.go
index deb34f5bb..313d086ec 100644
--- a/p2p/server_test.go
+++ b/p2p/server_test.go
@@ -301,6 +301,8 @@ func (tg taskgen) taskDone(t task, now time.Time) {
}
func (tg taskgen) addStatic(*discover.Node) {
}
+func (tg taskgen) removeStatic(*discover.Node) {
+}
type testTask struct {
index int