mirror of
https://github.com/arnaucube/go-ethereum.git
synced 2026-03-02 06:56:46 +01:00
Compare commits
84 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b70acf3c5b | ||
|
|
b5be6b72cb | ||
|
|
318ad3c1e4 | ||
|
|
e949a2ed2f | ||
|
|
8d0108fc5d | ||
|
|
ba41efa8a0 | ||
|
|
f81660b6db | ||
|
|
91bceb4ace | ||
|
|
be746628c7 | ||
|
|
ec5f531f4b | ||
|
|
37e5816bcd | ||
|
|
665bb43a4c | ||
|
|
e4bf004560 | ||
|
|
1609df3275 | ||
|
|
24f288770e | ||
|
|
65e6319b12 | ||
|
|
ec75953f50 | ||
|
|
801a13f791 | ||
|
|
eea8d6aa96 | ||
|
|
2b9cd71d67 | ||
|
|
5df83e3bd9 | ||
|
|
6061707371 | ||
|
|
20899c05a4 | ||
|
|
4c8c5e2f74 | ||
|
|
d1a95c643e | ||
|
|
9c3ea0d32d | ||
|
|
67e0894d9e | ||
|
|
6cc87a31c6 | ||
|
|
b8c766a9c5 | ||
|
|
66441c9b4b | ||
|
|
18d51d1de8 | ||
|
|
01d5fc670b | ||
|
|
0f1cbfd3da | ||
|
|
586f10ecb1 | ||
|
|
978737f5d5 | ||
|
|
fa0e057f8a | ||
|
|
bca7bfa927 | ||
|
|
12d654a6fc | ||
|
|
8e64e4383c | ||
|
|
f59d8cde26 | ||
|
|
94c0519be2 | ||
|
|
529c502876 | ||
|
|
c04c8f10f0 | ||
|
|
e05d35e6e0 | ||
|
|
e1e2df656a | ||
|
|
f7da5b29f0 | ||
|
|
2b4c236773 | ||
|
|
a8ca75738a | ||
|
|
aad4890082 | ||
|
|
e5edd3b983 | ||
|
|
a47341cf96 | ||
|
|
e46bda5093 | ||
|
|
a98d1d67d6 | ||
|
|
ba2884f343 | ||
|
|
1d80155d5e | ||
|
|
a0e42aa4e2 | ||
|
|
92959cd4ef | ||
|
|
2c802399c3 | ||
|
|
8ed72a8470 | ||
|
|
0d9a8207d6 | ||
|
|
04edbb0703 | ||
|
|
e1c1fce92c | ||
|
|
c8695209f6 | ||
|
|
9b95112a2d | ||
|
|
a602c57c8d | ||
|
|
f3228592f5 | ||
|
|
87b8254da1 | ||
|
|
20eab80189 | ||
|
|
810389c07a | ||
|
|
3badd3782b | ||
|
|
be2a264915 | ||
|
|
b81baf5423 | ||
|
|
64359c9417 | ||
|
|
5a3853f83f | ||
|
|
532d746036 | ||
|
|
c66e18b175 | ||
|
|
2d4bd3b3ad | ||
|
|
a96d6c68e2 | ||
|
|
504815091f | ||
|
|
4dd3e7fe35 | ||
|
|
4c614909ff | ||
|
|
b0a23c73cf | ||
|
|
922c1f8f9f | ||
|
|
fd27393df3 |
19
.travis.yml
19
.travis.yml
@@ -59,10 +59,6 @@ matrix:
|
||||
env:
|
||||
- azure-osx
|
||||
- mobile
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.android.platforms
|
||||
- $HOME/.cocoapods
|
||||
script:
|
||||
- go run build/ci.go install
|
||||
- go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -upload gethstore/builds
|
||||
@@ -71,21 +67,24 @@ matrix:
|
||||
- brew update
|
||||
- brew install android-sdk maven gpg
|
||||
- alias gpg="gpg2"
|
||||
- export ANDROID_HOME=/usr/local/opt/android-sdk
|
||||
|
||||
- mkdir -p $ANDROID_HOME/platforms
|
||||
- mv -f $HOME/.android.platforms $ANDROID_HOME/platforms
|
||||
- echo "y" | android update sdk --no-ui --filter platform
|
||||
- export ANDROID_HOME=/usr/local/opt/android-sdk
|
||||
- echo "y" | android update sdk --no-ui --filter `android list sdk | grep "SDK Platform Android" | grep -E 'API 15|API 19|API 24' | awk '{print $1}' | cut -d '-' -f 1 | tr '\n' ','`
|
||||
|
||||
- go run build/ci.go aar -signer ANDROID_SIGNING_KEY -deploy https://oss.sonatype.org -upload gethstore/builds
|
||||
- mv -f $ANDROID_HOME/platforms $HOME/.android.platforms
|
||||
|
||||
# Build the iOS framework and upload it to CocoaPods and Azure
|
||||
- gem uninstall cocoapods -a
|
||||
- gem install cocoapods --pre
|
||||
|
||||
- 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
|
||||
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then git clone --depth=1 https://github.com/CocoaPods/Specs.git ~/.cocoapods/repos/master && pod setup --verbose; fi
|
||||
|
||||
- xctool -version
|
||||
- xcrun simctl list
|
||||
- travis_wait 30 go run build/ci.go xcode -signer IOS_SIGNING_KEY -deploy trunk -upload gethstore/builds
|
||||
|
||||
- go run build/ci.go xcode -signer IOS_SIGNING_KEY -deploy trunk -upload gethstore/builds
|
||||
|
||||
install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
|
||||
14
Dockerfile
Normal file
14
Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
||||
FROM alpine:3.3
|
||||
|
||||
ADD . /go-ethereum
|
||||
RUN \
|
||||
apk add --update git go make gcc musl-dev && \
|
||||
(cd go-ethereum && make geth) && \
|
||||
cp go-ethereum/build/bin/geth /geth && \
|
||||
apk del git go make gcc musl-dev && \
|
||||
rm -rf /go-ethereum && rm -rf /var/cache/apk/*
|
||||
|
||||
EXPOSE 8545
|
||||
EXPOSE 30303
|
||||
|
||||
ENTRYPOINT ["/geth"]
|
||||
25
Makefile
25
Makefile
@@ -2,12 +2,11 @@
|
||||
# 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 clean
|
||||
.PHONY: geth android ios geth-cross evm all test clean
|
||||
.PHONY: geth-linux geth-linux-386 geth-linux-amd64 geth-linux-mips64 geth-linux-mips64le
|
||||
.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
|
||||
.PHONY: geth-windows geth-windows-386 geth-windows-amd64
|
||||
.PHONY: geth-android geth-ios
|
||||
|
||||
GOBIN = build/bin
|
||||
GO ?= latest
|
||||
@@ -20,11 +19,21 @@ geth:
|
||||
evm:
|
||||
build/env.sh go run build/ci.go install ./cmd/evm
|
||||
@echo "Done building."
|
||||
@echo "Run \"$(GOBIN)/evm to start the evm."
|
||||
@echo "Run \"$(GOBIN)/evm\" to start the evm."
|
||||
|
||||
all:
|
||||
build/env.sh go run build/ci.go install
|
||||
|
||||
android:
|
||||
build/env.sh go run build/ci.go aar --local
|
||||
@echo "Done building."
|
||||
@echo "Import \"$(GOBIN)/geth.aar\" to use the library."
|
||||
|
||||
ios:
|
||||
build/env.sh go run build/ci.go xcode --local
|
||||
@echo "Done building."
|
||||
@echo "Import \"$(GOBIN)/Geth.framework\" to use the library."
|
||||
|
||||
test: all
|
||||
build/env.sh go run build/ci.go test
|
||||
|
||||
@@ -112,13 +121,3 @@ 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:
|
||||
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:
|
||||
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-*
|
||||
|
||||
27
README.md
27
README.md
@@ -1,28 +1,14 @@
|
||||
## Ethereum Go
|
||||
|
||||
Official golang implementation of the Ethereum protocol
|
||||
|
||||
| Linux | OSX | ARM | Windows | Tests
|
||||
----------|---------|-----|-----|---------|------
|
||||
develop | [](https://build.ethdev.com/builders/Linux%20Go%20develop%20branch/builds/-1) | [](https://build.ethdev.com/builders/OSX%20Go%20develop%20branch/builds/-1) | [](https://build.ethdev.com/builders/ARM%20Go%20develop%20branch/builds/-1) | [](https://build.ethdev.com/builders/Windows%20Go%20develop%20branch/builds/-1) | [](https://travis-ci.org/ethereum/go-ethereum) [](https://codecov.io/github/ethereum/go-ethereum?branch=develop)
|
||||
master | [](https://build.ethdev.com/builders/Linux%20Go%20master%20branch/builds/-1) | [](https://build.ethdev.com/builders/OSX%20Go%20master%20branch/builds/-1) | [](https://build.ethdev.com/builders/ARM%20Go%20master%20branch/builds/-1) | [](https://build.ethdev.com/builders/Windows%20Go%20master%20branch/builds/-1) | [](https://travis-ci.org/ethereum/go-ethereum) [](https://codecov.io/github/ethereum/go-ethereum?branch=master)
|
||||
Official golang implementation of the Ethereum protocol.
|
||||
|
||||
[](https://godoc.org/github.com/ethereum/go-ethereum)
|
||||
[](https://gitter.im/ethereum/go-ethereum?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
|
||||
## Automated development builds
|
||||
|
||||
The following builds are built automatically by our build servers after each push to the [develop](https://github.com/ethereum/go-ethereum/tree/develop) branch.
|
||||
|
||||
* [Docker](https://registry.hub.docker.com/u/ethereum/client-go/)
|
||||
* [OS X](https://build.ethdev.com/builds/OSX%20Go%20develop%20branch/Mist-OSX-latest.dmg)
|
||||
* Ubuntu
|
||||
[trusty](https://build.ethdev.com/builds/Linux%20Go%20develop%20deb%20i386-trusty/latest/) |
|
||||
[utopic](https://build.ethdev.com/builds/Linux%20Go%20develop%20deb%20i386-utopic/latest/)
|
||||
* [Windows 64-bit](https://build.ethdev.com/builds/Windows%20Go%20develop%20branch/Geth-Win64-latest.zip)
|
||||
* [ARM](https://build.ethdev.com/builds/ARM%20Go%20develop%20branch/geth-ARM-latest.tar.bz2)
|
||||
Automated builds are available for stable releases and the unstable master branch.
|
||||
Binary archives are published at https://geth.ethereum.org/downloads/.
|
||||
|
||||
## Building the source
|
||||
|
||||
@@ -46,13 +32,16 @@ The go-ethereum project comes with several wrappers/executables found in the `cm
|
||||
|
||||
| Command | Description |
|
||||
|:----------:|-------------|
|
||||
| **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default) archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. Please see our [Command Line Options](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options) wiki page for details. |
|
||||
| **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default) archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [CLI Wiki page](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options) for command line options |
|
||||
| `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-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`). |
|
||||
| `bzzd` | swarm daemon. This is the entrypoint for the swarm network. `bzzd --help` for command line options. See https://swarm-guide.readthedocs.io for swarm documentation. |
|
||||
| `bzzup` | swarm command line file uploader. `bzzup --help` for command line options |
|
||||
| `bzzhash` | command to calculate the swarm hash of a file or directory. `bzzhash --help` for command line options |
|
||||
|
||||
## Running geth
|
||||
|
||||
@@ -273,7 +262,7 @@ Please make sure your contributions adhere to our coding guidelines:
|
||||
|
||||
* Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
|
||||
* Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
|
||||
* Pull requests need to be based on and opened against the `develop` branch.
|
||||
* Pull requests need to be based on and opened against the `master` branch.
|
||||
* Commit messages should be prefixed with the package(s) they modify.
|
||||
* E.g. "eth, rpc: make trace configs optional"
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ func (ac *addrCache) scan() ([]Account, error) {
|
||||
buf = new(bufio.Reader)
|
||||
addrs []Account
|
||||
keyJSON struct {
|
||||
Address common.Address `json:"address"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
)
|
||||
for _, fi := range files {
|
||||
@@ -241,15 +241,16 @@ func (ac *addrCache) scan() ([]Account, error) {
|
||||
}
|
||||
buf.Reset(fd)
|
||||
// Parse the address.
|
||||
keyJSON.Address = common.Address{}
|
||||
keyJSON.Address = ""
|
||||
err = json.NewDecoder(buf).Decode(&keyJSON)
|
||||
addr := common.HexToAddress(keyJSON.Address)
|
||||
switch {
|
||||
case err != nil:
|
||||
glog.V(logger.Debug).Infof("can't decode key %s: %v", path, err)
|
||||
case (keyJSON.Address == common.Address{}):
|
||||
case (addr == common.Address{}):
|
||||
glog.V(logger.Debug).Infof("can't decode key %s: missing or zero address", path)
|
||||
default:
|
||||
addrs = append(addrs, Account{Address: keyJSON.Address, File: path})
|
||||
addrs = append(addrs, Account{Address: addr, File: path})
|
||||
}
|
||||
fd.Close()
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// 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 darwin,!ios freebsd linux,!arm64 netbsd solaris windows
|
||||
// +build darwin,!ios freebsd linux,!arm64 netbsd solaris
|
||||
|
||||
package accounts
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// 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 ios linux,arm64 !darwin,!freebsd,!linux,!netbsd,!solaris,!windows
|
||||
// +build ios linux,arm64 windows !darwin,!freebsd,!linux,!netbsd,!solaris
|
||||
|
||||
// This is the fallback implementation of directory watching.
|
||||
// It is used on unsupported platforms.
|
||||
|
||||
11
appveyor.yml
11
appveyor.yml
@@ -22,19 +22,18 @@ environment:
|
||||
|
||||
install:
|
||||
- rmdir C:\go /s /q
|
||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.7.3.windows-amd64.zip
|
||||
- 7z x go1.7.3.windows-amd64.zip -y -oC:\ > NUL
|
||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.7.3.windows-%GETH_ARCH%.zip
|
||||
- 7z x go1.7.3.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
|
||||
- go version
|
||||
- gcc --version
|
||||
|
||||
build_script:
|
||||
- go run build\ci.go install -arch %GETH_ARCH%
|
||||
- go run build\ci.go install
|
||||
|
||||
after_build:
|
||||
- go run build\ci.go archive -arch %GETH_ARCH% -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
||||
- go run build\ci.go nsis -arch %GETH_ARCH% -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
||||
- go run build\ci.go archive -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
||||
- go run build\ci.go nsis -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
||||
|
||||
test_script:
|
||||
- set GOARCH=%GETH_ARCH%
|
||||
- set CGO_ENABLED=1
|
||||
- go run build\ci.go test -vet -coverage
|
||||
|
||||
58
build/ci.go
58
build/ci.go
@@ -29,8 +29,8 @@ Available commands are:
|
||||
importkeys -- imports signing keys from env
|
||||
debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package
|
||||
nsis -- creates a Windows NSIS installer
|
||||
aar [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an Android archive
|
||||
xcode [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an iOS XCode framework
|
||||
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
|
||||
xgo [ options ] -- cross builds according to options
|
||||
|
||||
For all commands, -n prevents execution of external programs (dry run mode).
|
||||
@@ -72,6 +72,9 @@ var (
|
||||
executablePath("abigen"),
|
||||
executablePath("evm"),
|
||||
executablePath("geth"),
|
||||
executablePath("bzzd"),
|
||||
executablePath("bzzhash"),
|
||||
executablePath("bzzup"),
|
||||
executablePath("rlpdump"),
|
||||
}
|
||||
|
||||
@@ -89,6 +92,18 @@ var (
|
||||
Name: "evm",
|
||||
Description: "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode.",
|
||||
},
|
||||
{
|
||||
Name: "bzzd",
|
||||
Description: "Ethereum Swarm daemon",
|
||||
},
|
||||
{
|
||||
Name: "bzzup",
|
||||
Description: "Ethereum Swarm command line file/directory uploader",
|
||||
},
|
||||
{
|
||||
Name: "bzzhash",
|
||||
Description: "Ethereum Swarm file/directory hash calculator",
|
||||
},
|
||||
{
|
||||
Name: "abigen",
|
||||
Description: "Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages.",
|
||||
@@ -459,7 +474,7 @@ func makeWorkdir(wdflag string) string {
|
||||
}
|
||||
|
||||
func isUnstableBuild(env build.Environment) bool {
|
||||
if env.Branch != "master" && env.Tag != "" {
|
||||
if env.Tag != "" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -654,6 +669,7 @@ func doWindowsInstaller(cmdline []string) {
|
||||
|
||||
func doAndroidArchive(cmdline []string) {
|
||||
var (
|
||||
local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`)
|
||||
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. ANDROID_SIGNING_KEY)`)
|
||||
deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "https://oss.sonatype.org")`)
|
||||
upload = flag.String("upload", "", `Destination to upload the archive (usually "gethstore/builds")`)
|
||||
@@ -666,6 +682,11 @@ func doAndroidArchive(cmdline []string) {
|
||||
build.MustRun(gomobileTool("init"))
|
||||
build.MustRun(gomobileTool("bind", "--target", "android", "--javapkg", "org.ethereum", "-v", "github.com/ethereum/go-ethereum/mobile"))
|
||||
|
||||
if *local {
|
||||
// If we're building locally, copy bundle to build dir and skip Maven
|
||||
os.Rename("geth.aar", filepath.Join(GOBIN, "geth.aar"))
|
||||
return
|
||||
}
|
||||
meta := newMavenMetadata(env)
|
||||
build.Render("build/mvn.pom", meta.Package+".pom", 0755, meta)
|
||||
|
||||
@@ -744,7 +765,7 @@ func newMavenMetadata(env build.Environment) mavenMetadata {
|
||||
continue
|
||||
}
|
||||
// Split the author and insert as a contributor
|
||||
re := regexp.MustCompile("([^<]+) <(.+>)")
|
||||
re := regexp.MustCompile("([^<]+) <(.+)>")
|
||||
parts := re.FindStringSubmatch(line)
|
||||
if len(parts) == 3 {
|
||||
contribs = append(contribs, mavenContributor{Name: parts[1], Email: parts[2]})
|
||||
@@ -768,6 +789,7 @@ func newMavenMetadata(env build.Environment) mavenMetadata {
|
||||
|
||||
func doXCodeFramework(cmdline []string) {
|
||||
var (
|
||||
local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`)
|
||||
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. IOS_SIGNING_KEY)`)
|
||||
deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "trunk")`)
|
||||
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
|
||||
@@ -778,12 +800,18 @@ func doXCodeFramework(cmdline []string) {
|
||||
// Build the iOS XCode framework
|
||||
build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile"))
|
||||
build.MustRun(gomobileTool("init"))
|
||||
bind := gomobileTool("bind", "--target", "ios", "--tags", "ios", "--prefix", "GE", "-v", "github.com/ethereum/go-ethereum/mobile")
|
||||
|
||||
if *local {
|
||||
// If we're building locally, use the build folder and stop afterwards
|
||||
bind.Dir, _ = filepath.Abs(GOBIN)
|
||||
build.MustRun(bind)
|
||||
return
|
||||
}
|
||||
archive := "geth-" + archiveBasename("ios", env)
|
||||
if err := os.Mkdir(archive, os.ModePerm); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
bind := gomobileTool("bind", "--target", "ios", "--tags", "ios", "--prefix", "GE", "-v", "github.com/ethereum/go-ethereum/mobile")
|
||||
bind.Dir, _ = filepath.Abs(archive)
|
||||
build.MustRun(bind)
|
||||
build.MustRunCommand("tar", "-zcvf", archive+".tar.gz", archive)
|
||||
@@ -797,16 +825,16 @@ func doXCodeFramework(cmdline []string) {
|
||||
}
|
||||
// Prepare and upload a PodSpec to CocoaPods
|
||||
if *deploy != "" {
|
||||
meta := newPodMetadata(env)
|
||||
build.Render("build/pod.podspec", meta.Name+".podspec", 0755, meta)
|
||||
build.MustRunCommand("pod", *deploy, "push", meta.Name+".podspec", "--allow-warnings")
|
||||
meta := newPodMetadata(env, archive)
|
||||
build.Render("build/pod.podspec", "Geth.podspec", 0755, meta)
|
||||
build.MustRunCommand("pod", *deploy, "push", "Geth.podspec", "--allow-warnings", "--verbose")
|
||||
}
|
||||
}
|
||||
|
||||
type podMetadata struct {
|
||||
Name string
|
||||
Version string
|
||||
Commit string
|
||||
Archive string
|
||||
Contributors []podContributor
|
||||
}
|
||||
|
||||
@@ -815,7 +843,7 @@ type podContributor struct {
|
||||
Email string
|
||||
}
|
||||
|
||||
func newPodMetadata(env build.Environment) podMetadata {
|
||||
func newPodMetadata(env build.Environment, archive string) podMetadata {
|
||||
// Collect the list of authors from the repo root
|
||||
contribs := []podContributor{}
|
||||
if authors, err := os.Open("AUTHORS"); err == nil {
|
||||
@@ -829,20 +857,20 @@ func newPodMetadata(env build.Environment) podMetadata {
|
||||
continue
|
||||
}
|
||||
// Split the author and insert as a contributor
|
||||
re := regexp.MustCompile("([^<]+) <(.+>)")
|
||||
re := regexp.MustCompile("([^<]+) <(.+)>")
|
||||
parts := re.FindStringSubmatch(line)
|
||||
if len(parts) == 3 {
|
||||
contribs = append(contribs, podContributor{Name: parts[1], Email: parts[2]})
|
||||
}
|
||||
}
|
||||
}
|
||||
name := "Geth"
|
||||
version := build.VERSION()
|
||||
if isUnstableBuild(env) {
|
||||
name += "Develop"
|
||||
version += "-unstable." + env.Buildnum
|
||||
}
|
||||
return podMetadata{
|
||||
Name: name,
|
||||
Version: archiveVersion(env),
|
||||
Archive: archive,
|
||||
Version: version,
|
||||
Commit: env.Commit,
|
||||
Contributors: contribs,
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ fi
|
||||
|
||||
# Set up the environment to use the workspace.
|
||||
GOPATH="$workspace"
|
||||
export GOPATH
|
||||
GO15VENDOREXPERIMENT=1
|
||||
export GOPATH GO15VENDOREXPERIMENT
|
||||
|
||||
# Run the command inside the workspace.
|
||||
cd "$ethdir/go-ethereum"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = '{{.Name}}'
|
||||
spec.name = 'Geth'
|
||||
spec.version = '{{.Version}}'
|
||||
spec.license = { :type => 'GNU Lesser General Public License, Version 3.0' }
|
||||
spec.homepage = 'https://github.com/ethereum/go-ethereum'
|
||||
@@ -14,9 +14,9 @@ Pod::Spec.new do |spec|
|
||||
spec.ios.vendored_frameworks = 'Frameworks/Geth.framework'
|
||||
|
||||
spec.prepare_command = <<-CMD
|
||||
curl https://gethstore.blob.core.windows.net/builds/geth-ios-all-{{.Version}}.tar.gz | tar -xvz
|
||||
curl https://gethstore.blob.core.windows.net/builds/{{.Archive}}.tar.gz | tar -xvz
|
||||
mkdir Frameworks
|
||||
mv geth-ios-all-{{.Version}}/Geth.framework Frameworks
|
||||
rm -rf geth-ios-all-{{.Version}}
|
||||
mv {{.Archive}}/Geth.framework Frameworks
|
||||
rm -rf {{.Archive}}
|
||||
CMD
|
||||
end
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -39,6 +40,7 @@ func main() {
|
||||
nodeKeyFile = flag.String("nodekey", "", "private key filename")
|
||||
nodeKeyHex = flag.String("nodekeyhex", "", "private key as hex (for testing)")
|
||||
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)")
|
||||
runv5 = flag.Bool("v5", false, "run a v5 topic discovery bootnode")
|
||||
|
||||
nodeKey *ecdsa.PrivateKey
|
||||
@@ -81,12 +83,20 @@ func main() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
var restrictList *netutil.Netlist
|
||||
if *netrestrict != "" {
|
||||
restrictList, err = netutil.ParseNetlist(*netrestrict)
|
||||
if err != nil {
|
||||
utils.Fatalf("-netrestrict: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if *runv5 {
|
||||
if _, err := discv5.ListenUDP(nodeKey, *listenAddr, natm, ""); err != nil {
|
||||
if _, err := discv5.ListenUDP(nodeKey, *listenAddr, natm, "", restrictList); err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
} else {
|
||||
if _, err := discover.ListenUDP(nodeKey, *listenAddr, natm, ""); err != nil {
|
||||
if _, err := discover.ListenUDP(nodeKey, *listenAddr, natm, "", restrictList); err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
@@ -38,6 +39,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/swarm"
|
||||
bzzapi "github.com/ethereum/go-ethereum/swarm/api"
|
||||
"github.com/ethereum/go-ethereum/swarm/network"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
@@ -61,17 +63,22 @@ var (
|
||||
Name: "bzzport",
|
||||
Usage: "Swarm local http api port",
|
||||
}
|
||||
SwarmNetworkIdFlag = cli.IntFlag{
|
||||
Name: "bzznetworkid",
|
||||
Usage: "Network identifier (integer, default 322=swarm testnet)",
|
||||
Value: network.NetworkId,
|
||||
}
|
||||
SwarmConfigPathFlag = cli.StringFlag{
|
||||
Name: "bzzconfig",
|
||||
Usage: "Swarm config file path (datadir/bzz)",
|
||||
}
|
||||
SwarmSwapDisabled = cli.BoolFlag{
|
||||
Name: "bzznoswap",
|
||||
Usage: "Swarm SWAP disabled (default false)",
|
||||
SwarmSwapEnabled = cli.BoolFlag{
|
||||
Name: "swap",
|
||||
Usage: "Swarm SWAP enabled (default false)",
|
||||
}
|
||||
SwarmSyncDisabled = cli.BoolFlag{
|
||||
Name: "bzznosync",
|
||||
Usage: "Swarm Syncing disabled (default false)",
|
||||
SwarmSyncEnabled = cli.BoolTFlag{
|
||||
Name: "sync",
|
||||
Usage: "Swarm Syncing enabled (default true)",
|
||||
}
|
||||
EthAPI = cli.StringFlag{
|
||||
Name: "ethapi",
|
||||
@@ -86,6 +93,7 @@ func init() {
|
||||
// Override flag defaults so bzzd can run alongside geth.
|
||||
utils.ListenPortFlag.Value = 30399
|
||||
utils.IPCPathFlag.Value = utils.DirectoryString{Value: "bzzd.ipc"}
|
||||
utils.IPCApiFlag.Value = "admin, bzz, chequebook, debug, rpc, web3"
|
||||
|
||||
// Set up the cli app.
|
||||
app.Commands = nil
|
||||
@@ -96,20 +104,24 @@ func init() {
|
||||
utils.BootnodesFlag,
|
||||
utils.KeyStoreDirFlag,
|
||||
utils.ListenPortFlag,
|
||||
utils.MaxPeersFlag,
|
||||
utils.NATFlag,
|
||||
utils.NoDiscoverFlag,
|
||||
utils.DiscoveryV5Flag,
|
||||
utils.NetrestrictFlag,
|
||||
utils.NodeKeyFileFlag,
|
||||
utils.NodeKeyHexFlag,
|
||||
utils.MaxPeersFlag,
|
||||
utils.NATFlag,
|
||||
utils.IPCDisabledFlag,
|
||||
utils.IPCApiFlag,
|
||||
utils.IPCPathFlag,
|
||||
// bzzd-specific flags
|
||||
EthAPI,
|
||||
SwarmConfigPathFlag,
|
||||
SwarmSwapDisabled,
|
||||
SwarmSyncDisabled,
|
||||
SwarmSwapEnabled,
|
||||
SwarmSyncEnabled,
|
||||
SwarmPortFlag,
|
||||
SwarmAccountFlag,
|
||||
SwarmNetworkIdFlag,
|
||||
ChequebookAddrFlag,
|
||||
}
|
||||
app.Flags = append(app.Flags, debug.Flags...)
|
||||
@@ -137,7 +149,8 @@ func bzzd(ctx *cli.Context) error {
|
||||
|
||||
// Add bootnodes as initial peers.
|
||||
if ctx.GlobalIsSet(utils.BootnodesFlag.Name) {
|
||||
injectBootnodes(stack.Server(), ctx.GlobalStringSlice(utils.BootnodesFlag.Name))
|
||||
bootnodes := strings.Split(ctx.GlobalString(utils.BootnodesFlag.Name), ",")
|
||||
injectBootnodes(stack.Server(), bootnodes)
|
||||
} else {
|
||||
injectBootnodes(stack.Server(), defaultBootnodes)
|
||||
}
|
||||
@@ -154,7 +167,7 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) {
|
||||
if bzzdir == "" {
|
||||
bzzdir = stack.InstanceDir()
|
||||
}
|
||||
bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey)
|
||||
bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey, ctx.GlobalUint64(SwarmNetworkIdFlag.Name))
|
||||
if err != nil {
|
||||
utils.Fatalf("unable to configure swarm: %v", err)
|
||||
}
|
||||
@@ -162,16 +175,18 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) {
|
||||
if len(bzzport) > 0 {
|
||||
bzzconfig.Port = bzzport
|
||||
}
|
||||
swapEnabled := !ctx.GlobalBool(SwarmSwapDisabled.Name)
|
||||
syncEnabled := !ctx.GlobalBool(SwarmSyncDisabled.Name)
|
||||
swapEnabled := ctx.GlobalBool(SwarmSwapEnabled.Name)
|
||||
syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabled.Name)
|
||||
|
||||
ethapi := ctx.GlobalString(EthAPI.Name)
|
||||
if ethapi == "" {
|
||||
utils.Fatalf("Option %q must not be empty", EthAPI.Name)
|
||||
}
|
||||
|
||||
boot := func(ctx *node.ServiceContext) (node.Service, error) {
|
||||
client, err := ethclient.Dial(ethapi)
|
||||
var client *ethclient.Client
|
||||
if ethapi == "" {
|
||||
err = fmt.Errorf("use ethapi flag to connect to a an eth client and talk to the blockchain")
|
||||
} else {
|
||||
client, err = ethclient.Dial(ethapi)
|
||||
}
|
||||
if err != nil {
|
||||
utils.Fatalf("Can't connect: %v", err)
|
||||
}
|
||||
@@ -240,6 +255,7 @@ func injectBootnodes(srv *p2p.Server, nodes []string) {
|
||||
n, err := discover.ParseNode(url)
|
||||
if err != nil {
|
||||
glog.Errorf("invalid bootnode %q", err)
|
||||
continue
|
||||
}
|
||||
srv.AddPeer(n)
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ func (c *client) uploadFile(file string, fi os.FileInfo) (manifest, error) {
|
||||
|
||||
func (c *client) uploadDirectory(dir string) (manifest, error) {
|
||||
dirm := manifest{}
|
||||
prefix := filepath.ToSlash(dir) + "/"
|
||||
prefix := filepath.ToSlash(filepath.Clean(dir)) + "/"
|
||||
err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil || fi.IsDir() {
|
||||
return err
|
||||
@@ -115,7 +115,7 @@ func (c *client) uploadDirectory(dir string) (manifest, error) {
|
||||
return fmt.Errorf("path %s outside directory %s", path, dir)
|
||||
}
|
||||
entry, err := c.uploadFile(path, fi)
|
||||
entry.Path = strings.TrimPrefix(filepath.ToSlash(path), prefix)
|
||||
entry.Path = strings.TrimPrefix(filepath.ToSlash(filepath.Clean(path)), prefix)
|
||||
dirm.Entries = append(dirm.Entries, entry)
|
||||
return err
|
||||
})
|
||||
|
||||
@@ -99,9 +99,6 @@ func importChain(ctx *cli.Context) error {
|
||||
if len(ctx.Args()) != 1 {
|
||||
utils.Fatalf("This command requires an argument.")
|
||||
}
|
||||
if ctx.GlobalBool(utils.TestNetFlag.Name) {
|
||||
state.StartingNonce = 1048576 // (2**20)
|
||||
}
|
||||
stack := makeFullNode(ctx)
|
||||
chain, chainDb := utils.MakeChain(ctx, stack)
|
||||
defer chainDb.Close()
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
@@ -46,7 +46,7 @@ func TestConsoleWelcome(t *testing.T) {
|
||||
// Gather all the infos the welcome message needs to contain
|
||||
geth.setTemplateFunc("goos", func() string { return runtime.GOOS })
|
||||
geth.setTemplateFunc("gover", runtime.Version)
|
||||
geth.setTemplateFunc("gethver", func() string { return utils.Version })
|
||||
geth.setTemplateFunc("gethver", func() string { return params.Version })
|
||||
geth.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
|
||||
geth.setTemplateFunc("apis", func() []string {
|
||||
apis := append(strings.Split(rpc.DefaultIPCApis, ","), rpc.MetadataApi)
|
||||
@@ -132,7 +132,7 @@ func testAttachWelcome(t *testing.T, geth *testgeth, endpoint string) {
|
||||
// Gather all the infos the welcome message needs to contain
|
||||
attach.setTemplateFunc("goos", func() string { return runtime.GOOS })
|
||||
attach.setTemplateFunc("gover", runtime.Version)
|
||||
attach.setTemplateFunc("gethver", func() string { return utils.Version })
|
||||
attach.setTemplateFunc("gethver", func() string { return params.Version })
|
||||
attach.setTemplateFunc("etherbase", func() string { return geth.Etherbase })
|
||||
attach.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
|
||||
attach.setTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") })
|
||||
|
||||
@@ -191,14 +191,15 @@ func testDAOForkBlockNewChain(t *testing.T, testnet bool, genesis string, votes
|
||||
|
||||
genesisHash := common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
|
||||
if testnet {
|
||||
genesisHash = common.HexToHash("0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303")
|
||||
genesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
|
||||
}
|
||||
if genesis != "" {
|
||||
genesisHash = daoGenesisHash
|
||||
}
|
||||
config, err := core.GetChainConfig(db, genesisHash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve chain config: %v", err)
|
||||
t.Errorf("failed to retrieve chain config: %v", err)
|
||||
return // we want to return here, the other checks can't make it past this point (nil panic).
|
||||
}
|
||||
// Validate the DAO hard-fork block number against the expected value
|
||||
if config.DAOForkBlock == nil {
|
||||
|
||||
@@ -34,13 +34,14 @@ import (
|
||||
"github.com/ethereum/go-ethereum/console"
|
||||
"github.com/ethereum/go-ethereum/contracts/release"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/internal/debug"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
@@ -149,6 +150,7 @@ participating.
|
||||
utils.NatspecEnabledFlag,
|
||||
utils.NoDiscoverFlag,
|
||||
utils.DiscoveryV5Flag,
|
||||
utils.NetrestrictFlag,
|
||||
utils.NodeKeyFileFlag,
|
||||
utils.NodeKeyHexFlag,
|
||||
utils.RPCEnabledFlag,
|
||||
@@ -173,6 +175,7 @@ participating.
|
||||
utils.VMEnableJitFlag,
|
||||
utils.NetworkIdFlag,
|
||||
utils.RPCCORSDomainFlag,
|
||||
utils.EthStatsURLFlag,
|
||||
utils.MetricsEnabledFlag,
|
||||
utils.FakePoWFlag,
|
||||
utils.SolcPathFlag,
|
||||
@@ -237,10 +240,6 @@ func initGenesis(ctx *cli.Context) error {
|
||||
utils.Fatalf("must supply path to genesis JSON file")
|
||||
}
|
||||
|
||||
if ctx.GlobalBool(utils.TestNetFlag.Name) {
|
||||
state.StartingNonce = 1048576 // (2**20)
|
||||
}
|
||||
|
||||
stack := makeFullNode(ctx)
|
||||
chaindb := utils.MakeChainDatabase(ctx, stack)
|
||||
|
||||
@@ -258,8 +257,24 @@ func initGenesis(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
func makeFullNode(ctx *cli.Context) *node.Node {
|
||||
// Create the default extradata and construct the base node
|
||||
var clientInfo = struct {
|
||||
Version uint
|
||||
Name string
|
||||
GoVersion string
|
||||
Os string
|
||||
}{uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch), clientIdentifier, runtime.Version(), runtime.GOOS}
|
||||
extra, err := rlp.EncodeToBytes(clientInfo)
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information:", err)
|
||||
}
|
||||
if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize)
|
||||
glog.V(logger.Debug).Infof("extra: %x\n", extra)
|
||||
extra = nil
|
||||
}
|
||||
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
|
||||
utils.RegisterEthService(ctx, stack, utils.MakeDefaultExtraData(clientIdentifier))
|
||||
utils.RegisterEthService(ctx, stack, extra)
|
||||
|
||||
// Whisper must be explicitly enabled, but is auto-enabled in --dev mode.
|
||||
shhEnabled := ctx.GlobalBool(utils.WhisperEnabledFlag.Name)
|
||||
@@ -267,14 +282,17 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
||||
if shhEnabled || shhAutoEnabled {
|
||||
utils.RegisterShhService(stack)
|
||||
}
|
||||
|
||||
// Add the Ethereum Stats daemon if requested
|
||||
if url := ctx.GlobalString(utils.EthStatsURLFlag.Name); url != "" {
|
||||
utils.RegisterEthStatsService(stack, url)
|
||||
}
|
||||
// Add the release oracle service so it boots along with node.
|
||||
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||
config := release.Config{
|
||||
Oracle: relOracle,
|
||||
Major: uint32(utils.VersionMajor),
|
||||
Minor: uint32(utils.VersionMinor),
|
||||
Patch: uint32(utils.VersionPatch),
|
||||
Major: uint32(params.VersionMajor),
|
||||
Minor: uint32(params.VersionMinor),
|
||||
Patch: uint32(params.VersionPatch),
|
||||
}
|
||||
commit, _ := hex.DecodeString(gitCommit)
|
||||
copy(config.Commit[:], commit)
|
||||
@@ -282,7 +300,6 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
||||
}); err != nil {
|
||||
utils.Fatalf("Failed to register the Geth release oracle service: %v", err)
|
||||
}
|
||||
|
||||
return stack
|
||||
}
|
||||
|
||||
@@ -346,7 +363,7 @@ func makedag(ctx *cli.Context) error {
|
||||
|
||||
func version(ctx *cli.Context) error {
|
||||
fmt.Println(strings.Title(clientIdentifier))
|
||||
fmt.Println("Version:", utils.Version)
|
||||
fmt.Println("Version:", params.Version)
|
||||
if gitCommit != "" {
|
||||
fmt.Println("Git Commit:", gitCommit)
|
||||
}
|
||||
|
||||
@@ -161,6 +161,7 @@ var AppHelpFlagGroups = []flagGroup{
|
||||
{
|
||||
Name: "LOGGING AND DEBUGGING",
|
||||
Flags: append([]cli.Flag{
|
||||
utils.EthStatsURLFlag,
|
||||
utils.MetricsEnabledFlag,
|
||||
utils.FakePoWFlag,
|
||||
}, debug.Flags...),
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package utils contains internal helper functions for go-ethereum commands.
|
||||
package utils
|
||||
|
||||
import (
|
||||
@@ -22,13 +23,11 @@ import (
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/ethash"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
@@ -38,9 +37,9 @@ import (
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethstats"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
"github.com/ethereum/go-ethereum/light"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
@@ -48,6 +47,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
@@ -88,7 +88,7 @@ func NewApp(gitCommit, usage string) *cli.App {
|
||||
app.Author = ""
|
||||
//app.Authors = nil
|
||||
app.Email = ""
|
||||
app.Version = Version
|
||||
app.Version = params.Version
|
||||
if gitCommit != "" {
|
||||
app.Version += "-" + gitCommit[:8]
|
||||
}
|
||||
@@ -116,7 +116,7 @@ var (
|
||||
}
|
||||
NetworkIdFlag = cli.IntFlag{
|
||||
Name: "networkid",
|
||||
Usage: "Network identifier (integer, 0=Olympic, 1=Frontier, 2=Morden)",
|
||||
Usage: "Network identifier (integer, 0=Olympic (disused), 1=Frontier, 2=Morden (disused), 3=Ropsten)",
|
||||
Value: eth.NetworkId,
|
||||
}
|
||||
OlympicFlag = cli.BoolFlag{
|
||||
@@ -244,8 +244,11 @@ var (
|
||||
Name: "jitvm",
|
||||
Usage: "Enable the JIT VM",
|
||||
}
|
||||
|
||||
// logging and debug settings
|
||||
// Logging and debug settings
|
||||
EthStatsURLFlag = cli.StringFlag{
|
||||
Name: "ethstats",
|
||||
Usage: "Reporting URL of a ethstats service (nodename:secret@host:port)",
|
||||
}
|
||||
MetricsEnabledFlag = cli.BoolFlag{
|
||||
Name: metrics.MetricsEnabledFlag,
|
||||
Usage: "Enable metrics collection and reporting",
|
||||
@@ -369,14 +372,20 @@ var (
|
||||
Name: "v5disc",
|
||||
Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism",
|
||||
}
|
||||
NetrestrictFlag = cli.StringFlag{
|
||||
Name: "netrestrict",
|
||||
Usage: "Restricts network communication to the given IP networks (CIDR masks)",
|
||||
}
|
||||
|
||||
WhisperEnabledFlag = cli.BoolFlag{
|
||||
Name: "shh",
|
||||
Usage: "Enable Whisper",
|
||||
}
|
||||
|
||||
// ATM the url is left to the user and deployment to
|
||||
JSpathFlag = cli.StringFlag{
|
||||
Name: "jspath",
|
||||
Usage: "JavaScript root path for `loadScript` and document root for `admin.httpGet`",
|
||||
Usage: "JavaScript root path for `loadScript`",
|
||||
Value: ".",
|
||||
}
|
||||
SolcPathFlag = cli.StringFlag{
|
||||
@@ -656,7 +665,7 @@ func MakePasswordList(ctx *cli.Context) []string {
|
||||
|
||||
// MakeNode configures a node with no services from command line flags.
|
||||
func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node {
|
||||
vsn := Version
|
||||
vsn := params.Version
|
||||
if gitCommit != "" {
|
||||
vsn += "-" + gitCommit[:8]
|
||||
}
|
||||
@@ -696,6 +705,14 @@ func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node {
|
||||
config.MaxPeers = 0
|
||||
config.ListenAddr = ":0"
|
||||
}
|
||||
if netrestrict := ctx.GlobalString(NetrestrictFlag.Name); netrestrict != "" {
|
||||
list, err := netutil.ParseNetlist(netrestrict)
|
||||
if err != nil {
|
||||
Fatalf("Option %q: %v", NetrestrictFlag.Name, err)
|
||||
}
|
||||
config.NetRestrict = list
|
||||
}
|
||||
|
||||
stack, err := node.New(config)
|
||||
if err != nil {
|
||||
Fatalf("Failed to create the protocol stack: %v", err)
|
||||
@@ -717,16 +734,6 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
|
||||
Fatalf("The %v flags are mutually exclusive", netFlags)
|
||||
}
|
||||
|
||||
// initialise new random number generator
|
||||
rand := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
// get enabled jit flag
|
||||
jitEnabled := ctx.GlobalBool(VMEnableJitFlag.Name)
|
||||
// if the jit is not enabled enable it for 10 pct of the people
|
||||
if !jitEnabled && rand.Float64() < 0.1 {
|
||||
jitEnabled = true
|
||||
glog.V(logger.Info).Infoln("You're one of the lucky few that will try out the JIT VM (random). If you get a consensus failure please be so kind to report this incident with the block hash that failed. You can switch to the regular VM by setting --jitvm=false")
|
||||
}
|
||||
|
||||
ethConf := ð.Config{
|
||||
Etherbase: MakeEtherbase(stack.AccountManager(), ctx),
|
||||
ChainConfig: MakeChainConfig(ctx, stack),
|
||||
@@ -742,8 +749,6 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
|
||||
ExtraData: MakeMinerExtra(extra, ctx),
|
||||
NatSpec: ctx.GlobalBool(NatspecEnabledFlag.Name),
|
||||
DocRoot: ctx.GlobalString(DocRootFlag.Name),
|
||||
EnableJit: jitEnabled,
|
||||
ForceJit: ctx.GlobalBool(VMForceJitFlag.Name),
|
||||
GasPrice: common.String2Big(ctx.GlobalString(GasPriceFlag.Name)),
|
||||
GpoMinGasPrice: common.String2Big(ctx.GlobalString(GpoMinGasPriceFlag.Name)),
|
||||
GpoMaxGasPrice: common.String2Big(ctx.GlobalString(GpoMaxGasPriceFlag.Name)),
|
||||
@@ -765,11 +770,9 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
|
||||
|
||||
case ctx.GlobalBool(TestNetFlag.Name):
|
||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||
ethConf.NetworkId = 2
|
||||
ethConf.NetworkId = 3
|
||||
}
|
||||
ethConf.Genesis = core.TestNetGenesisBlock()
|
||||
state.StartingNonce = 1048576 // (2**20)
|
||||
light.StartingNonce = 1048576 // (2**20)
|
||||
ethConf.Genesis = core.DefaultTestnetGenesisBlock()
|
||||
|
||||
case ctx.GlobalBool(DevModeFlag.Name):
|
||||
ethConf.Genesis = core.OlympicGenesisBlock()
|
||||
@@ -803,13 +806,30 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterShhService configures whisper and adds it to the given node.
|
||||
// RegisterShhService configures Whisper and adds it to the given node.
|
||||
func RegisterShhService(stack *node.Node) {
|
||||
if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil {
|
||||
Fatalf("Failed to register the Whisper service: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterEthStatsService configures the Ethereum Stats daemon and adds it to
|
||||
// th egiven node.
|
||||
func RegisterEthStatsService(stack *node.Node, url string) {
|
||||
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||
// Retrieve both eth and les services
|
||||
var ethServ *eth.Ethereum
|
||||
ctx.Service(ðServ)
|
||||
|
||||
var lesServ *les.LightEthereum
|
||||
ctx.Service(&lesServ)
|
||||
|
||||
return ethstats.New(url, ethServ, lesServ)
|
||||
}); err != nil {
|
||||
Fatalf("Failed to register the Ethereum Stats service: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// SetupNetwork configures the system for either the main net or some test network.
|
||||
func SetupNetwork(ctx *cli.Context) {
|
||||
switch {
|
||||
@@ -861,61 +881,26 @@ func MakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *params.ChainCon
|
||||
(genesis.Hash() == params.MainNetGenesisHash && !ctx.GlobalBool(TestNetFlag.Name)) ||
|
||||
(genesis.Hash() == params.TestNetGenesisHash && ctx.GlobalBool(TestNetFlag.Name))
|
||||
|
||||
// Set any missing chainConfig fields due to them being unset or system upgrade
|
||||
if defaults {
|
||||
if config.HomesteadBlock == nil {
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config.HomesteadBlock = params.TestNetHomesteadBlock
|
||||
} else {
|
||||
config.HomesteadBlock = params.MainNetHomesteadBlock
|
||||
}
|
||||
}
|
||||
if config.DAOForkBlock == nil {
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config.DAOForkBlock = params.TestNetDAOForkBlock
|
||||
} else {
|
||||
config.DAOForkBlock = params.MainNetDAOForkBlock
|
||||
}
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config = params.TestnetChainConfig
|
||||
} else {
|
||||
// Homestead fork
|
||||
config.HomesteadBlock = params.MainNetHomesteadBlock
|
||||
// DAO fork
|
||||
config.DAOForkBlock = params.MainNetDAOForkBlock
|
||||
config.DAOForkSupport = true
|
||||
}
|
||||
if config.EIP150Block == nil {
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config.EIP150Block = params.TestNetHomesteadGasRepriceBlock
|
||||
} else {
|
||||
config.EIP150Block = params.MainNetHomesteadGasRepriceBlock
|
||||
}
|
||||
}
|
||||
if config.EIP150Hash == (common.Hash{}) {
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config.EIP150Hash = params.TestNetHomesteadGasRepriceHash
|
||||
} else {
|
||||
config.EIP150Hash = params.MainNetHomesteadGasRepriceHash
|
||||
}
|
||||
}
|
||||
if config.EIP155Block == nil {
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config.EIP150Block = params.TestNetSpuriousDragon
|
||||
} else {
|
||||
config.EIP155Block = params.MainNetSpuriousDragon
|
||||
}
|
||||
}
|
||||
if config.EIP158Block == nil {
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config.EIP158Block = params.TestNetSpuriousDragon
|
||||
} else {
|
||||
config.EIP158Block = params.MainNetSpuriousDragon
|
||||
}
|
||||
}
|
||||
if config.ChainId.BitLen() == 0 {
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config.ChainId = params.TestNetChainID
|
||||
} else {
|
||||
config.ChainId = params.MainNetChainID
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
// Force override any existing configs if explicitly requested
|
||||
switch {
|
||||
case ctx.GlobalBool(SupportDAOFork.Name):
|
||||
|
||||
232
common/hexutil/hexutil.go
Normal file
232
common/hexutil/hexutil.go
Normal file
@@ -0,0 +1,232 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/*
|
||||
Package hexutil implements hex encoding with 0x prefix.
|
||||
This encoding is used by the Ethereum RPC API to transport binary data in JSON payloads.
|
||||
|
||||
Encoding Rules
|
||||
|
||||
All hex data must have prefix "0x".
|
||||
|
||||
For byte slices, the hex data must be of even length. An empty byte slice
|
||||
encodes as "0x".
|
||||
|
||||
Integers are encoded using the least amount of digits (no leading zero digits). Their
|
||||
encoding may be of uneven length. The number zero encodes as "0x0".
|
||||
*/
|
||||
package hexutil
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const uintBits = 32 << (uint64(^uint(0)) >> 63)
|
||||
|
||||
var (
|
||||
ErrEmptyString = errors.New("empty hex string")
|
||||
ErrMissingPrefix = errors.New("missing 0x prefix for hex data")
|
||||
ErrSyntax = errors.New("invalid hex")
|
||||
ErrEmptyNumber = errors.New("hex number has no digits after 0x")
|
||||
ErrLeadingZero = errors.New("hex number has leading zero digits after 0x")
|
||||
ErrOddLength = errors.New("hex string has odd length")
|
||||
ErrUint64Range = errors.New("hex number does not fit into 64 bits")
|
||||
ErrUintRange = fmt.Errorf("hex number does not fit into %d bits", uintBits)
|
||||
)
|
||||
|
||||
// Decode decodes a hex string with 0x prefix.
|
||||
func Decode(input string) ([]byte, error) {
|
||||
if len(input) == 0 {
|
||||
return nil, ErrEmptyString
|
||||
}
|
||||
if !has0xPrefix(input) {
|
||||
return nil, ErrMissingPrefix
|
||||
}
|
||||
return hex.DecodeString(input[2:])
|
||||
}
|
||||
|
||||
// MustDecode decodes a hex string with 0x prefix. It panics for invalid input.
|
||||
func MustDecode(input string) []byte {
|
||||
dec, err := Decode(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
// Encode encodes b as a hex string with 0x prefix.
|
||||
func Encode(b []byte) string {
|
||||
enc := make([]byte, len(b)*2+2)
|
||||
copy(enc, "0x")
|
||||
hex.Encode(enc[2:], b)
|
||||
return string(enc)
|
||||
}
|
||||
|
||||
// DecodeUint64 decodes a hex string with 0x prefix as a quantity.
|
||||
func DecodeUint64(input string) (uint64, error) {
|
||||
raw, err := checkNumber(input)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
dec, err := strconv.ParseUint(raw, 16, 64)
|
||||
if err != nil {
|
||||
err = mapError(err)
|
||||
}
|
||||
return dec, err
|
||||
}
|
||||
|
||||
// MustDecodeUint64 decodes a hex string with 0x prefix as a quantity.
|
||||
// It panics for invalid input.
|
||||
func MustDecodeUint64(input string) uint64 {
|
||||
dec, err := DecodeUint64(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
// EncodeUint64 encodes i as a hex string with 0x prefix.
|
||||
func EncodeUint64(i uint64) string {
|
||||
enc := make([]byte, 2, 10)
|
||||
copy(enc, "0x")
|
||||
return string(strconv.AppendUint(enc, i, 16))
|
||||
}
|
||||
|
||||
var bigWordNibbles int
|
||||
|
||||
func init() {
|
||||
// This is a weird way to compute the number of nibbles required for big.Word.
|
||||
// The usual way would be to use constant arithmetic but go vet can't handle that.
|
||||
b, _ := new(big.Int).SetString("FFFFFFFFFF", 16)
|
||||
switch len(b.Bits()) {
|
||||
case 1:
|
||||
bigWordNibbles = 16
|
||||
case 2:
|
||||
bigWordNibbles = 8
|
||||
default:
|
||||
panic("weird big.Word size")
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeBig decodes a hex string with 0x prefix as a quantity.
|
||||
func DecodeBig(input string) (*big.Int, error) {
|
||||
raw, err := checkNumber(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
words := make([]big.Word, len(raw)/bigWordNibbles+1)
|
||||
end := len(raw)
|
||||
for i := range words {
|
||||
start := end - bigWordNibbles
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
for ri := start; ri < end; ri++ {
|
||||
nib := decodeNibble(raw[ri])
|
||||
if nib == badNibble {
|
||||
return nil, ErrSyntax
|
||||
}
|
||||
words[i] *= 16
|
||||
words[i] += big.Word(nib)
|
||||
}
|
||||
end = start
|
||||
}
|
||||
dec := new(big.Int).SetBits(words)
|
||||
return dec, nil
|
||||
}
|
||||
|
||||
// MustDecodeBig decodes a hex string with 0x prefix as a quantity.
|
||||
// It panics for invalid input.
|
||||
func MustDecodeBig(input string) *big.Int {
|
||||
dec, err := DecodeBig(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
// EncodeBig encodes bigint as a hex string with 0x prefix.
|
||||
// The sign of the integer is ignored.
|
||||
func EncodeBig(bigint *big.Int) string {
|
||||
nbits := bigint.BitLen()
|
||||
if nbits == 0 {
|
||||
return "0x0"
|
||||
}
|
||||
enc := make([]byte, 2, (nbits/8)*2+2)
|
||||
copy(enc, "0x")
|
||||
for i := len(bigint.Bits()) - 1; i >= 0; i-- {
|
||||
enc = strconv.AppendUint(enc, uint64(bigint.Bits()[i]), 16)
|
||||
}
|
||||
return string(enc)
|
||||
}
|
||||
|
||||
func has0xPrefix(input string) bool {
|
||||
return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
|
||||
}
|
||||
|
||||
func checkNumber(input string) (raw string, err error) {
|
||||
if len(input) == 0 {
|
||||
return "", ErrEmptyString
|
||||
}
|
||||
if !has0xPrefix(input) {
|
||||
return "", ErrMissingPrefix
|
||||
}
|
||||
input = input[2:]
|
||||
if len(input) == 0 {
|
||||
return "", ErrEmptyNumber
|
||||
}
|
||||
if len(input) > 1 && input[0] == '0' {
|
||||
return "", ErrLeadingZero
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
const badNibble = ^uint64(0)
|
||||
|
||||
func decodeNibble(in byte) uint64 {
|
||||
switch {
|
||||
case in >= '0' && in <= '9':
|
||||
return uint64(in - '0')
|
||||
case in >= 'A' && in <= 'F':
|
||||
return uint64(in - 'A' + 10)
|
||||
case in >= 'a' && in <= 'f':
|
||||
return uint64(in - 'a' + 10)
|
||||
default:
|
||||
return badNibble
|
||||
}
|
||||
}
|
||||
|
||||
func mapError(err error) error {
|
||||
if err, ok := err.(*strconv.NumError); ok {
|
||||
switch err.Err {
|
||||
case strconv.ErrRange:
|
||||
return ErrUint64Range
|
||||
case strconv.ErrSyntax:
|
||||
return ErrSyntax
|
||||
}
|
||||
}
|
||||
if _, ok := err.(hex.InvalidByteError); ok {
|
||||
return ErrSyntax
|
||||
}
|
||||
if err == hex.ErrLength {
|
||||
return ErrOddLength
|
||||
}
|
||||
return err
|
||||
}
|
||||
186
common/hexutil/hexutil_test.go
Normal file
186
common/hexutil/hexutil_test.go
Normal file
@@ -0,0 +1,186 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package hexutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type marshalTest struct {
|
||||
input interface{}
|
||||
want string
|
||||
}
|
||||
|
||||
type unmarshalTest struct {
|
||||
input string
|
||||
want interface{}
|
||||
wantErr error
|
||||
}
|
||||
|
||||
var (
|
||||
encodeBytesTests = []marshalTest{
|
||||
{[]byte{}, "0x"},
|
||||
{[]byte{0}, "0x00"},
|
||||
{[]byte{0, 0, 1, 2}, "0x00000102"},
|
||||
}
|
||||
|
||||
encodeBigTests = []marshalTest{
|
||||
{referenceBig("0"), "0x0"},
|
||||
{referenceBig("1"), "0x1"},
|
||||
{referenceBig("ff"), "0xff"},
|
||||
{referenceBig("112233445566778899aabbccddeeff"), "0x112233445566778899aabbccddeeff"},
|
||||
}
|
||||
|
||||
encodeUint64Tests = []marshalTest{
|
||||
{uint64(0), "0x0"},
|
||||
{uint64(1), "0x1"},
|
||||
{uint64(0xff), "0xff"},
|
||||
{uint64(0x1122334455667788), "0x1122334455667788"},
|
||||
}
|
||||
|
||||
decodeBytesTests = []unmarshalTest{
|
||||
// invalid
|
||||
{input: ``, wantErr: ErrEmptyString},
|
||||
{input: `0`, wantErr: ErrMissingPrefix},
|
||||
{input: `0x0`, wantErr: hex.ErrLength},
|
||||
{input: `0x023`, wantErr: hex.ErrLength},
|
||||
{input: `0xxx`, wantErr: hex.InvalidByteError('x')},
|
||||
{input: `0x01zz01`, wantErr: hex.InvalidByteError('z')},
|
||||
// valid
|
||||
{input: `0x`, want: []byte{}},
|
||||
{input: `0X`, want: []byte{}},
|
||||
{input: `0x02`, want: []byte{0x02}},
|
||||
{input: `0X02`, want: []byte{0x02}},
|
||||
{input: `0xffffffffff`, want: []byte{0xff, 0xff, 0xff, 0xff, 0xff}},
|
||||
{
|
||||
input: `0xffffffffffffffffffffffffffffffffffff`,
|
||||
want: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||
},
|
||||
}
|
||||
|
||||
decodeBigTests = []unmarshalTest{
|
||||
// invalid
|
||||
{input: `0`, wantErr: ErrMissingPrefix},
|
||||
{input: `0x`, wantErr: ErrEmptyNumber},
|
||||
{input: `0x01`, wantErr: ErrLeadingZero},
|
||||
{input: `0xx`, wantErr: ErrSyntax},
|
||||
{input: `0x1zz01`, wantErr: ErrSyntax},
|
||||
// valid
|
||||
{input: `0x0`, want: big.NewInt(0)},
|
||||
{input: `0x2`, want: big.NewInt(0x2)},
|
||||
{input: `0x2F2`, want: big.NewInt(0x2f2)},
|
||||
{input: `0X2F2`, want: big.NewInt(0x2f2)},
|
||||
{input: `0x1122aaff`, want: big.NewInt(0x1122aaff)},
|
||||
{input: `0xbBb`, want: big.NewInt(0xbbb)},
|
||||
{input: `0xfffffffff`, want: big.NewInt(0xfffffffff)},
|
||||
{
|
||||
input: `0x112233445566778899aabbccddeeff`,
|
||||
want: referenceBig("112233445566778899aabbccddeeff"),
|
||||
},
|
||||
{
|
||||
input: `0xffffffffffffffffffffffffffffffffffff`,
|
||||
want: referenceBig("ffffffffffffffffffffffffffffffffffff"),
|
||||
},
|
||||
}
|
||||
|
||||
decodeUint64Tests = []unmarshalTest{
|
||||
// invalid
|
||||
{input: `0`, wantErr: ErrMissingPrefix},
|
||||
{input: `0x`, wantErr: ErrEmptyNumber},
|
||||
{input: `0x01`, wantErr: ErrLeadingZero},
|
||||
{input: `0xfffffffffffffffff`, wantErr: ErrUint64Range},
|
||||
{input: `0xx`, wantErr: ErrSyntax},
|
||||
{input: `0x1zz01`, wantErr: ErrSyntax},
|
||||
// valid
|
||||
{input: `0x0`, want: uint64(0)},
|
||||
{input: `0x2`, want: uint64(0x2)},
|
||||
{input: `0x2F2`, want: uint64(0x2f2)},
|
||||
{input: `0X2F2`, want: uint64(0x2f2)},
|
||||
{input: `0x1122aaff`, want: uint64(0x1122aaff)},
|
||||
{input: `0xbbb`, want: uint64(0xbbb)},
|
||||
{input: `0xffffffffffffffff`, want: uint64(0xffffffffffffffff)},
|
||||
}
|
||||
)
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
for _, test := range encodeBytesTests {
|
||||
enc := Encode(test.input.([]byte))
|
||||
if enc != test.want {
|
||||
t.Errorf("input %x: wrong encoding %s", test.input, enc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
for _, test := range decodeBytesTests {
|
||||
dec, err := Decode(test.input)
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(test.want.([]byte), dec) {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeBig(t *testing.T) {
|
||||
for _, test := range encodeBigTests {
|
||||
enc := EncodeBig(test.input.(*big.Int))
|
||||
if enc != test.want {
|
||||
t.Errorf("input %x: wrong encoding %s", test.input, enc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeBig(t *testing.T) {
|
||||
for _, test := range decodeBigTests {
|
||||
dec, err := DecodeBig(test.input)
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if dec.Cmp(test.want.(*big.Int)) != 0 {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeUint64(t *testing.T) {
|
||||
for _, test := range encodeUint64Tests {
|
||||
enc := EncodeUint64(test.input.(uint64))
|
||||
if enc != test.want {
|
||||
t.Errorf("input %x: wrong encoding %s", test.input, enc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeUint64(t *testing.T) {
|
||||
for _, test := range decodeUint64Tests {
|
||||
dec, err := DecodeUint64(test.input)
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if dec != test.want.(uint64) {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
271
common/hexutil/json.go
Normal file
271
common/hexutil/json.go
Normal file
@@ -0,0 +1,271 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package hexutil
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
jsonNull = []byte("null")
|
||||
jsonZero = []byte(`"0x0"`)
|
||||
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.
|
||||
// The empty slice marshals as "0x".
|
||||
type Bytes []byte
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (b Bytes) MarshalJSON() ([]byte, error) {
|
||||
result := make([]byte, len(b)*2+4)
|
||||
copy(result, `"0x`)
|
||||
hex.Encode(result[3:], b)
|
||||
result[len(result)-1] = '"'
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (b *Bytes) UnmarshalJSON(input []byte) error {
|
||||
raw, err := checkJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec := make([]byte, len(raw)/2)
|
||||
if _, err = hex.Decode(dec, raw); err != nil {
|
||||
err = mapError(err)
|
||||
} else {
|
||||
*b = dec
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// String returns the hex encoding of b.
|
||||
func (b Bytes) String() string {
|
||||
return Encode(b)
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes input as a JSON string with 0x prefix. The length of out
|
||||
// determines the required input length. This function is commonly used to implement the
|
||||
// UnmarshalJSON method for fixed-size types:
|
||||
//
|
||||
// type Foo [8]byte
|
||||
//
|
||||
// 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 {
|
||||
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.
|
||||
type Big big.Int
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (b *Big) MarshalJSON() ([]byte, error) {
|
||||
if 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 := make([]byte, 3, (nbits/8)*2+4)
|
||||
copy(enc, `"0x`)
|
||||
for i := len(bigint.Bits()) - 1; i >= 0; i-- {
|
||||
enc = strconv.AppendUint(enc, uint64(bigint.Bits()[i]), 16)
|
||||
}
|
||||
enc = append(enc, '"')
|
||||
return enc, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (b *Big) UnmarshalJSON(input []byte) error {
|
||||
raw, err := checkNumberJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
words := make([]big.Word, len(raw)/bigWordNibbles+1)
|
||||
end := len(raw)
|
||||
for i := range words {
|
||||
start := end - bigWordNibbles
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
for ri := start; ri < end; ri++ {
|
||||
nib := decodeNibble(raw[ri])
|
||||
if nib == badNibble {
|
||||
return ErrSyntax
|
||||
}
|
||||
words[i] *= 16
|
||||
words[i] += big.Word(nib)
|
||||
}
|
||||
end = start
|
||||
}
|
||||
var dec big.Int
|
||||
dec.SetBits(words)
|
||||
*b = (Big)(dec)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToInt converts b to a big.Int.
|
||||
func (b *Big) ToInt() *big.Int {
|
||||
return (*big.Int)(b)
|
||||
}
|
||||
|
||||
// String returns the hex encoding of b.
|
||||
func (b *Big) String() string {
|
||||
return EncodeBig(b.ToInt())
|
||||
}
|
||||
|
||||
// Uint64 marshals/unmarshals as a JSON string with 0x prefix.
|
||||
// The zero value marshals as "0x0".
|
||||
type Uint64 uint64
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (b Uint64) MarshalJSON() ([]byte, error) {
|
||||
buf := make([]byte, 3, 12)
|
||||
copy(buf, `"0x`)
|
||||
buf = strconv.AppendUint(buf, uint64(b), 16)
|
||||
buf = append(buf, '"')
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (b *Uint64) UnmarshalJSON(input []byte) error {
|
||||
raw, err := checkNumberJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(raw) > 16 {
|
||||
return ErrUint64Range
|
||||
}
|
||||
var dec uint64
|
||||
for _, byte := range raw {
|
||||
nib := decodeNibble(byte)
|
||||
if nib == badNibble {
|
||||
return ErrSyntax
|
||||
}
|
||||
dec *= 16
|
||||
dec += uint64(nib)
|
||||
}
|
||||
*b = Uint64(dec)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the hex encoding of b.
|
||||
func (b Uint64) String() string {
|
||||
return EncodeUint64(uint64(b))
|
||||
}
|
||||
|
||||
// Uint marshals/unmarshals as a JSON string with 0x prefix.
|
||||
// The zero value marshals as "0x0".
|
||||
type Uint uint
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (b Uint) MarshalJSON() ([]byte, error) {
|
||||
return Uint64(b).MarshalJSON()
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (b *Uint) UnmarshalJSON(input []byte) error {
|
||||
var u64 Uint64
|
||||
err := u64.UnmarshalJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if u64 > Uint64(^uint(0)) {
|
||||
return ErrUintRange
|
||||
}
|
||||
*b = Uint(u64)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the hex encoding of b.
|
||||
func (b Uint) String() string {
|
||||
return EncodeUint64(uint64(b))
|
||||
}
|
||||
|
||||
func isString(input []byte) bool {
|
||||
return len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"'
|
||||
}
|
||||
|
||||
func bytesHave0xPrefix(input []byte) bool {
|
||||
return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
|
||||
}
|
||||
|
||||
func checkJSON(input []byte) (raw []byte, err error) {
|
||||
if !isString(input) {
|
||||
return nil, errNonString
|
||||
}
|
||||
if len(input) == 2 {
|
||||
return nil, ErrEmptyString
|
||||
}
|
||||
if !bytesHave0xPrefix(input[1:]) {
|
||||
return nil, ErrMissingPrefix
|
||||
}
|
||||
input = input[3 : len(input)-1]
|
||||
if len(input)%2 != 0 {
|
||||
return nil, ErrOddLength
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func checkNumberJSON(input []byte) (raw []byte, err error) {
|
||||
if !isString(input) {
|
||||
return nil, errNonString
|
||||
}
|
||||
input = input[1 : len(input)-1]
|
||||
if len(input) == 0 {
|
||||
return nil, ErrEmptyString
|
||||
}
|
||||
if !bytesHave0xPrefix(input) {
|
||||
return nil, ErrMissingPrefix
|
||||
}
|
||||
input = input[2:]
|
||||
if len(input) == 0 {
|
||||
return nil, ErrEmptyNumber
|
||||
}
|
||||
if len(input) > 1 && input[0] == '0' {
|
||||
return nil, ErrLeadingZero
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
258
common/hexutil/json_test.go
Normal file
258
common/hexutil/json_test.go
Normal file
@@ -0,0 +1,258 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package hexutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func checkError(t *testing.T, input string, got, want error) bool {
|
||||
if got == nil {
|
||||
if want != nil {
|
||||
t.Errorf("input %s: got no error, want %q", input, want)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if want == nil {
|
||||
t.Errorf("input %s: unexpected error %q", input, got)
|
||||
} else if got.Error() != want.Error() {
|
||||
t.Errorf("input %s: got error %q, want %q", input, got, want)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func referenceBig(s string) *big.Int {
|
||||
b, ok := new(big.Int).SetString(s, 16)
|
||||
if !ok {
|
||||
panic("invalid")
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func referenceBytes(s string) []byte {
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
var unmarshalBytesTests = []unmarshalTest{
|
||||
// invalid encoding
|
||||
{input: "", wantErr: errNonString},
|
||||
{input: "null", wantErr: errNonString},
|
||||
{input: "10", wantErr: errNonString},
|
||||
{input: `""`, wantErr: ErrEmptyString},
|
||||
{input: `"0"`, wantErr: ErrMissingPrefix},
|
||||
{input: `"0x0"`, wantErr: ErrOddLength},
|
||||
{input: `"0xxx"`, wantErr: ErrSyntax},
|
||||
{input: `"0x01zz01"`, wantErr: ErrSyntax},
|
||||
|
||||
// valid encoding
|
||||
{input: `"0x"`, want: referenceBytes("")},
|
||||
{input: `"0x02"`, want: referenceBytes("02")},
|
||||
{input: `"0X02"`, want: referenceBytes("02")},
|
||||
{input: `"0xffffffffff"`, want: referenceBytes("ffffffffff")},
|
||||
{
|
||||
input: `"0xffffffffffffffffffffffffffffffffffff"`,
|
||||
want: referenceBytes("ffffffffffffffffffffffffffffffffffff"),
|
||||
},
|
||||
}
|
||||
|
||||
func TestUnmarshalBytes(t *testing.T) {
|
||||
for _, test := range unmarshalBytesTests {
|
||||
var v Bytes
|
||||
err := v.UnmarshalJSON([]byte(test.input))
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(test.want.([]byte), []byte(v)) {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, &v, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalBytes(b *testing.B) {
|
||||
input := []byte(`"0x123456789abcdef123456789abcdef"`)
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v Bytes
|
||||
if err := v.UnmarshalJSON(input); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalBytes(t *testing.T) {
|
||||
for _, test := range encodeBytesTests {
|
||||
in := test.input.([]byte)
|
||||
out, err := Bytes(in).MarshalJSON()
|
||||
if err != nil {
|
||||
t.Errorf("%x: %v", in, err)
|
||||
continue
|
||||
}
|
||||
if want := `"` + test.want + `"`; string(out) != want {
|
||||
t.Errorf("%x: MarshalJSON output mismatch: got %q, want %q", in, out, want)
|
||||
continue
|
||||
}
|
||||
if out := Bytes(in).String(); out != test.want {
|
||||
t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unmarshalBigTests = []unmarshalTest{
|
||||
// invalid encoding
|
||||
{input: "", wantErr: errNonString},
|
||||
{input: "null", wantErr: errNonString},
|
||||
{input: "10", wantErr: errNonString},
|
||||
{input: `""`, wantErr: ErrEmptyString},
|
||||
{input: `"0"`, wantErr: ErrMissingPrefix},
|
||||
{input: `"0x"`, wantErr: ErrEmptyNumber},
|
||||
{input: `"0x01"`, wantErr: ErrLeadingZero},
|
||||
{input: `"0xx"`, wantErr: ErrSyntax},
|
||||
{input: `"0x1zz01"`, wantErr: ErrSyntax},
|
||||
|
||||
// valid encoding
|
||||
{input: `"0x0"`, want: big.NewInt(0)},
|
||||
{input: `"0x2"`, want: big.NewInt(0x2)},
|
||||
{input: `"0x2F2"`, want: big.NewInt(0x2f2)},
|
||||
{input: `"0X2F2"`, want: big.NewInt(0x2f2)},
|
||||
{input: `"0x1122aaff"`, want: big.NewInt(0x1122aaff)},
|
||||
{input: `"0xbBb"`, want: big.NewInt(0xbbb)},
|
||||
{input: `"0xfffffffff"`, want: big.NewInt(0xfffffffff)},
|
||||
{
|
||||
input: `"0x112233445566778899aabbccddeeff"`,
|
||||
want: referenceBig("112233445566778899aabbccddeeff"),
|
||||
},
|
||||
{
|
||||
input: `"0xffffffffffffffffffffffffffffffffffff"`,
|
||||
want: referenceBig("ffffffffffffffffffffffffffffffffffff"),
|
||||
},
|
||||
}
|
||||
|
||||
func TestUnmarshalBig(t *testing.T) {
|
||||
for _, test := range unmarshalBigTests {
|
||||
var v Big
|
||||
err := v.UnmarshalJSON([]byte(test.input))
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if test.want != nil && test.want.(*big.Int).Cmp((*big.Int)(&v)) != 0 {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, (*big.Int)(&v), test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalBig(b *testing.B) {
|
||||
input := []byte(`"0x123456789abcdef123456789abcdef"`)
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v Big
|
||||
if err := v.UnmarshalJSON(input); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalBig(t *testing.T) {
|
||||
for _, test := range encodeBigTests {
|
||||
in := test.input.(*big.Int)
|
||||
out, err := (*Big)(in).MarshalJSON()
|
||||
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 := (*Big)(in).String(); out != test.want {
|
||||
t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unmarshalUint64Tests = []unmarshalTest{
|
||||
// invalid encoding
|
||||
{input: "", wantErr: errNonString},
|
||||
{input: "null", wantErr: errNonString},
|
||||
{input: "10", wantErr: errNonString},
|
||||
{input: `""`, wantErr: ErrEmptyString},
|
||||
{input: `"0"`, wantErr: ErrMissingPrefix},
|
||||
{input: `"0x"`, wantErr: ErrEmptyNumber},
|
||||
{input: `"0x01"`, wantErr: ErrLeadingZero},
|
||||
{input: `"0xfffffffffffffffff"`, wantErr: ErrUint64Range},
|
||||
{input: `"0xx"`, wantErr: ErrSyntax},
|
||||
{input: `"0x1zz01"`, wantErr: ErrSyntax},
|
||||
|
||||
// valid encoding
|
||||
{input: `"0x0"`, want: uint64(0)},
|
||||
{input: `"0x2"`, want: uint64(0x2)},
|
||||
{input: `"0x2F2"`, want: uint64(0x2f2)},
|
||||
{input: `"0X2F2"`, want: uint64(0x2f2)},
|
||||
{input: `"0x1122aaff"`, want: uint64(0x1122aaff)},
|
||||
{input: `"0xbbb"`, want: uint64(0xbbb)},
|
||||
{input: `"0xffffffffffffffff"`, want: uint64(0xffffffffffffffff)},
|
||||
}
|
||||
|
||||
func TestUnmarshalUint64(t *testing.T) {
|
||||
for _, test := range unmarshalUint64Tests {
|
||||
var v Uint64
|
||||
err := v.UnmarshalJSON([]byte(test.input))
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if uint64(v) != test.want.(uint64) {
|
||||
t.Errorf("input %s: value mismatch: got %d, want %d", test.input, v, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalUint64(b *testing.B) {
|
||||
input := []byte(`"0x123456789abcdf"`)
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v Uint64
|
||||
v.UnmarshalJSON(input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUint64(t *testing.T) {
|
||||
for _, test := range encodeUint64Tests {
|
||||
in := test.input.(uint64)
|
||||
out, err := Uint64(in).MarshalJSON()
|
||||
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 := (Uint64)(in).String(); out != test.want {
|
||||
t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +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 httpclient
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
type HTTPClient struct {
|
||||
*http.Transport
|
||||
DocRoot string
|
||||
schemes []string
|
||||
}
|
||||
|
||||
func New(docRoot string) (self *HTTPClient) {
|
||||
self = &HTTPClient{
|
||||
Transport: &http.Transport{},
|
||||
DocRoot: docRoot,
|
||||
schemes: []string{"file"},
|
||||
}
|
||||
self.RegisterProtocol("file", http.NewFileTransport(http.Dir(self.DocRoot)))
|
||||
return
|
||||
}
|
||||
|
||||
// Clients should be reused instead of created as needed. Clients are safe for concurrent use by multiple goroutines.
|
||||
|
||||
// A Client is higher-level than a RoundTripper (such as Transport) and additionally handles HTTP details such as cookies and redirects.
|
||||
|
||||
func (self *HTTPClient) Client() *http.Client {
|
||||
return &http.Client{
|
||||
Transport: self,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *HTTPClient) RegisterScheme(scheme string, rt http.RoundTripper) {
|
||||
self.schemes = append(self.schemes, scheme)
|
||||
self.RegisterProtocol(scheme, rt)
|
||||
}
|
||||
|
||||
func (self *HTTPClient) HasScheme(scheme string) bool {
|
||||
for _, s := range self.schemes {
|
||||
if s == scheme {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *HTTPClient) GetAuthContent(uri string, hash common.Hash) ([]byte, error) {
|
||||
// retrieve content
|
||||
content, err := self.Get(uri, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// check hash to authenticate content
|
||||
chash := crypto.Keccak256Hash(content)
|
||||
if chash != hash {
|
||||
return nil, fmt.Errorf("content hash mismatch %x != %x (exp)", hash[:], chash[:])
|
||||
}
|
||||
|
||||
return content, nil
|
||||
|
||||
}
|
||||
|
||||
// Get(uri, path) downloads the document at uri, if path is non-empty it
|
||||
// is interpreted as a filepath to which the contents are saved
|
||||
func (self *HTTPClient) Get(uri, path string) ([]byte, error) {
|
||||
// retrieve content
|
||||
resp, err := self.Client().Get(uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
var content []byte
|
||||
content, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode/100 != 2 {
|
||||
return content, fmt.Errorf("HTTP error: %s", resp.Status)
|
||||
}
|
||||
|
||||
if path != "" {
|
||||
var abspath string
|
||||
abspath, err = filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = ioutil.WriteFile(abspath, content, 0600)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return content, nil
|
||||
|
||||
}
|
||||
@@ -1,77 +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 httpclient
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
func TestGetAuthContent(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "httpclient-test")
|
||||
if err != nil {
|
||||
t.Fatal("cannot create temporary directory:", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
client := New(dir)
|
||||
|
||||
text := "test"
|
||||
hash := crypto.Keccak256Hash([]byte(text))
|
||||
if err := ioutil.WriteFile(path.Join(dir, "test.content"), []byte(text), os.ModePerm); err != nil {
|
||||
t.Fatal("could not write test file", err)
|
||||
}
|
||||
content, err := client.GetAuthContent("file:///test.content", hash)
|
||||
if err != nil {
|
||||
t.Errorf("no error expected, got %v", err)
|
||||
}
|
||||
if string(content) != text {
|
||||
t.Errorf("incorrect content. expected %v, got %v", text, string(content))
|
||||
}
|
||||
|
||||
hash = common.Hash{}
|
||||
content, err = client.GetAuthContent("file:///test.content", hash)
|
||||
expected := "content hash mismatch 0000000000000000000000000000000000000000000000000000000000000000 != 9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658 (exp)"
|
||||
if err == nil {
|
||||
t.Errorf("expected error, got nothing")
|
||||
} else {
|
||||
if err.Error() != expected {
|
||||
t.Errorf("expected error '%s' got '%v'", expected, err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type rt struct{}
|
||||
|
||||
func (rt) RoundTrip(req *http.Request) (resp *http.Response, err error) { return }
|
||||
|
||||
func TestRegisterScheme(t *testing.T) {
|
||||
client := New("/tmp/")
|
||||
if client.HasScheme("scheme") {
|
||||
t.Errorf("expected scheme not to be registered")
|
||||
}
|
||||
client.RegisterScheme("scheme", rt{})
|
||||
if !client.HasScheme("scheme") {
|
||||
t.Errorf("expected scheme to be registered")
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,279 +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 ethreg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/compiler"
|
||||
"github.com/ethereum/go-ethereum/common/registrar"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// registryAPIBackend is a backend for an Ethereum Registry.
|
||||
type registryAPIBackend struct {
|
||||
config *params.ChainConfig
|
||||
bc *core.BlockChain
|
||||
chainDb ethdb.Database
|
||||
txPool *core.TxPool
|
||||
am *accounts.Manager
|
||||
}
|
||||
|
||||
// PrivateRegistarAPI offers various functions to access the Ethereum registry.
|
||||
type PrivateRegistarAPI struct {
|
||||
config *params.ChainConfig
|
||||
be *registryAPIBackend
|
||||
}
|
||||
|
||||
// NewPrivateRegistarAPI creates a new PrivateRegistarAPI instance.
|
||||
func NewPrivateRegistarAPI(config *params.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI {
|
||||
return &PrivateRegistarAPI{
|
||||
config: config,
|
||||
be: ®istryAPIBackend{
|
||||
config: config,
|
||||
bc: bc,
|
||||
chainDb: chainDb,
|
||||
txPool: txPool,
|
||||
am: am,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SetGlobalRegistrar allows clients to set the global registry for the node.
|
||||
// This method can be used to deploy a new registry. First zero out the current
|
||||
// address by calling the method with namereg = '0x0' and then call this method
|
||||
// again with '' as namereg. This will submit a transaction to the network which
|
||||
// will deploy a new registry on execution. The TX hash is returned. When called
|
||||
// with namereg '' and the current address is not zero the current global is
|
||||
// address is returned..
|
||||
func (api *PrivateRegistarAPI) SetGlobalRegistrar(namereg string, from common.Address) (string, error) {
|
||||
return registrar.New(api.be).SetGlobalRegistrar(namereg, from)
|
||||
}
|
||||
|
||||
// SetHashReg queries the registry for a hash.
|
||||
func (api *PrivateRegistarAPI) SetHashReg(hashreg string, from common.Address) (string, error) {
|
||||
return registrar.New(api.be).SetHashReg(hashreg, from)
|
||||
}
|
||||
|
||||
// SetUrlHint queries the registry for an url.
|
||||
func (api *PrivateRegistarAPI) SetUrlHint(hashreg string, from common.Address) (string, error) {
|
||||
return registrar.New(api.be).SetUrlHint(hashreg, from)
|
||||
}
|
||||
|
||||
// SaveInfo stores contract information on the local file system.
|
||||
func (api *PrivateRegistarAPI) SaveInfo(info *compiler.ContractInfo, filename string) (contenthash common.Hash, err error) {
|
||||
return compiler.SaveInfo(info, filename)
|
||||
}
|
||||
|
||||
// Register registers a new content hash in the registry.
|
||||
func (api *PrivateRegistarAPI) Register(sender common.Address, addr common.Address, contentHashHex string) (bool, error) {
|
||||
block := api.be.bc.CurrentBlock()
|
||||
state, err := state.New(block.Root(), api.be.chainDb)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
codeb := state.GetCode(addr)
|
||||
codeHash := common.BytesToHash(crypto.Keccak256(codeb))
|
||||
contentHash := common.HexToHash(contentHashHex)
|
||||
|
||||
_, err = registrar.New(api.be).SetHashToHash(sender, codeHash, contentHash)
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
// RegisterUrl registers a new url in the registry.
|
||||
func (api *PrivateRegistarAPI) RegisterUrl(sender common.Address, contentHashHex string, url string) (bool, error) {
|
||||
_, err := registrar.New(api.be).SetUrlToHash(sender, common.HexToHash(contentHashHex), url)
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
// callmsg is the message type used for call transations.
|
||||
type callmsg struct {
|
||||
from *state.StateObject
|
||||
to *common.Address
|
||||
gas, gasPrice *big.Int
|
||||
value *big.Int
|
||||
data []byte
|
||||
}
|
||||
|
||||
// accessor boilerplate to implement core.Message
|
||||
func (m callmsg) From() (common.Address, error) {
|
||||
return m.from.Address(), nil
|
||||
}
|
||||
func (m callmsg) FromFrontier() (common.Address, error) {
|
||||
return m.from.Address(), nil
|
||||
}
|
||||
func (m callmsg) Nonce() uint64 {
|
||||
return 0
|
||||
}
|
||||
func (m callmsg) CheckNonce() bool {
|
||||
return false
|
||||
}
|
||||
func (m callmsg) To() *common.Address {
|
||||
return m.to
|
||||
}
|
||||
func (m callmsg) GasPrice() *big.Int {
|
||||
return m.gasPrice
|
||||
}
|
||||
func (m callmsg) Gas() *big.Int {
|
||||
return m.gas
|
||||
}
|
||||
func (m callmsg) Value() *big.Int {
|
||||
return m.value
|
||||
}
|
||||
func (m callmsg) Data() []byte {
|
||||
return m.data
|
||||
}
|
||||
|
||||
// Call forms a transaction from the given arguments and tries to execute it on
|
||||
// a private VM with a copy of the state. Any changes are therefore only temporary
|
||||
// and not part of the actual state. This allows for local execution/queries.
|
||||
func (be *registryAPIBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) {
|
||||
block := be.bc.CurrentBlock()
|
||||
statedb, err := state.New(block.Root(), be.chainDb)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
var from *state.StateObject
|
||||
if len(fromStr) == 0 {
|
||||
accounts := be.am.Accounts()
|
||||
if len(accounts) == 0 {
|
||||
from = statedb.GetOrNewStateObject(common.Address{})
|
||||
} else {
|
||||
from = statedb.GetOrNewStateObject(accounts[0].Address)
|
||||
}
|
||||
} else {
|
||||
from = statedb.GetOrNewStateObject(common.HexToAddress(fromStr))
|
||||
}
|
||||
|
||||
from.SetBalance(common.MaxBig)
|
||||
|
||||
var to *common.Address
|
||||
if len(toStr) > 0 {
|
||||
addr := common.HexToAddress(toStr)
|
||||
to = &addr
|
||||
}
|
||||
gas := common.Big(gasStr)
|
||||
if gas.BitLen() == 0 {
|
||||
gas = big.NewInt(50000000)
|
||||
}
|
||||
gasPrice := common.Big(gasPriceStr)
|
||||
if gasPrice.BitLen() == 0 {
|
||||
gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon)
|
||||
}
|
||||
msg := types.NewMessage(from.Address(), to, 0, common.Big(valueStr), gas, gasPrice, common.FromHex(dataStr), false)
|
||||
|
||||
header := be.bc.CurrentBlock().Header()
|
||||
vmenv := core.NewEnv(statedb, be.config, be.bc, msg, header, vm.Config{})
|
||||
gp := new(core.GasPool).AddGas(common.MaxBig)
|
||||
res, gas, err := core.ApplyMessage(vmenv, msg, gp)
|
||||
|
||||
return common.ToHex(res), gas.String(), err
|
||||
}
|
||||
|
||||
// StorageAt returns the data stores in the state for the given address and location.
|
||||
func (be *registryAPIBackend) StorageAt(addr string, storageAddr string) string {
|
||||
block := be.bc.CurrentBlock()
|
||||
state, err := state.New(block.Root(), be.chainDb)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return state.GetState(common.HexToAddress(addr), common.HexToHash(storageAddr)).Hex()
|
||||
}
|
||||
|
||||
// Transact forms a transaction from the given arguments and submits it to the
|
||||
// transactio pool for execution.
|
||||
func (be *registryAPIBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
|
||||
if len(toStr) > 0 && toStr != "0x" && !common.IsHexAddress(toStr) {
|
||||
return "", errors.New("invalid address")
|
||||
}
|
||||
|
||||
var (
|
||||
from = common.HexToAddress(fromStr)
|
||||
to = common.HexToAddress(toStr)
|
||||
value = common.Big(valueStr)
|
||||
gas *big.Int
|
||||
price *big.Int
|
||||
data []byte
|
||||
contractCreation bool
|
||||
)
|
||||
|
||||
if len(gasStr) == 0 {
|
||||
gas = big.NewInt(90000)
|
||||
} else {
|
||||
gas = common.Big(gasStr)
|
||||
}
|
||||
|
||||
if len(gasPriceStr) == 0 {
|
||||
price = big.NewInt(10000000000000)
|
||||
} else {
|
||||
price = common.Big(gasPriceStr)
|
||||
}
|
||||
|
||||
data = common.FromHex(codeStr)
|
||||
if len(toStr) == 0 {
|
||||
contractCreation = true
|
||||
}
|
||||
|
||||
nonce := be.txPool.State().GetNonce(from)
|
||||
if len(nonceStr) != 0 {
|
||||
nonce = common.Big(nonceStr).Uint64()
|
||||
}
|
||||
|
||||
var tx *types.Transaction
|
||||
if contractCreation {
|
||||
tx = types.NewContractCreation(nonce, value, gas, price, data)
|
||||
} else {
|
||||
tx = types.NewTransaction(nonce, to, value, gas, price, data)
|
||||
}
|
||||
|
||||
sigHash := (types.HomesteadSigner{}).Hash(tx)
|
||||
signature, err := be.am.SignEthereum(from, sigHash.Bytes())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
signedTx, err := tx.WithSignature(types.HomesteadSigner{}, signature)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
be.txPool.SetLocal(signedTx)
|
||||
if err := be.txPool.Add(signedTx); err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if contractCreation {
|
||||
addr := crypto.CreateAddress(from, nonce)
|
||||
glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex())
|
||||
} else {
|
||||
glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signedTx.Hash().Hex(), tx.To().Hex())
|
||||
}
|
||||
|
||||
return signedTx.Hash().Hex(), nil
|
||||
}
|
||||
@@ -1,436 +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 registrar
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"regexp"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
)
|
||||
|
||||
/*
|
||||
Registrar implements the Ethereum name registrar services mapping
|
||||
- arbitrary strings to ethereum addresses
|
||||
- hashes to hashes
|
||||
- hashes to arbitrary strings
|
||||
(likely will provide lookup service for all three)
|
||||
|
||||
The Registrar is used by
|
||||
* the roundtripper transport implementation of
|
||||
url schemes to resolve domain names and services that register these names
|
||||
* contract info retrieval (NatSpec).
|
||||
|
||||
The Registrar uses 3 contracts on the blockchain:
|
||||
* GlobalRegistrar: Name (string) -> Address (Owner)
|
||||
* HashReg : Key Hash (hash of domain name or contract code) -> Content Hash
|
||||
* UrlHint : Content Hash -> Url Hint
|
||||
|
||||
These contracts are (currently) not included in the genesis block.
|
||||
Each Set<X> needs to be called once on each blockchain/network once.
|
||||
|
||||
Contract addresses need to be set the first time any Registrar method is called
|
||||
in a client session.
|
||||
This is done for frontier by default, otherwise the caller needs to make sure
|
||||
the relevant environment initialised the desired contracts
|
||||
*/
|
||||
var (
|
||||
// GlobalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b" // olympic
|
||||
GlobalRegistrarAddr = "0x33990122638b9132ca29c723bdf037f1a891a70c" // frontier
|
||||
HashRegAddr = "0x23bf622b5a65f6060d855fca401133ded3520620" // frontier
|
||||
UrlHintAddr = "0x73ed5ef6c010727dfd2671dbb70faac19ec18626" // frontier
|
||||
|
||||
zero = regexp.MustCompile("^(0x)?0*$")
|
||||
)
|
||||
|
||||
const (
|
||||
trueHex = "0000000000000000000000000000000000000000000000000000000000000001"
|
||||
falseHex = "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
)
|
||||
|
||||
func abiSignature(s string) string {
|
||||
return common.ToHex(crypto.Keccak256([]byte(s))[:4])
|
||||
}
|
||||
|
||||
var (
|
||||
HashRegName = "HashReg"
|
||||
UrlHintName = "UrlHint"
|
||||
|
||||
registerContentHashAbi = abiSignature("register(uint256,uint256)")
|
||||
registerUrlAbi = abiSignature("register(uint256,uint8,uint256)")
|
||||
setOwnerAbi = abiSignature("setowner()")
|
||||
reserveAbi = abiSignature("reserve(bytes32)")
|
||||
resolveAbi = abiSignature("addr(bytes32)")
|
||||
registerAbi = abiSignature("setAddress(bytes32,address,bool)")
|
||||
addressAbiPrefix = falseHex[:24]
|
||||
)
|
||||
|
||||
// Registrar's backend is defined as an interface (implemented by xeth, but could be remote)
|
||||
type Backend interface {
|
||||
StorageAt(string, string) string
|
||||
Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error)
|
||||
Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error)
|
||||
}
|
||||
|
||||
// TODO Registrar should also just implement The Resolver and Registry interfaces
|
||||
// Simplify for now.
|
||||
type VersionedRegistrar interface {
|
||||
Resolver(*big.Int) *Registrar
|
||||
Registry() *Registrar
|
||||
}
|
||||
|
||||
type Registrar struct {
|
||||
backend Backend
|
||||
}
|
||||
|
||||
func New(b Backend) (res *Registrar) {
|
||||
res = &Registrar{b}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Registrar) SetGlobalRegistrar(namereg string, addr common.Address) (txhash string, err error) {
|
||||
if namereg != "" {
|
||||
GlobalRegistrarAddr = namereg
|
||||
return
|
||||
}
|
||||
if zero.MatchString(GlobalRegistrarAddr) {
|
||||
if (addr == common.Address{}) {
|
||||
err = fmt.Errorf("GlobalRegistrar address not found and sender for creation not given")
|
||||
return
|
||||
} else {
|
||||
txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "800000", "", GlobalRegistrarCode)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("GlobalRegistrar address not found and sender for creation failed: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Registrar) SetHashReg(hashreg string, addr common.Address) (txhash string, err error) {
|
||||
if hashreg != "" {
|
||||
HashRegAddr = hashreg
|
||||
} else {
|
||||
if !zero.MatchString(HashRegAddr) {
|
||||
return
|
||||
}
|
||||
nameHex, extra := encodeName(HashRegName, 2)
|
||||
hashRegAbi := resolveAbi + nameHex + extra
|
||||
glog.V(logger.Detail).Infof("\ncall HashRegAddr %v with %v\n", GlobalRegistrarAddr, hashRegAbi)
|
||||
var res string
|
||||
res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", hashRegAbi)
|
||||
if len(res) >= 40 {
|
||||
HashRegAddr = "0x" + res[len(res)-40:len(res)]
|
||||
}
|
||||
if err != nil || zero.MatchString(HashRegAddr) {
|
||||
if (addr == common.Address{}) {
|
||||
err = fmt.Errorf("HashReg address not found and sender for creation not given")
|
||||
return
|
||||
}
|
||||
|
||||
txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "", "", HashRegCode)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("HashReg address not found and sender for creation failed: %v", err)
|
||||
}
|
||||
glog.V(logger.Detail).Infof("created HashRegAddr @ txhash %v\n", txhash)
|
||||
} else {
|
||||
glog.V(logger.Detail).Infof("HashRegAddr found at @ %v\n", HashRegAddr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Registrar) SetUrlHint(urlhint string, addr common.Address) (txhash string, err error) {
|
||||
if urlhint != "" {
|
||||
UrlHintAddr = urlhint
|
||||
} else {
|
||||
if !zero.MatchString(UrlHintAddr) {
|
||||
return
|
||||
}
|
||||
nameHex, extra := encodeName(UrlHintName, 2)
|
||||
urlHintAbi := resolveAbi + nameHex + extra
|
||||
glog.V(logger.Detail).Infof("UrlHint address query data: %s to %s", urlHintAbi, GlobalRegistrarAddr)
|
||||
var res string
|
||||
res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", urlHintAbi)
|
||||
if len(res) >= 40 {
|
||||
UrlHintAddr = "0x" + res[len(res)-40:len(res)]
|
||||
}
|
||||
if err != nil || zero.MatchString(UrlHintAddr) {
|
||||
if (addr == common.Address{}) {
|
||||
err = fmt.Errorf("UrlHint address not found and sender for creation not given")
|
||||
return
|
||||
}
|
||||
txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "210000", "", UrlHintCode)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("UrlHint address not found and sender for creation failed: %v", err)
|
||||
}
|
||||
glog.V(logger.Detail).Infof("created UrlHint @ txhash %v\n", txhash)
|
||||
} else {
|
||||
glog.V(logger.Detail).Infof("UrlHint found @ %v\n", HashRegAddr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ReserveName(from, name) reserves name for the sender address in the globalRegistrar
|
||||
// the tx needs to be mined to take effect
|
||||
func (self *Registrar) ReserveName(address common.Address, name string) (txh string, err error) {
|
||||
if zero.MatchString(GlobalRegistrarAddr) {
|
||||
return "", fmt.Errorf("GlobalRegistrar address is not set")
|
||||
}
|
||||
nameHex, extra := encodeName(name, 2)
|
||||
abi := reserveAbi + nameHex + extra
|
||||
glog.V(logger.Detail).Infof("Reserve data: %s", abi)
|
||||
return self.backend.Transact(
|
||||
address.Hex(),
|
||||
GlobalRegistrarAddr,
|
||||
"", "", "", "",
|
||||
abi,
|
||||
)
|
||||
}
|
||||
|
||||
// SetAddressToName(from, name, addr) will set the Address to address for name
|
||||
// in the globalRegistrar using from as the sender of the transaction
|
||||
// the tx needs to be mined to take effect
|
||||
func (self *Registrar) SetAddressToName(from common.Address, name string, address common.Address) (txh string, err error) {
|
||||
if zero.MatchString(GlobalRegistrarAddr) {
|
||||
return "", fmt.Errorf("GlobalRegistrar address is not set")
|
||||
}
|
||||
|
||||
nameHex, extra := encodeName(name, 6)
|
||||
addrHex := encodeAddress(address)
|
||||
|
||||
abi := registerAbi + nameHex + addrHex + trueHex + extra
|
||||
glog.V(logger.Detail).Infof("SetAddressToName data: %s to %s ", abi, GlobalRegistrarAddr)
|
||||
|
||||
return self.backend.Transact(
|
||||
from.Hex(),
|
||||
GlobalRegistrarAddr,
|
||||
"", "", "", "",
|
||||
abi,
|
||||
)
|
||||
}
|
||||
|
||||
// NameToAddr(from, name) queries the registrar for the address on name
|
||||
func (self *Registrar) NameToAddr(from common.Address, name string) (address common.Address, err error) {
|
||||
if zero.MatchString(GlobalRegistrarAddr) {
|
||||
return address, fmt.Errorf("GlobalRegistrar address is not set")
|
||||
}
|
||||
|
||||
nameHex, extra := encodeName(name, 2)
|
||||
abi := resolveAbi + nameHex + extra
|
||||
glog.V(logger.Detail).Infof("NameToAddr data: %s", abi)
|
||||
res, _, err := self.backend.Call(
|
||||
from.Hex(),
|
||||
GlobalRegistrarAddr,
|
||||
"", "", "",
|
||||
abi,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
address = common.HexToAddress(res)
|
||||
return
|
||||
}
|
||||
|
||||
// called as first step in the registration process on HashReg
|
||||
func (self *Registrar) SetOwner(address common.Address) (txh string, err error) {
|
||||
if zero.MatchString(HashRegAddr) {
|
||||
return "", fmt.Errorf("HashReg address is not set")
|
||||
}
|
||||
return self.backend.Transact(
|
||||
address.Hex(),
|
||||
HashRegAddr,
|
||||
"", "", "", "",
|
||||
setOwnerAbi,
|
||||
)
|
||||
}
|
||||
|
||||
// registers some content hash to a key/code hash
|
||||
// e.g., the contract Info combined Json Doc's ContentHash
|
||||
// to CodeHash of a contract or hash of a domain
|
||||
func (self *Registrar) SetHashToHash(address common.Address, codehash, dochash common.Hash) (txh string, err error) {
|
||||
if zero.MatchString(HashRegAddr) {
|
||||
return "", fmt.Errorf("HashReg address is not set")
|
||||
}
|
||||
|
||||
_, err = self.SetOwner(address)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
codehex := common.Bytes2Hex(codehash[:])
|
||||
dochex := common.Bytes2Hex(dochash[:])
|
||||
|
||||
data := registerContentHashAbi + codehex + dochex
|
||||
glog.V(logger.Detail).Infof("SetHashToHash data: %s sent to %v\n", data, HashRegAddr)
|
||||
return self.backend.Transact(
|
||||
address.Hex(),
|
||||
HashRegAddr,
|
||||
"", "", "", "",
|
||||
data,
|
||||
)
|
||||
}
|
||||
|
||||
// SetUrlToHash(from, hash, url) registers a url to a content hash so that the content can be fetched
|
||||
// address is used as sender for the transaction and will be the owner of a new
|
||||
// registry entry on first time use
|
||||
// FIXME: silently doing nothing if sender is not the owner
|
||||
// note that with content addressed storage, this step is no longer necessary
|
||||
func (self *Registrar) SetUrlToHash(address common.Address, hash common.Hash, url string) (txh string, err error) {
|
||||
if zero.MatchString(UrlHintAddr) {
|
||||
return "", fmt.Errorf("UrlHint address is not set")
|
||||
}
|
||||
|
||||
hashHex := common.Bytes2Hex(hash[:])
|
||||
var urlHex string
|
||||
urlb := []byte(url)
|
||||
var cnt byte
|
||||
n := len(urlb)
|
||||
|
||||
for n > 0 {
|
||||
if n > 32 {
|
||||
n = 32
|
||||
}
|
||||
urlHex = common.Bytes2Hex(urlb[:n])
|
||||
urlb = urlb[n:]
|
||||
n = len(urlb)
|
||||
bcnt := make([]byte, 32)
|
||||
bcnt[31] = cnt
|
||||
data := registerUrlAbi +
|
||||
hashHex +
|
||||
common.Bytes2Hex(bcnt) +
|
||||
common.Bytes2Hex(common.Hex2BytesFixed(urlHex, 32))
|
||||
txh, err = self.backend.Transact(
|
||||
address.Hex(),
|
||||
UrlHintAddr,
|
||||
"", "", "", "",
|
||||
data,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cnt++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HashToHash(key) resolves contenthash for key (a hash) using HashReg
|
||||
// resolution is costless non-transactional
|
||||
// implemented as direct retrieval from db
|
||||
func (self *Registrar) HashToHash(khash common.Hash) (chash common.Hash, err error) {
|
||||
if zero.MatchString(HashRegAddr) {
|
||||
return common.Hash{}, fmt.Errorf("HashReg address is not set")
|
||||
}
|
||||
|
||||
// look up in hashReg
|
||||
at := HashRegAddr[2:]
|
||||
key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:]))
|
||||
hash := self.backend.StorageAt(at, key)
|
||||
|
||||
if hash == "0x0" || len(hash) < 3 || (hash == common.Hash{}.Hex()) {
|
||||
err = fmt.Errorf("HashToHash: content hash not found for '%v'", khash.Hex())
|
||||
return
|
||||
}
|
||||
copy(chash[:], common.Hex2BytesFixed(hash[2:], 32))
|
||||
return
|
||||
}
|
||||
|
||||
// HashToUrl(contenthash) resolves the url for contenthash using UrlHint
|
||||
// resolution is costless non-transactional
|
||||
// implemented as direct retrieval from db
|
||||
// if we use content addressed storage, this step is no longer necessary
|
||||
func (self *Registrar) HashToUrl(chash common.Hash) (uri string, err error) {
|
||||
if zero.MatchString(UrlHintAddr) {
|
||||
return "", fmt.Errorf("UrlHint address is not set")
|
||||
}
|
||||
// look up in URL reg
|
||||
var str string = " "
|
||||
var idx uint32
|
||||
for len(str) > 0 {
|
||||
mapaddr := storageMapping(storageIdx2Addr(1), chash[:])
|
||||
key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(idx)))
|
||||
hex := self.backend.StorageAt(UrlHintAddr[2:], key)
|
||||
str = string(common.Hex2Bytes(hex[2:]))
|
||||
l := 0
|
||||
for (l < len(str)) && (str[l] == 0) {
|
||||
l++
|
||||
}
|
||||
|
||||
str = str[l:]
|
||||
uri = uri + str
|
||||
idx++
|
||||
}
|
||||
|
||||
if len(uri) == 0 {
|
||||
err = fmt.Errorf("HashToUrl: URL hint not found for '%v'", chash.Hex())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func storageIdx2Addr(varidx uint32) []byte {
|
||||
data := make([]byte, 32)
|
||||
binary.BigEndian.PutUint32(data[28:32], varidx)
|
||||
return data
|
||||
}
|
||||
|
||||
func storageMapping(addr, key []byte) []byte {
|
||||
data := make([]byte, 64)
|
||||
copy(data[0:32], key[0:32])
|
||||
copy(data[32:64], addr[0:32])
|
||||
sha := crypto.Keccak256(data)
|
||||
return sha
|
||||
}
|
||||
|
||||
func storageFixedArray(addr, idx []byte) []byte {
|
||||
var carry byte
|
||||
for i := 31; i >= 0; i-- {
|
||||
var b byte = addr[i] + idx[i] + carry
|
||||
if b < addr[i] {
|
||||
carry = 1
|
||||
} else {
|
||||
carry = 0
|
||||
}
|
||||
addr[i] = b
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
func storageAddress(addr []byte) string {
|
||||
return common.ToHex(addr)
|
||||
}
|
||||
|
||||
func encodeAddress(address common.Address) string {
|
||||
return addressAbiPrefix + address.Hex()[2:]
|
||||
}
|
||||
|
||||
func encodeName(name string, index uint8) (string, string) {
|
||||
extra := common.Bytes2Hex([]byte(name))
|
||||
if len(name) > 32 {
|
||||
return fmt.Sprintf("%064x", index), extra
|
||||
}
|
||||
return extra + falseHex[len(extra):], ""
|
||||
}
|
||||
@@ -1,158 +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 registrar
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
type testBackend struct {
|
||||
// contracts mock
|
||||
contracts map[string](map[string]string)
|
||||
}
|
||||
|
||||
var (
|
||||
text = "test"
|
||||
codehash = common.StringToHash("1234")
|
||||
hash = common.BytesToHash(crypto.Keccak256([]byte(text)))
|
||||
url = "bzz://bzzhash/my/path/contr.act"
|
||||
)
|
||||
|
||||
func NewTestBackend() *testBackend {
|
||||
self := &testBackend{}
|
||||
self.contracts = make(map[string](map[string]string))
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *testBackend) initHashReg() {
|
||||
self.contracts[HashRegAddr[2:]] = make(map[string]string)
|
||||
key := storageAddress(storageMapping(storageIdx2Addr(1), codehash[:]))
|
||||
self.contracts[HashRegAddr[2:]][key] = hash.Hex()
|
||||
}
|
||||
|
||||
func (self *testBackend) initUrlHint() {
|
||||
self.contracts[UrlHintAddr[2:]] = make(map[string]string)
|
||||
mapaddr := storageMapping(storageIdx2Addr(1), hash[:])
|
||||
|
||||
key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(0)))
|
||||
self.contracts[UrlHintAddr[2:]][key] = common.ToHex([]byte(url))
|
||||
key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(1)))
|
||||
self.contracts[UrlHintAddr[2:]][key] = "0x0"
|
||||
}
|
||||
|
||||
func (self *testBackend) StorageAt(ca, sa string) (res string) {
|
||||
c := self.contracts[ca]
|
||||
if c == nil {
|
||||
return "0x0"
|
||||
}
|
||||
res = c[sa]
|
||||
return
|
||||
}
|
||||
|
||||
func (self *testBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (self *testBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error) {
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
func TestSetGlobalRegistrar(t *testing.T) {
|
||||
b := NewTestBackend()
|
||||
res := New(b)
|
||||
_, err := res.SetGlobalRegistrar("addresshex", common.BigToAddress(common.Big1))
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v'", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashToHash(t *testing.T) {
|
||||
b := NewTestBackend()
|
||||
res := New(b)
|
||||
|
||||
HashRegAddr = "0x0"
|
||||
got, err := res.HashToHash(codehash)
|
||||
if err == nil {
|
||||
t.Errorf("expected error")
|
||||
} else {
|
||||
exp := "HashReg address is not set"
|
||||
if err.Error() != exp {
|
||||
t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
HashRegAddr = common.BigToAddress(common.Big1).Hex() //[2:]
|
||||
got, err = res.HashToHash(codehash)
|
||||
if err == nil {
|
||||
t.Errorf("expected error")
|
||||
} else {
|
||||
exp := "HashToHash: content hash not found for '" + codehash.Hex() + "'"
|
||||
if err.Error() != exp {
|
||||
t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
b.initHashReg()
|
||||
got, err = res.HashToHash(codehash)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
} else {
|
||||
if got != hash {
|
||||
t.Errorf("incorrect result, expected '%v', got '%v'", hash.Hex(), got.Hex())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashToUrl(t *testing.T) {
|
||||
b := NewTestBackend()
|
||||
res := New(b)
|
||||
|
||||
UrlHintAddr = "0x0"
|
||||
got, err := res.HashToUrl(hash)
|
||||
if err == nil {
|
||||
t.Errorf("expected error")
|
||||
} else {
|
||||
exp := "UrlHint address is not set"
|
||||
if err.Error() != exp {
|
||||
t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
UrlHintAddr = common.BigToAddress(common.Big2).Hex() //[2:]
|
||||
got, err = res.HashToUrl(hash)
|
||||
if err == nil {
|
||||
t.Errorf("expected error")
|
||||
} else {
|
||||
exp := "HashToUrl: URL hint not found for '" + hash.Hex() + "'"
|
||||
if err.Error() != exp {
|
||||
t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
b.initUrlHint()
|
||||
got, err = res.HashToUrl(hash)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
} else {
|
||||
if got != url {
|
||||
t.Errorf("incorrect result, expected '%v', got '%s'", url, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,14 +17,12 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -32,8 +30,6 @@ const (
|
||||
AddressLength = 20
|
||||
)
|
||||
|
||||
var hashJsonLengthErr = errors.New("common: unmarshalJSON failed: hash must be exactly 32 bytes")
|
||||
|
||||
type (
|
||||
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
|
||||
Hash [HashLength]byte
|
||||
@@ -57,30 +53,16 @@ func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
|
||||
func (h Hash) Str() string { return string(h[:]) }
|
||||
func (h Hash) Bytes() []byte { return h[:] }
|
||||
func (h Hash) Big() *big.Int { return Bytes2Big(h[:]) }
|
||||
func (h Hash) Hex() string { return "0x" + Bytes2Hex(h[:]) }
|
||||
func (h Hash) Hex() string { return hexutil.Encode(h[:]) }
|
||||
|
||||
// UnmarshalJSON parses a hash in its hex from to a hash.
|
||||
func (h *Hash) UnmarshalJSON(input []byte) error {
|
||||
length := len(input)
|
||||
if length >= 2 && input[0] == '"' && input[length-1] == '"' {
|
||||
input = input[1 : length-1]
|
||||
}
|
||||
// strip "0x" for length check
|
||||
if len(input) > 1 && strings.ToLower(string(input[:2])) == "0x" {
|
||||
input = input[2:]
|
||||
}
|
||||
|
||||
// validate the length of the input hash
|
||||
if len(input) != HashLength*2 {
|
||||
return hashJsonLengthErr
|
||||
}
|
||||
h.SetBytes(FromHex(string(input)))
|
||||
return nil
|
||||
return hexutil.UnmarshalJSON("Hash", input, h[:])
|
||||
}
|
||||
|
||||
// Serialize given hash to JSON
|
||||
func (h Hash) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(h.Hex())
|
||||
return hexutil.Bytes(h[:]).MarshalJSON()
|
||||
}
|
||||
|
||||
// Sets the hash to the value of b. If b is larger than len(h) it will panic
|
||||
@@ -142,7 +124,7 @@ func (a Address) Str() string { return string(a[:]) }
|
||||
func (a Address) Bytes() []byte { return a[:] }
|
||||
func (a Address) Big() *big.Int { return Bytes2Big(a[:]) }
|
||||
func (a Address) Hash() Hash { return BytesToHash(a[:]) }
|
||||
func (a Address) Hex() string { return "0x" + Bytes2Hex(a[:]) }
|
||||
func (a Address) Hex() string { return hexutil.Encode(a[:]) }
|
||||
|
||||
// Sets the address to the value of b. If b is larger than len(a) it will panic
|
||||
func (a *Address) SetBytes(b []byte) {
|
||||
@@ -164,34 +146,12 @@ func (a *Address) Set(other Address) {
|
||||
|
||||
// Serialize given address to JSON
|
||||
func (a Address) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(a.Hex())
|
||||
return hexutil.Bytes(a[:]).MarshalJSON()
|
||||
}
|
||||
|
||||
// Parse address from raw json data
|
||||
func (a *Address) UnmarshalJSON(data []byte) error {
|
||||
if len(data) > 2 && data[0] == '"' && data[len(data)-1] == '"' {
|
||||
data = data[1 : len(data)-1]
|
||||
}
|
||||
|
||||
if len(data) > 2 && data[0] == '0' && data[1] == 'x' {
|
||||
data = data[2:]
|
||||
}
|
||||
|
||||
if len(data) != 2*AddressLength {
|
||||
return fmt.Errorf("Invalid address length, expected %d got %d bytes", 2*AddressLength, len(data))
|
||||
}
|
||||
|
||||
n, err := hex.Decode(a[:], data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n != AddressLength {
|
||||
return fmt.Errorf("Invalid address")
|
||||
}
|
||||
|
||||
a.Set(HexToAddress(string(data)))
|
||||
return nil
|
||||
func (a *Address) UnmarshalJSON(input []byte) error {
|
||||
return hexutil.UnmarshalJSON("Address", input, a[:])
|
||||
}
|
||||
|
||||
// PP Pretty Prints a byte slice in the following format:
|
||||
|
||||
@@ -18,7 +18,10 @@ package common
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
func TestBytesConversion(t *testing.T) {
|
||||
@@ -38,19 +41,26 @@ func TestHashJsonValidation(t *testing.T) {
|
||||
var tests = []struct {
|
||||
Prefix string
|
||||
Size int
|
||||
Error error
|
||||
Error string
|
||||
}{
|
||||
{"", 2, hashJsonLengthErr},
|
||||
{"", 62, hashJsonLengthErr},
|
||||
{"", 66, hashJsonLengthErr},
|
||||
{"", 65, hashJsonLengthErr},
|
||||
{"0X", 64, nil},
|
||||
{"0x", 64, nil},
|
||||
{"0x", 62, hashJsonLengthErr},
|
||||
{"", 62, hexutil.ErrMissingPrefix.Error()},
|
||||
{"0x", 66, "hex string has length 66, want 64 for Hash"},
|
||||
{"0x", 63, hexutil.ErrOddLength.Error()},
|
||||
{"0x", 0, "hex string has length 0, want 64 for Hash"},
|
||||
{"0x", 64, ""},
|
||||
{"0X", 64, ""},
|
||||
}
|
||||
for i, test := range tests {
|
||||
if err := h.UnmarshalJSON(append([]byte(test.Prefix), make([]byte, test.Size)...)); err != test.Error {
|
||||
t.Errorf("test #%d: error mismatch: have %v, want %v", i, err, test.Error)
|
||||
for _, test := range tests {
|
||||
input := `"` + test.Prefix + strings.Repeat("0", test.Size) + `"`
|
||||
err := h.UnmarshalJSON([]byte(input))
|
||||
if err == nil {
|
||||
if test.Error != "" {
|
||||
t.Errorf("%s: error mismatch: have nil, want %q", input, test.Error)
|
||||
}
|
||||
} else {
|
||||
if err.Error() != test.Error {
|
||||
t.Errorf("%s: error mismatch: have %q, want %q", input, err, test.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ FROM alpine:3.4
|
||||
|
||||
RUN \
|
||||
apk add --update go git make gcc musl-dev && \
|
||||
git clone --depth 1 --branch develop https://github.com/ethereum/go-ethereum && \
|
||||
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 && \
|
||||
|
||||
@@ -2,7 +2,7 @@ FROM alpine:3.4
|
||||
|
||||
RUN \
|
||||
apk add --update go git make gcc musl-dev && \
|
||||
git clone --depth 1 https://github.com/ethereum/go-ethereum && \
|
||||
git clone --depth 1 --branch release/1.5 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 && \
|
||||
|
||||
@@ -93,14 +93,14 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error {
|
||||
// Verify UncleHash before running other uncle validations
|
||||
unclesSha := types.CalcUncleHash(block.Uncles())
|
||||
if unclesSha != header.UncleHash {
|
||||
return fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha)
|
||||
return fmt.Errorf("invalid uncles root hash (remote: %x local: %x)", header.UncleHash, unclesSha)
|
||||
}
|
||||
|
||||
// The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]]))
|
||||
// can be used by light clients to make sure they've received the correct Txs
|
||||
txSha := types.DeriveSha(block.Transactions())
|
||||
if txSha != header.TxHash {
|
||||
return fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha)
|
||||
return fmt.Errorf("invalid transaction root hash (remote: %x local: %x)", header.TxHash, txSha)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -113,23 +113,23 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error {
|
||||
func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas *big.Int) (err error) {
|
||||
header := block.Header()
|
||||
if block.GasUsed().Cmp(usedGas) != 0 {
|
||||
return ValidationError(fmt.Sprintf("gas used error (%v / %v)", block.GasUsed(), usedGas))
|
||||
return ValidationError(fmt.Sprintf("invalid gas used (remote: %v local: %v)", block.GasUsed(), usedGas))
|
||||
}
|
||||
// Validate the received block's bloom with the one derived from the generated receipts.
|
||||
// For valid blocks this should always validate to true.
|
||||
rbloom := types.CreateBloom(receipts)
|
||||
if rbloom != header.Bloom {
|
||||
return fmt.Errorf("unable to replicate block's bloom=%x vs calculated bloom=%x", header.Bloom, rbloom)
|
||||
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
|
||||
}
|
||||
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
|
||||
receiptSha := types.DeriveSha(receipts)
|
||||
if receiptSha != header.ReceiptHash {
|
||||
return fmt.Errorf("invalid receipt root hash. received=%x calculated=%x", header.ReceiptHash, receiptSha)
|
||||
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
|
||||
}
|
||||
// Validate the state root against the received state root and throw
|
||||
// an error if they don't match.
|
||||
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
|
||||
return fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root)
|
||||
return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -223,7 +223,7 @@ func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Heade
|
||||
|
||||
expd := CalcDifficulty(config, header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty)
|
||||
if expd.Cmp(header.Difficulty) != 0 {
|
||||
return fmt.Errorf("Difficulty check failed for header %v, %v", header.Difficulty, expd)
|
||||
return fmt.Errorf("Difficulty check failed for header (remote: %v local: %v)", header.Difficulty, expd)
|
||||
}
|
||||
|
||||
a := new(big.Int).Set(parent.GasLimit)
|
||||
@@ -232,7 +232,7 @@ func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Heade
|
||||
b := new(big.Int).Set(parent.GasLimit)
|
||||
b = b.Div(b, params.GasLimitBoundDivisor)
|
||||
if !(a.Cmp(b) < 0) || (header.GasLimit.Cmp(params.MinGasLimit) == -1) {
|
||||
return fmt.Errorf("GasLimit check failed for header %v (%v > %v)", header.GasLimit, a, b)
|
||||
return fmt.Errorf("GasLimit check failed for header (remote: %v local_max: %v)", header.GasLimit, b)
|
||||
}
|
||||
|
||||
num := new(big.Int).Set(parent.Number)
|
||||
@@ -251,7 +251,7 @@ func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Heade
|
||||
if err := ValidateDAOHeaderExtraData(config, header); err != nil {
|
||||
return err
|
||||
}
|
||||
if config.EIP150Block != nil && config.EIP150Block.Cmp(header.Number) == 0 {
|
||||
if !uncle && config.EIP150Block != nil && config.EIP150Block.Cmp(header.Number) == 0 {
|
||||
if config.EIP150Hash != (common.Hash{}) && config.EIP150Hash != header.Hash() {
|
||||
return ValidationError("Homestead gas reprice fork hash mismatch: have 0x%x, want 0x%x", header.Hash(), config.EIP150Hash)
|
||||
}
|
||||
|
||||
@@ -152,9 +152,14 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.P
|
||||
// Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain
|
||||
for hash, _ := range BadHashes {
|
||||
if header := bc.GetHeaderByHash(hash); header != nil {
|
||||
glog.V(logger.Error).Infof("Found bad hash, rewinding chain to block #%d [%x…]", header.Number, header.ParentHash[:4])
|
||||
bc.SetHead(header.Number.Uint64() - 1)
|
||||
glog.V(logger.Error).Infoln("Chain rewind was successful, resuming normal operation")
|
||||
// get the canonical block corresponding to the offending header's number
|
||||
headerByNumber := bc.GetHeaderByNumber(header.Number.Uint64())
|
||||
// make sure the headerByNumber (if present) is in our current canonical chain
|
||||
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])
|
||||
bc.SetHead(header.Number.Uint64() - 1)
|
||||
glog.V(logger.Error).Infoln("Chain rewind was successful, resuming normal operation")
|
||||
}
|
||||
}
|
||||
}
|
||||
// Take ownership of this particular state
|
||||
@@ -878,7 +883,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
|
||||
if BadHashes[block.Hash()] {
|
||||
err := BadHashError(block.Hash())
|
||||
reportBlock(block, err)
|
||||
self.reportBlock(block, nil, err)
|
||||
return i, err
|
||||
}
|
||||
// Stage 1 validation of the block using the chain's validator
|
||||
@@ -910,7 +915,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
reportBlock(block, err)
|
||||
self.reportBlock(block, nil, err)
|
||||
|
||||
return i, err
|
||||
}
|
||||
@@ -924,19 +929,19 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
err = self.stateCache.Reset(chain[i-1].Root())
|
||||
}
|
||||
if err != nil {
|
||||
reportBlock(block, err)
|
||||
self.reportBlock(block, nil, err)
|
||||
return i, err
|
||||
}
|
||||
// Process block using the parent state as reference point.
|
||||
receipts, logs, usedGas, err := self.processor.Process(block, self.stateCache, vm.Config{})
|
||||
if err != nil {
|
||||
reportBlock(block, err)
|
||||
self.reportBlock(block, receipts, err)
|
||||
return i, err
|
||||
}
|
||||
// Validate the state using the default validator
|
||||
err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash(), block.NumberU64()-1), self.stateCache, receipts, usedGas)
|
||||
if err != nil {
|
||||
reportBlock(block, err)
|
||||
self.reportBlock(block, receipts, err)
|
||||
return i, err
|
||||
}
|
||||
// Write state changes to database
|
||||
@@ -1207,10 +1212,23 @@ func (self *BlockChain) update() {
|
||||
}
|
||||
|
||||
// reportBlock logs a bad block error.
|
||||
func reportBlock(block *types.Block, err error) {
|
||||
func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) {
|
||||
if glog.V(logger.Error) {
|
||||
glog.Errorf("Bad block #%v (%s)\n", block.Number(), block.Hash().Hex())
|
||||
glog.Errorf(" %v", err)
|
||||
var receiptString string
|
||||
for _, receipt := range receipts {
|
||||
receiptString += fmt.Sprintf("\t%v\n", receipt)
|
||||
}
|
||||
glog.Errorf(`
|
||||
########## BAD BLOCK #########
|
||||
Chain config: %v
|
||||
|
||||
Number: %v
|
||||
Hash: 0x%x
|
||||
%v
|
||||
|
||||
Error: %v
|
||||
##############################
|
||||
`, bc.config, block.Number(), block.Hash(), receiptString, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -143,12 +143,12 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||
}
|
||||
receipts, _, usedGas, err := blockchain.Processor().Process(block, statedb, vm.Config{})
|
||||
if err != nil {
|
||||
reportBlock(block, err)
|
||||
blockchain.reportBlock(block, receipts, err)
|
||||
return err
|
||||
}
|
||||
err = blockchain.Validator().ValidateState(block, blockchain.GetBlockByHash(block.ParentHash()), statedb, receipts, usedGas)
|
||||
if err != nil {
|
||||
reportBlock(block, err)
|
||||
blockchain.reportBlock(block, receipts, err)
|
||||
return err
|
||||
}
|
||||
blockchain.mu.Lock()
|
||||
@@ -1136,13 +1136,14 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
|
||||
func TestEIP155Transition(t *testing.T) {
|
||||
// Configure and generate a sample block chain
|
||||
var (
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
genesis = WriteGenesisBlockForTesting(db, GenesisAccount{address, funds})
|
||||
config = ¶ms.ChainConfig{ChainId: big.NewInt(1), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
|
||||
mux event.TypeMux
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
deleteAddr = common.Address{1}
|
||||
genesis = WriteGenesisBlockForTesting(db, GenesisAccount{address, funds}, GenesisAccount{deleteAddr, new(big.Int)})
|
||||
config = ¶ms.ChainConfig{ChainId: big.NewInt(1), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
|
||||
mux event.TypeMux
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, config, FakePow{}, &mux)
|
||||
@@ -1231,3 +1232,66 @@ func TestEIP155Transition(t *testing.T) {
|
||||
t.Error("expected error:", types.ErrInvalidChainId)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEIP161AccountRemoval(t *testing.T) {
|
||||
// Configure and generate a sample block chain
|
||||
var (
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
theAddr = common.Address{1}
|
||||
genesis = WriteGenesisBlockForTesting(db, GenesisAccount{address, funds})
|
||||
config = ¶ms.ChainConfig{
|
||||
ChainId: big.NewInt(1),
|
||||
HomesteadBlock: new(big.Int),
|
||||
EIP155Block: new(big.Int),
|
||||
EIP158Block: big.NewInt(2),
|
||||
}
|
||||
mux event.TypeMux
|
||||
|
||||
blockchain, _ = NewBlockChain(db, config, FakePow{}, &mux)
|
||||
)
|
||||
blocks, _ := GenerateChain(config, genesis, db, 3, func(i int, block *BlockGen) {
|
||||
var (
|
||||
tx *types.Transaction
|
||||
err error
|
||||
signer = types.NewEIP155Signer(config.ChainId)
|
||||
)
|
||||
switch i {
|
||||
case 0:
|
||||
tx, err = types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil).SignECDSA(signer, key)
|
||||
case 1:
|
||||
tx, err = types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil).SignECDSA(signer, key)
|
||||
case 2:
|
||||
tx, err = types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil).SignECDSA(signer, key)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block.AddTx(tx)
|
||||
})
|
||||
// account must exist pre eip 161
|
||||
if _, err := blockchain.InsertChain(types.Blocks{blocks[0]}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !blockchain.stateCache.Exist(theAddr) {
|
||||
t.Error("expected account to exist")
|
||||
}
|
||||
|
||||
// account needs to be deleted post eip 161
|
||||
if _, err := blockchain.InsertChain(types.Blocks{blocks[1]}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if blockchain.stateCache.Exist(theAddr) {
|
||||
t.Error("account should not expect")
|
||||
}
|
||||
|
||||
// account musn't be created post eip 161
|
||||
if _, err := blockchain.InsertChain(types.Blocks{blocks[2]}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if blockchain.stateCache.Exist(theAddr) {
|
||||
t.Error("account should not expect")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,4 +21,5 @@ import "github.com/ethereum/go-ethereum/common"
|
||||
// Set of manually tracked bad hashes (usually hard forks)
|
||||
var BadHashes = map[common.Hash]bool{
|
||||
common.HexToHash("05bef30ef572270f654746da22639a7a0c97dd97a7050b9e252391996aaeb689"): true,
|
||||
common.HexToHash("7d05d08cbc596a2e5e4f13b80a743e53e09221b5323c3a61946b20873e58583f"): true,
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -38,9 +38,6 @@ type PendingLogsEvent struct {
|
||||
// PendingStateEvent is posted pre mining and notifies of pending state changes.
|
||||
type PendingStateEvent struct{}
|
||||
|
||||
// NewBlockEvent is posted when a block has been imported.
|
||||
type NewBlockEvent struct{ Block *types.Block }
|
||||
|
||||
// NewMinedBlockEvent is posted when a block has been imported.
|
||||
type NewMinedBlockEvent struct{ Block *types.Block }
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"compress/bzip2"
|
||||
"compress/gzip"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
@@ -174,7 +175,7 @@ func WriteDefaultGenesisBlock(chainDb ethdb.Database) (*types.Block, error) {
|
||||
// WriteTestNetGenesisBlock assembles the Morden test network genesis block and
|
||||
// writes it - along with all associated state - into a chain database.
|
||||
func WriteTestNetGenesisBlock(chainDb ethdb.Database) (*types.Block, error) {
|
||||
return WriteGenesisBlock(chainDb, strings.NewReader(TestNetGenesisBlock()))
|
||||
return WriteGenesisBlock(chainDb, strings.NewReader(DefaultTestnetGenesisBlock()))
|
||||
}
|
||||
|
||||
// WriteOlympicGenesisBlock assembles the Olympic genesis block and writes it
|
||||
@@ -197,6 +198,15 @@ func DefaultGenesisBlock() string {
|
||||
return string(blob)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// OlympicGenesisBlock assembles a JSON string representing the Olympic genesis
|
||||
// block.
|
||||
func OlympicGenesisBlock() string {
|
||||
@@ -220,25 +230,3 @@ func OlympicGenesisBlock() string {
|
||||
}
|
||||
}`, types.EncodeNonce(42), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes())
|
||||
}
|
||||
|
||||
// TestNetGenesisBlock assembles a JSON string representing the Morden test net
|
||||
// genenis block.
|
||||
func TestNetGenesisBlock() string {
|
||||
return fmt.Sprintf(`{
|
||||
"nonce": "0x%x",
|
||||
"difficulty": "0x20000",
|
||||
"mixhash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x2FEFD8",
|
||||
"alloc": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1" },
|
||||
"0000000000000000000000000000000000000002": { "balance": "1" },
|
||||
"0000000000000000000000000000000000000003": { "balance": "1" },
|
||||
"0000000000000000000000000000000000000004": { "balance": "1" },
|
||||
"102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }
|
||||
}
|
||||
}`, types.EncodeNonce(0x6d6f7264656e))
|
||||
}
|
||||
|
||||
@@ -67,10 +67,13 @@ type (
|
||||
addLogChange struct {
|
||||
txhash common.Hash
|
||||
}
|
||||
touchChange struct {
|
||||
account *common.Address
|
||||
prev bool
|
||||
}
|
||||
)
|
||||
|
||||
func (ch createObjectChange) undo(s *StateDB) {
|
||||
s.GetStateObject(*ch.account).deleted = true
|
||||
delete(s.stateObjects, *ch.account)
|
||||
delete(s.stateObjectsDirty, *ch.account)
|
||||
}
|
||||
@@ -87,6 +90,15 @@ func (ch suicideChange) undo(s *StateDB) {
|
||||
}
|
||||
}
|
||||
|
||||
var ripemd = common.HexToAddress("0000000000000000000000000000000000000003")
|
||||
|
||||
func (ch touchChange) undo(s *StateDB) {
|
||||
if !ch.prev && *ch.account != ripemd {
|
||||
delete(s.stateObjects, *ch.account)
|
||||
delete(s.stateObjectsDirty, *ch.account)
|
||||
}
|
||||
}
|
||||
|
||||
func (ch balanceChange) undo(s *StateDB) {
|
||||
s.GetStateObject(*ch.account).setBalance(ch.prev)
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ type StateObject struct {
|
||||
// during the "update" phase of the state transition.
|
||||
dirtyCode bool // true if the code was updated
|
||||
suicided bool
|
||||
touched bool
|
||||
deleted bool
|
||||
onDirty func(addr common.Address) // Callback method to mark a state object newly dirty
|
||||
}
|
||||
@@ -139,6 +140,18 @@ func (self *StateObject) markSuicided() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *StateObject) touch() {
|
||||
c.db.journal = append(c.db.journal, touchChange{
|
||||
account: &c.address,
|
||||
prev: c.touched,
|
||||
})
|
||||
if c.onDirty != nil {
|
||||
c.onDirty(c.Address())
|
||||
c.onDirty = nil
|
||||
}
|
||||
c.touched = true
|
||||
}
|
||||
|
||||
func (c *StateObject) getTrie(db trie.Database) *trie.SecureTrie {
|
||||
if c.trie == nil {
|
||||
var err error
|
||||
@@ -231,7 +244,11 @@ func (self *StateObject) CommitTrie(db trie.Database, dbw trie.DatabaseWriter) e
|
||||
func (c *StateObject) AddBalance(amount *big.Int) {
|
||||
// EIP158: We must check emptiness for the objects such that the account
|
||||
// clearing (0,0,0 objects) can take effect.
|
||||
if amount.Cmp(common.Big0) == 0 && !c.empty() {
|
||||
if amount.Cmp(common.Big0) == 0 {
|
||||
if c.empty() {
|
||||
c.touch()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
c.SetBalance(new(big.Int).Add(c.Balance(), amount))
|
||||
|
||||
@@ -34,10 +34,6 @@ import (
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
// The starting nonce determines the default nonce when new accounts are being
|
||||
// created.
|
||||
var StartingNonce uint64
|
||||
|
||||
// Trie cache generation limit after which to evic trie nodes from memory.
|
||||
var MaxTrieCacheGen = uint16(120)
|
||||
|
||||
@@ -239,7 +235,7 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 {
|
||||
return stateObject.Nonce()
|
||||
}
|
||||
|
||||
return StartingNonce
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *StateDB) GetCode(addr common.Address) []byte {
|
||||
@@ -423,7 +419,7 @@ func (self *StateDB) MarkStateObjectDirty(addr common.Address) {
|
||||
func (self *StateDB) createObject(addr common.Address) (newobj, prev *StateObject) {
|
||||
prev = self.GetStateObject(addr)
|
||||
newobj = newObject(self, addr, Account{}, self.MarkStateObjectDirty)
|
||||
newobj.setNonce(StartingNonce) // sets the object to dirty
|
||||
newobj.setNonce(0) // sets the object to dirty
|
||||
if prev == nil {
|
||||
if glog.V(logger.Core) {
|
||||
glog.Infof("(+) %x\n", addr)
|
||||
|
||||
@@ -116,6 +116,7 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSnapshotRandom(t *testing.T) {
|
||||
t.Skip("@fjl fix me please")
|
||||
config := &quick.Config{MaxCount: 1000}
|
||||
err := quick.Check((*snapshotTest).run, config)
|
||||
if cerr, ok := err.(*quick.CheckError); ok {
|
||||
@@ -354,3 +355,22 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestTouchDelete(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
state, _ := New(common.Hash{}, db)
|
||||
state.GetOrNewStateObject(common.Address{})
|
||||
root, _ := state.Commit(false)
|
||||
state.Reset(root)
|
||||
|
||||
snapshot := state.Snapshot()
|
||||
state.AddBalance(common.Address{}, new(big.Int))
|
||||
if len(state.stateObjectsDirty) != 1 {
|
||||
t.Fatal("expected one dirty state object")
|
||||
}
|
||||
|
||||
state.RevertToSnapshot(snapshot)
|
||||
if len(state.stateObjectsDirty) != 0 {
|
||||
t.Fatal("expected no dirty state object")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||
}
|
||||
// Iterate over and process the individual transactions
|
||||
for i, tx := range block.Transactions() {
|
||||
//fmt.Println("tx:", i)
|
||||
statedb.StartRecord(tx.Hash(), block.Hash(), i)
|
||||
receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
|
||||
if err != nil {
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
@@ -63,20 +64,12 @@ func (n BlockNonce) Uint64() uint64 {
|
||||
|
||||
// MarshalJSON implements json.Marshaler
|
||||
func (n BlockNonce) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf(`"0x%x"`, n)), nil
|
||||
return hexutil.Bytes(n[:]).MarshalJSON()
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler
|
||||
func (n *BlockNonce) UnmarshalJSON(input []byte) error {
|
||||
var b hexBytes
|
||||
if err := b.UnmarshalJSON(input); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(b) != 8 {
|
||||
return errBadNonceSize
|
||||
}
|
||||
copy((*n)[:], b)
|
||||
return nil
|
||||
return hexutil.UnmarshalJSON("BlockNonce", input, n[:])
|
||||
}
|
||||
|
||||
// Header represents a block header in the Ethereum blockchain.
|
||||
@@ -106,12 +99,12 @@ type jsonHeader struct {
|
||||
TxHash *common.Hash `json:"transactionsRoot"`
|
||||
ReceiptHash *common.Hash `json:"receiptsRoot"`
|
||||
Bloom *Bloom `json:"logsBloom"`
|
||||
Difficulty *hexBig `json:"difficulty"`
|
||||
Number *hexBig `json:"number"`
|
||||
GasLimit *hexBig `json:"gasLimit"`
|
||||
GasUsed *hexBig `json:"gasUsed"`
|
||||
Time *hexBig `json:"timestamp"`
|
||||
Extra *hexBytes `json:"extraData"`
|
||||
Difficulty *hexutil.Big `json:"difficulty"`
|
||||
Number *hexutil.Big `json:"number"`
|
||||
GasLimit *hexutil.Big `json:"gasLimit"`
|
||||
GasUsed *hexutil.Big `json:"gasUsed"`
|
||||
Time *hexutil.Big `json:"timestamp"`
|
||||
Extra *hexutil.Bytes `json:"extraData"`
|
||||
MixDigest *common.Hash `json:"mixHash"`
|
||||
Nonce *BlockNonce `json:"nonce"`
|
||||
}
|
||||
@@ -151,12 +144,12 @@ func (h *Header) MarshalJSON() ([]byte, error) {
|
||||
TxHash: &h.TxHash,
|
||||
ReceiptHash: &h.ReceiptHash,
|
||||
Bloom: &h.Bloom,
|
||||
Difficulty: (*hexBig)(h.Difficulty),
|
||||
Number: (*hexBig)(h.Number),
|
||||
GasLimit: (*hexBig)(h.GasLimit),
|
||||
GasUsed: (*hexBig)(h.GasUsed),
|
||||
Time: (*hexBig)(h.Time),
|
||||
Extra: (*hexBytes)(&h.Extra),
|
||||
Difficulty: (*hexutil.Big)(h.Difficulty),
|
||||
Number: (*hexutil.Big)(h.Number),
|
||||
GasLimit: (*hexutil.Big)(h.GasLimit),
|
||||
GasUsed: (*hexutil.Big)(h.GasUsed),
|
||||
Time: (*hexutil.Big)(h.Time),
|
||||
Extra: (*hexutil.Bytes)(&h.Extra),
|
||||
MixDigest: &h.MixDigest,
|
||||
Nonce: &h.Nonce,
|
||||
})
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
@@ -77,20 +78,12 @@ func (b Bloom) TestBytes(test []byte) bool {
|
||||
|
||||
// MarshalJSON encodes b as a hex string with 0x prefix.
|
||||
func (b Bloom) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf(`"%#x"`, b[:])), nil
|
||||
return hexutil.Bytes(b[:]).MarshalJSON()
|
||||
}
|
||||
|
||||
// UnmarshalJSON b as a hex string with 0x prefix.
|
||||
func (b *Bloom) UnmarshalJSON(input []byte) error {
|
||||
var dec hexBytes
|
||||
if err := dec.UnmarshalJSON(input); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(dec) != bloomLength {
|
||||
return fmt.Errorf("invalid bloom size, want %d bytes", bloomLength)
|
||||
}
|
||||
copy((*b)[:], dec)
|
||||
return nil
|
||||
return hexutil.UnmarshalJSON("Bloom", input, b[:])
|
||||
}
|
||||
|
||||
func CreateBloom(receipts Receipts) Bloom {
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// JSON unmarshaling utilities.
|
||||
|
||||
type hexBytes []byte
|
||||
|
||||
func (b *hexBytes) MarshalJSON() ([]byte, error) {
|
||||
if b != nil {
|
||||
return []byte(fmt.Sprintf(`"0x%x"`, []byte(*b))), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *hexBytes) UnmarshalJSON(input []byte) error {
|
||||
if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' {
|
||||
return fmt.Errorf("cannot unmarshal non-string into hexBytes")
|
||||
}
|
||||
input = input[1 : len(input)-1]
|
||||
if len(input) < 2 || input[0] != '0' || input[1] != 'x' {
|
||||
return fmt.Errorf("missing 0x prefix in hexBytes input %q", input)
|
||||
}
|
||||
dec := make(hexBytes, (len(input)-2)/2)
|
||||
if _, err := hex.Decode(dec, input[2:]); err != nil {
|
||||
return err
|
||||
}
|
||||
*b = dec
|
||||
return nil
|
||||
}
|
||||
|
||||
type hexBig big.Int
|
||||
|
||||
func (b *hexBig) MarshalJSON() ([]byte, error) {
|
||||
if b != nil {
|
||||
return []byte(fmt.Sprintf(`"0x%x"`, (*big.Int)(b))), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *hexBig) UnmarshalJSON(input []byte) error {
|
||||
raw, err := checkHexNumber(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec, ok := new(big.Int).SetString(string(raw), 16)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid hex number")
|
||||
}
|
||||
*b = (hexBig)(*dec)
|
||||
return nil
|
||||
}
|
||||
|
||||
type hexUint64 uint64
|
||||
|
||||
func (b *hexUint64) MarshalJSON() ([]byte, error) {
|
||||
if b != nil {
|
||||
return []byte(fmt.Sprintf(`"0x%x"`, *(*uint64)(b))), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *hexUint64) UnmarshalJSON(input []byte) error {
|
||||
raw, err := checkHexNumber(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fmt.Sscanf(string(raw), "%x", b)
|
||||
return err
|
||||
}
|
||||
|
||||
func checkHexNumber(input []byte) (raw []byte, err error) {
|
||||
if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' {
|
||||
return nil, fmt.Errorf("cannot unmarshal non-string into hex number")
|
||||
}
|
||||
input = input[1 : len(input)-1]
|
||||
if len(input) < 2 || input[0] != '0' || input[1] != 'x' {
|
||||
return nil, fmt.Errorf("missing 0x prefix in hex number input %q", input)
|
||||
}
|
||||
if len(input) == 2 {
|
||||
return nil, fmt.Errorf("empty hex number")
|
||||
}
|
||||
raw = input[2:]
|
||||
if len(raw)%2 != 0 {
|
||||
raw = append([]byte{'0'}, raw...)
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var unmarshalHeaderTests = map[string]struct {
|
||||
input string
|
||||
wantHash common.Hash
|
||||
wantError error
|
||||
}{
|
||||
"block 0x1e2200": {
|
||||
input: `{"difficulty":"0x311ca98cebfe","extraData":"0x7777772e62772e636f6d","gasLimit":"0x47db3d","gasUsed":"0x43760c","hash":"0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","mixHash":"0x1ccfddb506dac5afc09b6f92eb09a043ffc8e08f7592250af57b9c64c20f9b25","nonce":"0x670bd98c79585197","number":"0x1e2200","parentHash":"0xd3e13296d064e7344f20c57c57b67a022f6bf7741fa42428c2db77e91abdf1f8","receiptsRoot":"0xeeab1776c1fafbe853a8ee0c1bafe2e775a1b6fdb6ff3e9f9410ddd4514889ff","sha3Uncles":"0x5fbfa4ec8b089678c53b6798cc0d9260ea40a529e06d5300aae35596262e0eb3","size":"0x57f","stateRoot":"0x62ad2007e4a3f31ea98e5d2fd150d894887bafde36eeac7331a60ae12053ec76","timestamp":"0x579b82f2","totalDifficulty":"0x24fe813c101d00f97","transactions":["0xb293408e85735bfc78b35aa89de8b48e49641e3d82e3d52ea2d44ec42a4e88cf","0x124acc383ff2da6faa0357829084dae64945221af6f6f09da1d11688b779f939","0xee090208b6051c442ccdf9ec19f66389e604d342a6d71144c7227ce995bef46f"],"transactionsRoot":"0xce0042dd9af0c1923dd7f58ca6faa156d39d4ef39fdb65c5bcd1d4b4720096db","uncles":["0x6818a31d1f204cf640c952082940b68b8db6d1b39ee71f7efe0e3629ed5d7eb3"]}`,
|
||||
wantHash: common.HexToHash("0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48"),
|
||||
},
|
||||
"bad nonce": {
|
||||
input: `{"difficulty":"0x311ca98cebfe","extraData":"0x7777772e62772e636f6d","gasLimit":"0x47db3d","gasUsed":"0x43760c","hash":"0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","mixHash":"0x1ccfddb506dac5afc09b6f92eb09a043ffc8e08f7592250af57b9c64c20f9b25","nonce":"0x670bd98c7958","number":"0x1e2200","parentHash":"0xd3e13296d064e7344f20c57c57b67a022f6bf7741fa42428c2db77e91abdf1f8","receiptsRoot":"0xeeab1776c1fafbe853a8ee0c1bafe2e775a1b6fdb6ff3e9f9410ddd4514889ff","sha3Uncles":"0x5fbfa4ec8b089678c53b6798cc0d9260ea40a529e06d5300aae35596262e0eb3","size":"0x57f","stateRoot":"0x62ad2007e4a3f31ea98e5d2fd150d894887bafde36eeac7331a60ae12053ec76","timestamp":"0x579b82f2","totalDifficulty":"0x24fe813c101d00f97","transactions":["0xb293408e85735bfc78b35aa89de8b48e49641e3d82e3d52ea2d44ec42a4e88cf","0x124acc383ff2da6faa0357829084dae64945221af6f6f09da1d11688b779f939","0xee090208b6051c442ccdf9ec19f66389e604d342a6d71144c7227ce995bef46f"],"transactionsRoot":"0xce0042dd9af0c1923dd7f58ca6faa156d39d4ef39fdb65c5bcd1d4b4720096db","uncles":["0x6818a31d1f204cf640c952082940b68b8db6d1b39ee71f7efe0e3629ed5d7eb3"]}`,
|
||||
wantError: errBadNonceSize,
|
||||
},
|
||||
"missing mixHash": {
|
||||
input: `{"difficulty":"0x311ca98cebfe","extraData":"0x7777772e62772e636f6d","gasLimit":"0x47db3d","gasUsed":"0x43760c","hash":"0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","nonce":"0x670bd98c79585197","number":"0x1e2200","parentHash":"0xd3e13296d064e7344f20c57c57b67a022f6bf7741fa42428c2db77e91abdf1f8","receiptsRoot":"0xeeab1776c1fafbe853a8ee0c1bafe2e775a1b6fdb6ff3e9f9410ddd4514889ff","sha3Uncles":"0x5fbfa4ec8b089678c53b6798cc0d9260ea40a529e06d5300aae35596262e0eb3","size":"0x57f","stateRoot":"0x62ad2007e4a3f31ea98e5d2fd150d894887bafde36eeac7331a60ae12053ec76","timestamp":"0x579b82f2","totalDifficulty":"0x24fe813c101d00f97","transactions":["0xb293408e85735bfc78b35aa89de8b48e49641e3d82e3d52ea2d44ec42a4e88cf","0x124acc383ff2da6faa0357829084dae64945221af6f6f09da1d11688b779f939","0xee090208b6051c442ccdf9ec19f66389e604d342a6d71144c7227ce995bef46f"],"transactionsRoot":"0xce0042dd9af0c1923dd7f58ca6faa156d39d4ef39fdb65c5bcd1d4b4720096db","uncles":["0x6818a31d1f204cf640c952082940b68b8db6d1b39ee71f7efe0e3629ed5d7eb3"]}`,
|
||||
wantError: errMissingHeaderMixDigest,
|
||||
},
|
||||
"missing fields": {
|
||||
input: `{"gasLimit":"0x47db3d","gasUsed":"0x43760c","hash":"0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","mixHash":"0x1ccfddb506dac5afc09b6f92eb09a043ffc8e08f7592250af57b9c64c20f9b25","nonce":"0x670bd98c79585197","number":"0x1e2200","parentHash":"0xd3e13296d064e7344f20c57c57b67a022f6bf7741fa42428c2db77e91abdf1f8","receiptsRoot":"0xeeab1776c1fafbe853a8ee0c1bafe2e775a1b6fdb6ff3e9f9410ddd4514889ff","sha3Uncles":"0x5fbfa4ec8b089678c53b6798cc0d9260ea40a529e06d5300aae35596262e0eb3","size":"0x57f","stateRoot":"0x62ad2007e4a3f31ea98e5d2fd150d894887bafde36eeac7331a60ae12053ec76","timestamp":"0x579b82f2","totalDifficulty":"0x24fe813c101d00f97","transactions":["0xb293408e85735bfc78b35aa89de8b48e49641e3d82e3d52ea2d44ec42a4e88cf","0x124acc383ff2da6faa0357829084dae64945221af6f6f09da1d11688b779f939","0xee090208b6051c442ccdf9ec19f66389e604d342a6d71144c7227ce995bef46f"],"transactionsRoot":"0xce0042dd9af0c1923dd7f58ca6faa156d39d4ef39fdb65c5bcd1d4b4720096db","uncles":["0x6818a31d1f204cf640c952082940b68b8db6d1b39ee71f7efe0e3629ed5d7eb3"]}`,
|
||||
wantError: errMissingHeaderFields,
|
||||
},
|
||||
}
|
||||
|
||||
func TestUnmarshalHeader(t *testing.T) {
|
||||
for name, test := range unmarshalHeaderTests {
|
||||
var head *Header
|
||||
err := json.Unmarshal([]byte(test.input), &head)
|
||||
if !checkError(t, name, err, test.wantError) {
|
||||
continue
|
||||
}
|
||||
if head.Hash() != test.wantHash {
|
||||
t.Errorf("test %q: got hash %x, want %x", name, head.Hash(), test.wantHash)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalHeader(t *testing.T) {
|
||||
for name, test := range unmarshalHeaderTests {
|
||||
if test.wantError != nil {
|
||||
continue
|
||||
}
|
||||
var original *Header
|
||||
json.Unmarshal([]byte(test.input), &original)
|
||||
|
||||
blob, err := json.Marshal(original)
|
||||
if err != nil {
|
||||
t.Errorf("test %q: failed to marshal header: %v", name, err)
|
||||
continue
|
||||
}
|
||||
var proced *Header
|
||||
if err := json.Unmarshal(blob, &proced); err != nil {
|
||||
t.Errorf("Test %q: failed to unmarshal marhsalled header: %v", name, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(original, proced) {
|
||||
t.Errorf("test %q: header mismatch: have %+v, want %+v", name, proced, original)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unmarshalTransactionTests = map[string]struct {
|
||||
input string
|
||||
wantHash common.Hash
|
||||
wantFrom common.Address
|
||||
wantError error
|
||||
}{
|
||||
"value transfer": {
|
||||
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","v":"0x1c","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
|
||||
wantHash: common.HexToHash("0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9"),
|
||||
wantFrom: common.HexToAddress("0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689"),
|
||||
},
|
||||
/* TODO skipping this test as this type can not be tested with the current signing approach
|
||||
"bad signature fields": {
|
||||
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","v":"0x58","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
|
||||
wantError: ErrInvalidSig,
|
||||
},
|
||||
*/
|
||||
"missing signature v": {
|
||||
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
|
||||
wantError: errMissingTxSignatureFields,
|
||||
},
|
||||
"missing signature fields": {
|
||||
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00"}`,
|
||||
wantError: errMissingTxSignatureFields,
|
||||
},
|
||||
"missing fields": {
|
||||
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","v":"0x1c","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
|
||||
wantError: errMissingTxFields,
|
||||
},
|
||||
}
|
||||
|
||||
func TestUnmarshalTransaction(t *testing.T) {
|
||||
for name, test := range unmarshalTransactionTests {
|
||||
var tx *Transaction
|
||||
err := json.Unmarshal([]byte(test.input), &tx)
|
||||
if !checkError(t, name, err, test.wantError) {
|
||||
continue
|
||||
}
|
||||
|
||||
if tx.Hash() != test.wantHash {
|
||||
t.Errorf("test %q: got hash %x, want %x", name, tx.Hash(), test.wantHash)
|
||||
continue
|
||||
}
|
||||
from, err := Sender(HomesteadSigner{}, tx)
|
||||
if err != nil {
|
||||
t.Errorf("test %q: From error %v", name, err)
|
||||
}
|
||||
if from != test.wantFrom {
|
||||
t.Errorf("test %q: sender mismatch: got %x, want %x", name, from, test.wantFrom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalTransaction(t *testing.T) {
|
||||
for name, test := range unmarshalTransactionTests {
|
||||
if test.wantError != nil {
|
||||
continue
|
||||
}
|
||||
var original *Transaction
|
||||
json.Unmarshal([]byte(test.input), &original)
|
||||
|
||||
blob, err := json.Marshal(original)
|
||||
if err != nil {
|
||||
t.Errorf("test %q: failed to marshal transaction: %v", name, err)
|
||||
continue
|
||||
}
|
||||
var proced *Transaction
|
||||
if err := json.Unmarshal(blob, &proced); err != nil {
|
||||
t.Errorf("Test %q: failed to unmarshal marhsalled transaction: %v", name, err)
|
||||
continue
|
||||
}
|
||||
proced.Hash() // hack private fields to pass deep equal
|
||||
if !reflect.DeepEqual(original, proced) {
|
||||
t.Errorf("test %q: transaction mismatch: have %+v, want %+v", name, proced, original)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unmarshalReceiptTests = map[string]struct {
|
||||
input string
|
||||
wantError error
|
||||
}{
|
||||
"ok": {
|
||||
input: `{"blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","blockNumber":"0x1e773b","contractAddress":null,"cumulativeGasUsed":"0x10cea","from":"0xdf21fa922215b1a56f5a6d6294e6e36c85a0acfb","gasUsed":"0xbae2","logs":[{"address":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000df21fa922215b1a56f5a6d6294e6e36c85a0acfb","0x00000000000000000000000032be343b94f860124dc4fee278fdcbd38c102d88"],"data":"0x0000000000000000000000000000000000000000000000027cfefc4f3f392700","blockNumber":"0x1e773b","transactionIndex":"0x1","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","logIndex":"0x0"}],"logsBloom":"0x00000000000000020000000000020000000000000000000000000000000000000000000000000000000000000000000000040000000000000100000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000010000000000000000000000000000000000000000000000010000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000002002000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000","root":"0x6e8a06b2dac39ac5c9d4db5fb2a2a94ef7a6e5ec1c554079112112caf162998a","to":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","transactionIndex":"0x1"}`,
|
||||
},
|
||||
"missing post state": {
|
||||
input: `{"blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","blockNumber":"0x1e773b","contractAddress":null,"cumulativeGasUsed":"0x10cea","from":"0xdf21fa922215b1a56f5a6d6294e6e36c85a0acfb","gasUsed":"0xbae2","logs":[{"address":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000df21fa922215b1a56f5a6d6294e6e36c85a0acfb","0x00000000000000000000000032be343b94f860124dc4fee278fdcbd38c102d88"],"data":"0x0000000000000000000000000000000000000000000000027cfefc4f3f392700","blockNumber":"0x1e773b","transactionIndex":"0x1","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","logIndex":"0x0"}],"logsBloom":"0x00000000000000020000000000020000000000000000000000000000000000000000000000000000000000000000000000040000000000000100000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000010000000000000000000000000000000000000000000000010000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000002002000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000","to":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","transactionIndex":"0x1"}`,
|
||||
wantError: errMissingReceiptPostState,
|
||||
},
|
||||
"missing fields": {
|
||||
input: `{"blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","blockNumber":"0x1e773b","contractAddress":null,"cumulativeGasUsed":"0x10cea","from":"0xdf21fa922215b1a56f5a6d6294e6e36c85a0acfb","gasUsed":"0xbae2","logs":[{"address":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000df21fa922215b1a56f5a6d6294e6e36c85a0acfb","0x00000000000000000000000032be343b94f860124dc4fee278fdcbd38c102d88"],"data":"0x0000000000000000000000000000000000000000000000027cfefc4f3f392700","blockNumber":"0x1e773b","transactionIndex":"0x1","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","logIndex":"0x0"}],"logsBloom":"0x00000000000000020000000000020000000000000000000000000000000000000000000000000000000000000000000000040000000000000100000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000010000000000000000000000000000000000000000000000010000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000002002000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000","root":"0x6e8a06b2dac39ac5c9d4db5fb2a2a94ef7a6e5ec1c554079112112caf162998a","to":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413"}`,
|
||||
wantError: errMissingReceiptFields,
|
||||
},
|
||||
}
|
||||
|
||||
func TestUnmarshalReceipt(t *testing.T) {
|
||||
for name, test := range unmarshalReceiptTests {
|
||||
var r *Receipt
|
||||
err := json.Unmarshal([]byte(test.input), &r)
|
||||
checkError(t, name, err, test.wantError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalReceipt(t *testing.T) {
|
||||
for name, test := range unmarshalReceiptTests {
|
||||
if test.wantError != nil {
|
||||
continue
|
||||
}
|
||||
var original *Receipt
|
||||
json.Unmarshal([]byte(test.input), &original)
|
||||
|
||||
blob, err := json.Marshal(original)
|
||||
if err != nil {
|
||||
t.Errorf("test %q: failed to marshal receipt: %v", name, err)
|
||||
continue
|
||||
}
|
||||
var proced *Receipt
|
||||
if err := json.Unmarshal(blob, &proced); err != nil {
|
||||
t.Errorf("Test %q: failed to unmarshal marhsalled receipt: %v", name, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(original, proced) {
|
||||
t.Errorf("test %q: receipt mismatch: have %+v, want %+v", name, proced, original)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkError(t *testing.T, testname string, got, want error) bool {
|
||||
if got == nil {
|
||||
if want != nil {
|
||||
t.Errorf("test %q: got no error, want %q", testname, want)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if want == nil {
|
||||
t.Errorf("test %q: unexpected error %q", testname, got)
|
||||
} else if got.Error() != want.Error() {
|
||||
t.Errorf("test %q: got error %q, want %q", testname, got, want)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
@@ -49,12 +50,12 @@ type Receipt struct {
|
||||
|
||||
type jsonReceipt struct {
|
||||
PostState *common.Hash `json:"root"`
|
||||
CumulativeGasUsed *hexBig `json:"cumulativeGasUsed"`
|
||||
CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed"`
|
||||
Bloom *Bloom `json:"logsBloom"`
|
||||
Logs *vm.Logs `json:"logs"`
|
||||
TxHash *common.Hash `json:"transactionHash"`
|
||||
ContractAddress *common.Address `json:"contractAddress"`
|
||||
GasUsed *hexBig `json:"gasUsed"`
|
||||
GasUsed *hexutil.Big `json:"gasUsed"`
|
||||
}
|
||||
|
||||
// NewReceipt creates a barebone transaction receipt, copying the init fields.
|
||||
@@ -90,12 +91,12 @@ func (r *Receipt) MarshalJSON() ([]byte, error) {
|
||||
|
||||
return json.Marshal(&jsonReceipt{
|
||||
PostState: &root,
|
||||
CumulativeGasUsed: (*hexBig)(r.CumulativeGasUsed),
|
||||
CumulativeGasUsed: (*hexutil.Big)(r.CumulativeGasUsed),
|
||||
Bloom: &r.Bloom,
|
||||
Logs: &r.Logs,
|
||||
TxHash: &r.TxHash,
|
||||
ContractAddress: &r.ContractAddress,
|
||||
GasUsed: (*hexBig)(r.GasUsed),
|
||||
GasUsed: (*hexutil.Big)(r.GasUsed),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
@@ -69,15 +70,15 @@ type txdata struct {
|
||||
|
||||
type jsonTransaction struct {
|
||||
Hash *common.Hash `json:"hash"`
|
||||
AccountNonce *hexUint64 `json:"nonce"`
|
||||
Price *hexBig `json:"gasPrice"`
|
||||
GasLimit *hexBig `json:"gas"`
|
||||
AccountNonce *hexutil.Uint64 `json:"nonce"`
|
||||
Price *hexutil.Big `json:"gasPrice"`
|
||||
GasLimit *hexutil.Big `json:"gas"`
|
||||
Recipient *common.Address `json:"to"`
|
||||
Amount *hexBig `json:"value"`
|
||||
Payload *hexBytes `json:"input"`
|
||||
V *hexBig `json:"v"`
|
||||
R *hexBig `json:"r"`
|
||||
S *hexBig `json:"s"`
|
||||
Amount *hexutil.Big `json:"value"`
|
||||
Payload *hexutil.Bytes `json:"input"`
|
||||
V *hexutil.Big `json:"v"`
|
||||
R *hexutil.Big `json:"r"`
|
||||
S *hexutil.Big `json:"s"`
|
||||
}
|
||||
|
||||
func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
|
||||
@@ -170,15 +171,15 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
|
||||
|
||||
return json.Marshal(&jsonTransaction{
|
||||
Hash: &hash,
|
||||
AccountNonce: (*hexUint64)(&tx.data.AccountNonce),
|
||||
Price: (*hexBig)(tx.data.Price),
|
||||
GasLimit: (*hexBig)(tx.data.GasLimit),
|
||||
AccountNonce: (*hexutil.Uint64)(&tx.data.AccountNonce),
|
||||
Price: (*hexutil.Big)(tx.data.Price),
|
||||
GasLimit: (*hexutil.Big)(tx.data.GasLimit),
|
||||
Recipient: tx.data.Recipient,
|
||||
Amount: (*hexBig)(tx.data.Amount),
|
||||
Payload: (*hexBytes)(&tx.data.Payload),
|
||||
V: (*hexBig)(tx.data.V),
|
||||
R: (*hexBig)(tx.data.R),
|
||||
S: (*hexBig)(tx.data.S),
|
||||
Amount: (*hexutil.Big)(tx.data.Amount),
|
||||
Payload: (*hexutil.Bytes)(&tx.data.Payload),
|
||||
V: (*hexutil.Big)(tx.data.V),
|
||||
R: (*hexutil.Big)(tx.data.R),
|
||||
S: (*hexutil.Big)(tx.data.S),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
@@ -47,12 +48,12 @@ type Log struct {
|
||||
type jsonLog struct {
|
||||
Address *common.Address `json:"address"`
|
||||
Topics *[]common.Hash `json:"topics"`
|
||||
Data string `json:"data"`
|
||||
BlockNumber string `json:"blockNumber"`
|
||||
TxIndex string `json:"transactionIndex"`
|
||||
Data *hexutil.Bytes `json:"data"`
|
||||
BlockNumber *hexutil.Uint64 `json:"blockNumber"`
|
||||
TxIndex *hexutil.Uint `json:"transactionIndex"`
|
||||
TxHash *common.Hash `json:"transactionHash"`
|
||||
BlockHash *common.Hash `json:"blockHash"`
|
||||
Index string `json:"logIndex"`
|
||||
Index *hexutil.Uint `json:"logIndex"`
|
||||
}
|
||||
|
||||
func NewLog(address common.Address, topics []common.Hash, data []byte, number uint64) *Log {
|
||||
@@ -85,12 +86,12 @@ func (r *Log) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&jsonLog{
|
||||
Address: &r.Address,
|
||||
Topics: &r.Topics,
|
||||
Data: fmt.Sprintf("0x%x", r.Data),
|
||||
BlockNumber: fmt.Sprintf("0x%x", r.BlockNumber),
|
||||
TxIndex: fmt.Sprintf("0x%x", r.TxIndex),
|
||||
Data: (*hexutil.Bytes)(&r.Data),
|
||||
BlockNumber: (*hexutil.Uint64)(&r.BlockNumber),
|
||||
TxIndex: (*hexutil.Uint)(&r.TxIndex),
|
||||
TxHash: &r.TxHash,
|
||||
BlockHash: &r.BlockHash,
|
||||
Index: fmt.Sprintf("0x%x", r.Index),
|
||||
Index: (*hexutil.Uint)(&r.Index),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -100,29 +101,20 @@ func (r *Log) UnmarshalJSON(input []byte) error {
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.Address == nil || dec.Topics == nil || dec.Data == "" || dec.BlockNumber == "" ||
|
||||
dec.TxIndex == "" || dec.TxHash == nil || dec.BlockHash == nil || dec.Index == "" {
|
||||
if dec.Address == nil || dec.Topics == nil || dec.Data == nil || dec.BlockNumber == nil ||
|
||||
dec.TxIndex == nil || dec.TxHash == nil || dec.BlockHash == nil || dec.Index == nil {
|
||||
return errMissingLogFields
|
||||
}
|
||||
declog := Log{
|
||||
Address: *dec.Address,
|
||||
Topics: *dec.Topics,
|
||||
TxHash: *dec.TxHash,
|
||||
BlockHash: *dec.BlockHash,
|
||||
*r = Log{
|
||||
Address: *dec.Address,
|
||||
Topics: *dec.Topics,
|
||||
Data: *dec.Data,
|
||||
BlockNumber: uint64(*dec.BlockNumber),
|
||||
TxHash: *dec.TxHash,
|
||||
TxIndex: uint(*dec.TxIndex),
|
||||
BlockHash: *dec.BlockHash,
|
||||
Index: uint(*dec.Index),
|
||||
}
|
||||
if _, err := fmt.Sscanf(dec.Data, "0x%x", &declog.Data); err != nil {
|
||||
return fmt.Errorf("invalid hex log data")
|
||||
}
|
||||
if _, err := fmt.Sscanf(dec.BlockNumber, "0x%x", &declog.BlockNumber); err != nil {
|
||||
return fmt.Errorf("invalid hex log block number")
|
||||
}
|
||||
if _, err := fmt.Sscanf(dec.TxIndex, "0x%x", &declog.TxIndex); err != nil {
|
||||
return fmt.Errorf("invalid hex log tx index")
|
||||
}
|
||||
if _, err := fmt.Sscanf(dec.Index, "0x%x", &declog.Index); err != nil {
|
||||
return fmt.Errorf("invalid hex log index")
|
||||
}
|
||||
*r = declog
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@ var unmarshalLogTests = map[string]struct {
|
||||
"ok": {
|
||||
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x000000000000000000000000000000000000000000000001a055690d9db80000","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
|
||||
},
|
||||
"empty data": {
|
||||
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
|
||||
},
|
||||
"missing data": {
|
||||
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
|
||||
wantError: errMissingLogFields,
|
||||
|
||||
@@ -31,8 +31,6 @@ import (
|
||||
"github.com/ethereum/ethash"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/httpclient"
|
||||
"github.com/ethereum/go-ethereum/common/registrar/ethreg"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
@@ -127,7 +125,6 @@ type Ethereum struct {
|
||||
|
||||
eventMux *event.TypeMux
|
||||
pow *ethash.Ethash
|
||||
httpclient *httpclient.HTTPClient
|
||||
accountManager *accounts.Manager
|
||||
|
||||
ApiBackend *EthApiBackend
|
||||
@@ -173,7 +170,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
pow: pow,
|
||||
shutdownChan: make(chan bool),
|
||||
stopDbUpgrade: stopDbUpgrade,
|
||||
httpclient: httpclient.New(config.DocRoot),
|
||||
netVersionId: config.NetworkId,
|
||||
NatSpec: config.NatSpec,
|
||||
PowTest: config.PowTest,
|
||||
@@ -218,6 +214,8 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
|
||||
eth.chainConfig = config.ChainConfig
|
||||
|
||||
glog.V(logger.Info).Infoln("Chain config:", eth.chainConfig)
|
||||
|
||||
eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux())
|
||||
if err != nil {
|
||||
if err == core.ErrNoGenesis {
|
||||
@@ -354,10 +352,6 @@ func (s *Ethereum) APIs() []rpc.API {
|
||||
Version: "1.0",
|
||||
Service: s.netRPCService,
|
||||
Public: true,
|
||||
}, {
|
||||
Namespace: "admin",
|
||||
Version: "1.0",
|
||||
Service: ethreg.NewPrivateRegistarAPI(s.chainConfig, s.blockchain, s.chainDb, s.txPool, s.accountManager),
|
||||
},
|
||||
}...)
|
||||
}
|
||||
@@ -525,12 +519,6 @@ func (self *Ethereum) StopAutoDAG() {
|
||||
glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG OFF (ethash dir: %s)", ethash.DefaultDir)
|
||||
}
|
||||
|
||||
// HTTPClient returns the light http client used for fetching offchain docs
|
||||
// (natspec, source for verification)
|
||||
func (self *Ethereum) HTTPClient() *httpclient.HTTPClient {
|
||||
return self.httpclient
|
||||
}
|
||||
|
||||
// dagFiles(epoch) returns the two alternative DAG filenames (not a path)
|
||||
// 1) <revision>-<hex(seedhash[8])> 2) full-R<revision>-<hex(seedhash[8])>
|
||||
func dagFiles(epoch uint64) (string, string) {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package filters
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -28,6 +27,7 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
@@ -239,11 +239,17 @@ func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc
|
||||
return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
|
||||
}
|
||||
|
||||
rpcSub := notifier.CreateSubscription()
|
||||
var (
|
||||
rpcSub = notifier.CreateSubscription()
|
||||
matchedLogs = make(chan []Log)
|
||||
)
|
||||
|
||||
logsSub, err := api.events.SubscribeLogs(crit, matchedLogs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
matchedLogs := make(chan []Log)
|
||||
logsSub := api.events.SubscribeLogs(crit, matchedLogs)
|
||||
|
||||
for {
|
||||
select {
|
||||
@@ -276,18 +282,20 @@ type FilterCriteria struct {
|
||||
// used to retrieve logs when the state changes. This method cannot be
|
||||
// used to fetch logs that are already stored in the state.
|
||||
//
|
||||
// Default criteria for the from and to block are "latest".
|
||||
// Using "latest" as block number will return logs for mined blocks.
|
||||
// Using "pending" as block number returns logs for not yet mined (pending) blocks.
|
||||
// In case logs are removed (chain reorg) previously returned logs are returned
|
||||
// again but with the removed property set to true.
|
||||
//
|
||||
// In case "fromBlock" > "toBlock" an error is returned.
|
||||
//
|
||||
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter
|
||||
func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) rpc.ID {
|
||||
var (
|
||||
logs = make(chan []Log)
|
||||
logsSub = api.events.SubscribeLogs(crit, logs)
|
||||
)
|
||||
|
||||
if crit.FromBlock == nil {
|
||||
crit.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
|
||||
}
|
||||
if crit.ToBlock == nil {
|
||||
crit.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
|
||||
func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) {
|
||||
logs := make(chan []Log)
|
||||
logsSub, err := api.events.SubscribeLogs(crit, logs)
|
||||
if err != nil {
|
||||
return rpc.ID(""), err
|
||||
}
|
||||
|
||||
api.filtersMu.Lock()
|
||||
@@ -312,7 +320,7 @@ func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) rpc.ID {
|
||||
}
|
||||
}()
|
||||
|
||||
return logsSub.ID
|
||||
return logsSub.ID, nil
|
||||
}
|
||||
|
||||
// GetLogs returns logs matching the given argument that are stored within the state.
|
||||
@@ -363,28 +371,38 @@ func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]Log
|
||||
api.filtersMu.Unlock()
|
||||
|
||||
if !found || f.typ != LogsSubscription {
|
||||
return []Log{}, nil
|
||||
return nil, fmt.Errorf("filter not found")
|
||||
}
|
||||
|
||||
filter := New(api.backend, api.useMipMap)
|
||||
filter.SetBeginBlock(f.crit.FromBlock.Int64())
|
||||
filter.SetEndBlock(f.crit.ToBlock.Int64())
|
||||
if f.crit.FromBlock != nil {
|
||||
filter.SetBeginBlock(f.crit.FromBlock.Int64())
|
||||
} else {
|
||||
filter.SetBeginBlock(rpc.LatestBlockNumber.Int64())
|
||||
}
|
||||
if f.crit.ToBlock != nil {
|
||||
filter.SetEndBlock(f.crit.ToBlock.Int64())
|
||||
} else {
|
||||
filter.SetEndBlock(rpc.LatestBlockNumber.Int64())
|
||||
}
|
||||
filter.SetAddresses(f.crit.Addresses)
|
||||
filter.SetTopics(f.crit.Topics)
|
||||
|
||||
logs, err := filter.Find(ctx)
|
||||
return returnLogs(logs), err
|
||||
logs, err:= filter.Find(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return returnLogs(logs), nil
|
||||
}
|
||||
|
||||
// GetFilterChanges returns the logs for the filter with the given id since
|
||||
// last time is was called. This can be used for polling.
|
||||
//
|
||||
// For pending transaction and block filters the result is []common.Hash.
|
||||
// (pending)Log filters return []Log. If the filter could not be found
|
||||
// []interface{}{} is returned.
|
||||
// (pending)Log filters return []Log.
|
||||
//
|
||||
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges
|
||||
func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) interface{} {
|
||||
func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) {
|
||||
api.filtersMu.Lock()
|
||||
defer api.filtersMu.Unlock()
|
||||
|
||||
@@ -400,15 +418,15 @@ func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) interface{} {
|
||||
case PendingTransactionsSubscription, BlocksSubscription:
|
||||
hashes := f.hashes
|
||||
f.hashes = nil
|
||||
return returnHashes(hashes)
|
||||
case PendingLogsSubscription, LogsSubscription:
|
||||
return returnHashes(hashes), nil
|
||||
case LogsSubscription:
|
||||
logs := f.logs
|
||||
f.logs = nil
|
||||
return returnLogs(logs)
|
||||
return returnLogs(logs), nil
|
||||
}
|
||||
}
|
||||
|
||||
return []interface{}{}
|
||||
return []interface{}{}, fmt.Errorf("filter not found")
|
||||
}
|
||||
|
||||
// returnHashes is a helper that will return an empty hash array case the given hash array is nil,
|
||||
@@ -443,15 +461,11 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if raw.From == nil || raw.From.Int64() < 0 {
|
||||
args.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
|
||||
} else {
|
||||
if raw.From != nil {
|
||||
args.FromBlock = big.NewInt(raw.From.Int64())
|
||||
}
|
||||
|
||||
if raw.ToBlock == nil || raw.ToBlock.Int64() < 0 {
|
||||
args.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
|
||||
} else {
|
||||
if raw.ToBlock != nil {
|
||||
args.ToBlock = big.NewInt(raw.ToBlock.Int64())
|
||||
}
|
||||
|
||||
@@ -459,52 +473,28 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
|
||||
|
||||
if raw.Addresses != nil {
|
||||
// raw.Address can contain a single address or an array of addresses
|
||||
var addresses []common.Address
|
||||
if strAddrs, ok := raw.Addresses.([]interface{}); ok {
|
||||
for i, addr := range strAddrs {
|
||||
switch rawAddr := raw.Addresses.(type) {
|
||||
case []interface{}:
|
||||
for i, addr := range rawAddr {
|
||||
if strAddr, ok := addr.(string); ok {
|
||||
if len(strAddr) >= 2 && strAddr[0] == '0' && (strAddr[1] == 'x' || strAddr[1] == 'X') {
|
||||
strAddr = strAddr[2:]
|
||||
}
|
||||
if decAddr, err := hex.DecodeString(strAddr); err == nil {
|
||||
addresses = append(addresses, common.BytesToAddress(decAddr))
|
||||
} else {
|
||||
return fmt.Errorf("invalid address given")
|
||||
addr, err := decodeAddress(strAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid address at index %d: %v", i, err)
|
||||
}
|
||||
args.Addresses = append(args.Addresses, addr)
|
||||
} else {
|
||||
return fmt.Errorf("invalid address on index %d", i)
|
||||
return fmt.Errorf("non-string address at index %d", i)
|
||||
}
|
||||
}
|
||||
} else if singleAddr, ok := raw.Addresses.(string); ok {
|
||||
if len(singleAddr) >= 2 && singleAddr[0] == '0' && (singleAddr[1] == 'x' || singleAddr[1] == 'X') {
|
||||
singleAddr = singleAddr[2:]
|
||||
case string:
|
||||
addr, err := decodeAddress(rawAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid address: %v", err)
|
||||
}
|
||||
if decAddr, err := hex.DecodeString(singleAddr); err == nil {
|
||||
addresses = append(addresses, common.BytesToAddress(decAddr))
|
||||
} else {
|
||||
return fmt.Errorf("invalid address given")
|
||||
}
|
||||
} else {
|
||||
return errors.New("invalid address(es) given")
|
||||
args.Addresses = []common.Address{addr}
|
||||
default:
|
||||
return errors.New("invalid addresses in query")
|
||||
}
|
||||
args.Addresses = addresses
|
||||
}
|
||||
|
||||
// helper function which parses a string to a topic hash
|
||||
topicConverter := func(raw string) (common.Hash, error) {
|
||||
if len(raw) == 0 {
|
||||
return common.Hash{}, nil
|
||||
}
|
||||
if len(raw) >= 2 && raw[0] == '0' && (raw[1] == 'x' || raw[1] == 'X') {
|
||||
raw = raw[2:]
|
||||
}
|
||||
if len(raw) != 2*common.HashLength {
|
||||
return common.Hash{}, errors.New("invalid topic(s)")
|
||||
}
|
||||
if decAddr, err := hex.DecodeString(raw); err == nil {
|
||||
return common.BytesToHash(decAddr), nil
|
||||
}
|
||||
return common.Hash{}, errors.New("invalid topic(s)")
|
||||
}
|
||||
|
||||
// topics is an array consisting of strings and/or arrays of strings.
|
||||
@@ -512,20 +502,25 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
|
||||
if len(raw.Topics) > 0 {
|
||||
args.Topics = make([][]common.Hash, len(raw.Topics))
|
||||
for i, t := range raw.Topics {
|
||||
if t == nil { // ignore topic when matching logs
|
||||
switch topic := t.(type) {
|
||||
case nil:
|
||||
// ignore topic when matching logs
|
||||
args.Topics[i] = []common.Hash{common.Hash{}}
|
||||
} else if topic, ok := t.(string); ok { // match specific topic
|
||||
top, err := topicConverter(topic)
|
||||
|
||||
case string:
|
||||
// match specific topic
|
||||
top, err := decodeTopic(topic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args.Topics[i] = []common.Hash{top}
|
||||
} else if topics, ok := t.([]interface{}); ok { // or case e.g. [null, "topic0", "topic1"]
|
||||
for _, rawTopic := range topics {
|
||||
case []interface{}:
|
||||
// or case e.g. [null, "topic0", "topic1"]
|
||||
for _, rawTopic := range topic {
|
||||
if rawTopic == nil {
|
||||
args.Topics[i] = append(args.Topics[i], common.Hash{})
|
||||
} else if topic, ok := rawTopic.(string); ok {
|
||||
parsed, err := topicConverter(topic)
|
||||
parsed, err := decodeTopic(topic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -534,7 +529,7 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
|
||||
return fmt.Errorf("invalid topic(s)")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
default:
|
||||
return fmt.Errorf("invalid topic(s)")
|
||||
}
|
||||
}
|
||||
@@ -542,3 +537,19 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeAddress(s string) (common.Address, error) {
|
||||
b, err := hexutil.Decode(s)
|
||||
if err == nil && len(b) != common.AddressLength {
|
||||
err = fmt.Errorf("hex has invalid length %d after decoding", len(b))
|
||||
}
|
||||
return common.BytesToAddress(b), err
|
||||
}
|
||||
|
||||
func decodeTopic(s string) (common.Hash, error) {
|
||||
b, err := hexutil.Decode(s)
|
||||
if err == nil && len(b) != common.HashLength {
|
||||
err = fmt.Errorf("hex has invalid length %d after decoding", len(b))
|
||||
}
|
||||
return common.BytesToHash(b), err
|
||||
}
|
||||
|
||||
@@ -42,11 +42,11 @@ func TestUnmarshalJSONNewFilterArgs(t *testing.T) {
|
||||
if err := json.Unmarshal([]byte("{}"), &test0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if test0.FromBlock.Int64() != rpc.LatestBlockNumber.Int64() {
|
||||
t.Fatalf("expected %d, got %d", rpc.LatestBlockNumber, test0.FromBlock)
|
||||
if test0.FromBlock != nil {
|
||||
t.Fatalf("expected nil, got %d", test0.FromBlock)
|
||||
}
|
||||
if test0.ToBlock.Int64() != rpc.LatestBlockNumber.Int64() {
|
||||
t.Fatalf("expected %d, got %d", rpc.LatestBlockNumber, test0.ToBlock)
|
||||
if test0.ToBlock != nil {
|
||||
t.Fatalf("expected nil, got %d", test0.ToBlock)
|
||||
}
|
||||
if len(test0.Addresses) != 0 {
|
||||
t.Fatalf("expected 0 addresses, got %d", len(test0.Addresses))
|
||||
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
@@ -162,7 +164,7 @@ func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, er
|
||||
}
|
||||
unfiltered = append(unfiltered, rl...)
|
||||
}
|
||||
logs = append(logs, filterLogs(unfiltered, f.addresses, f.topics)...)
|
||||
logs = append(logs, filterLogs(unfiltered, nil, nil, f.addresses, f.topics)...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,12 +181,18 @@ func includes(addresses []common.Address, a common.Address) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func filterLogs(logs []Log, addresses []common.Address, topics [][]common.Hash) []Log {
|
||||
func filterLogs(logs []Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []Log {
|
||||
var ret []Log
|
||||
|
||||
// Filter the logs for interesting stuff
|
||||
Logs:
|
||||
for _, log := range logs {
|
||||
if fromBlock != nil && fromBlock.Int64() >= 0 && uint64(fromBlock.Int64()) > log.BlockNumber {
|
||||
continue
|
||||
}
|
||||
if toBlock != nil && toBlock.Int64() >= 0 && uint64(toBlock.Int64()) < log.BlockNumber {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(addresses) > 0 && !includes(addresses, log.Address) {
|
||||
continue
|
||||
}
|
||||
@@ -211,7 +219,6 @@ Logs:
|
||||
continue Logs
|
||||
}
|
||||
}
|
||||
|
||||
ret = append(ret, log)
|
||||
}
|
||||
|
||||
|
||||
@@ -43,13 +43,17 @@ const (
|
||||
UnknownSubscription Type = iota
|
||||
// LogsSubscription queries for new or removed (chain reorg) logs
|
||||
LogsSubscription
|
||||
// PendingLogsSubscription queries for logs for the pending block
|
||||
// PendingLogsSubscription queries for logs in pending blocks
|
||||
PendingLogsSubscription
|
||||
// MinedAndPendingLogsSubscription queries for logs in mined and pending blocks.
|
||||
MinedAndPendingLogsSubscription
|
||||
// PendingTransactionsSubscription queries tx hashes for pending
|
||||
// transactions entering the pending state
|
||||
PendingTransactionsSubscription
|
||||
// BlocksSubscription queries hashes for blocks that are imported
|
||||
BlocksSubscription
|
||||
// LastSubscription keeps track of the last index
|
||||
LastIndexSubscription
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -63,19 +67,26 @@ type Log struct {
|
||||
Removed bool `json:"removed"`
|
||||
}
|
||||
|
||||
// MarshalJSON returns *l as the JSON encoding of l.
|
||||
func (l *Log) MarshalJSON() ([]byte, error) {
|
||||
fields := map[string]interface{}{
|
||||
"address": l.Address,
|
||||
"data": fmt.Sprintf("0x%x", l.Data),
|
||||
"blockNumber": fmt.Sprintf("%#x", l.BlockNumber),
|
||||
"blockNumber": nil,
|
||||
"logIndex": fmt.Sprintf("%#x", l.Index),
|
||||
"blockHash": l.BlockHash,
|
||||
"blockHash": nil,
|
||||
"transactionHash": l.TxHash,
|
||||
"transactionIndex": fmt.Sprintf("%#x", l.TxIndex),
|
||||
"topics": l.Topics,
|
||||
"removed": l.Removed,
|
||||
}
|
||||
|
||||
// mined logs
|
||||
if l.BlockHash != (common.Hash{}) {
|
||||
fields["blockNumber"] = fmt.Sprintf("%#x", l.BlockNumber)
|
||||
fields["blockHash"] = l.BlockHash
|
||||
}
|
||||
|
||||
return json.Marshal(fields)
|
||||
}
|
||||
|
||||
@@ -169,8 +180,65 @@ func (es *EventSystem) subscribe(sub *subscription) *Subscription {
|
||||
}
|
||||
|
||||
// SubscribeLogs creates a subscription that will write all logs matching the
|
||||
// given criteria to the given logs channel. Default value for the from and to
|
||||
// block is "latest". If the fromBlock > toBlock an error is returned.
|
||||
func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []Log) (*Subscription, error) {
|
||||
var from, to rpc.BlockNumber
|
||||
if crit.FromBlock == nil {
|
||||
from = rpc.LatestBlockNumber
|
||||
} else {
|
||||
from = rpc.BlockNumber(crit.FromBlock.Int64())
|
||||
}
|
||||
if crit.ToBlock == nil {
|
||||
to = rpc.LatestBlockNumber
|
||||
} else {
|
||||
to = rpc.BlockNumber(crit.ToBlock.Int64())
|
||||
}
|
||||
|
||||
// only interested in pending logs
|
||||
if from == rpc.PendingBlockNumber && to == rpc.PendingBlockNumber {
|
||||
return es.subscribePendingLogs(crit, logs), nil
|
||||
}
|
||||
// only interested in new mined logs
|
||||
if from == rpc.LatestBlockNumber && to == rpc.LatestBlockNumber {
|
||||
return es.subscribeLogs(crit, logs), nil
|
||||
}
|
||||
// only interested in mined logs within a specific block range
|
||||
if from >= 0 && to >= 0 && to >= from {
|
||||
return es.subscribeLogs(crit, logs), nil
|
||||
}
|
||||
// interested in mined logs from a specific block number, new logs and pending logs
|
||||
if from >= rpc.LatestBlockNumber && to == rpc.PendingBlockNumber {
|
||||
return es.subscribeMinedPendingLogs(crit, logs), nil
|
||||
}
|
||||
// interested in logs from a specific block number to new mined blocks
|
||||
if from >= 0 && to == rpc.LatestBlockNumber {
|
||||
return es.subscribeLogs(crit, logs), nil
|
||||
}
|
||||
return nil, fmt.Errorf("invalid from and to block combination: from > to")
|
||||
}
|
||||
|
||||
// subscribeMinedPendingLogs creates a subscription that returned mined and
|
||||
// pending logs that match the given criteria.
|
||||
func (es *EventSystem) subscribeMinedPendingLogs(crit FilterCriteria, logs chan []Log) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: MinedAndPendingLogsSubscription,
|
||||
logsCrit: crit,
|
||||
created: time.Now(),
|
||||
logs: logs,
|
||||
hashes: make(chan common.Hash),
|
||||
headers: make(chan *types.Header),
|
||||
installed: make(chan struct{}),
|
||||
err: make(chan error),
|
||||
}
|
||||
|
||||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
// subscribeLogs creates a subscription that will write all logs matching the
|
||||
// given criteria to the given logs channel.
|
||||
func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []Log) *Subscription {
|
||||
func (es *EventSystem) subscribeLogs(crit FilterCriteria, logs chan []Log) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: LogsSubscription,
|
||||
@@ -186,9 +254,9 @@ func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []Log) *Subs
|
||||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
// SubscribePendingLogs creates a subscription that will write pending logs matching the
|
||||
// given criteria to the given channel.
|
||||
func (es *EventSystem) SubscribePendingLogs(crit FilterCriteria, logs chan []Log) *Subscription {
|
||||
// subscribePendingLogs creates a subscription that writes transaction hashes for
|
||||
// transactions that enter the transaction pool.
|
||||
func (es *EventSystem) subscribePendingLogs(crit FilterCriteria, logs chan []Log) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: PendingLogsSubscription,
|
||||
@@ -204,23 +272,6 @@ func (es *EventSystem) SubscribePendingLogs(crit FilterCriteria, logs chan []Log
|
||||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
// SubscribePendingTxEvents creates a sbuscription that writes transaction hashes for
|
||||
// transactions that enter the transaction pool.
|
||||
func (es *EventSystem) SubscribePendingTxEvents(hashes chan common.Hash) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: PendingTransactionsSubscription,
|
||||
created: time.Now(),
|
||||
logs: make(chan []Log),
|
||||
hashes: hashes,
|
||||
headers: make(chan *types.Header),
|
||||
installed: make(chan struct{}),
|
||||
err: make(chan error),
|
||||
}
|
||||
|
||||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
// SubscribeNewHeads creates a subscription that writes the header of a block that is
|
||||
// imported in the chain.
|
||||
func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscription {
|
||||
@@ -238,6 +289,23 @@ func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscripti
|
||||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
// SubscribePendingTxEvents creates a subscription that writes transaction hashes for
|
||||
// transactions that enter the transaction pool.
|
||||
func (es *EventSystem) SubscribePendingTxEvents(hashes chan common.Hash) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: PendingTransactionsSubscription,
|
||||
created: time.Now(),
|
||||
logs: make(chan []Log),
|
||||
hashes: hashes,
|
||||
headers: make(chan *types.Header),
|
||||
installed: make(chan struct{}),
|
||||
err: make(chan error),
|
||||
}
|
||||
|
||||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
type filterIndex map[Type]map[rpc.ID]*subscription
|
||||
|
||||
// broadcast event to filters that match criteria.
|
||||
@@ -251,7 +319,7 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) {
|
||||
if len(e) > 0 {
|
||||
for _, f := range filters[LogsSubscription] {
|
||||
if ev.Time.After(f.created) {
|
||||
if matchedLogs := filterLogs(convertLogs(e, false), f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
if matchedLogs := filterLogs(convertLogs(e, false), f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
f.logs <- matchedLogs
|
||||
}
|
||||
}
|
||||
@@ -260,7 +328,7 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) {
|
||||
case core.RemovedLogsEvent:
|
||||
for _, f := range filters[LogsSubscription] {
|
||||
if ev.Time.After(f.created) {
|
||||
if matchedLogs := filterLogs(convertLogs(e.Logs, true), f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
if matchedLogs := filterLogs(convertLogs(e.Logs, true), f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
f.logs <- matchedLogs
|
||||
}
|
||||
}
|
||||
@@ -268,7 +336,7 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) {
|
||||
case core.PendingLogsEvent:
|
||||
for _, f := range filters[PendingLogsSubscription] {
|
||||
if ev.Time.After(f.created) {
|
||||
if matchedLogs := filterLogs(convertLogs(e.Logs, false), f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
if matchedLogs := filterLogs(convertLogs(e.Logs, false), nil, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
f.logs <- matchedLogs
|
||||
}
|
||||
}
|
||||
@@ -351,8 +419,8 @@ func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.
|
||||
}
|
||||
unfiltered = append(unfiltered, rl...)
|
||||
}
|
||||
logs := filterLogs(unfiltered, addresses, topics)
|
||||
//fmt.Println("found", len(logs))
|
||||
|
||||
logs := filterLogs(unfiltered, nil, nil, addresses, topics)
|
||||
return logs
|
||||
}
|
||||
return nil
|
||||
@@ -364,6 +432,11 @@ func (es *EventSystem) eventLoop() {
|
||||
index = make(filterIndex)
|
||||
sub = es.mux.Subscribe(core.PendingLogsEvent{}, core.RemovedLogsEvent{}, vm.Logs{}, core.TxPreEvent{}, core.ChainEvent{})
|
||||
)
|
||||
|
||||
for i := UnknownSubscription; i < LastIndexSubscription; i++ {
|
||||
index[i] = make(map[rpc.ID]*subscription)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case ev, active := <-sub.Chan():
|
||||
@@ -372,13 +445,22 @@ func (es *EventSystem) eventLoop() {
|
||||
}
|
||||
es.broadcast(index, ev)
|
||||
case f := <-es.install:
|
||||
if _, found := index[f.typ]; !found {
|
||||
index[f.typ] = make(map[rpc.ID]*subscription)
|
||||
if f.typ == MinedAndPendingLogsSubscription {
|
||||
// the type are logs and pending logs subscriptions
|
||||
index[LogsSubscription][f.id] = f
|
||||
index[PendingLogsSubscription][f.id] = f
|
||||
} else {
|
||||
index[f.typ][f.id] = f
|
||||
}
|
||||
index[f.typ][f.id] = f
|
||||
close(f.installed)
|
||||
case f := <-es.uninstall:
|
||||
delete(index[f.typ], f.id)
|
||||
if f.typ == MinedAndPendingLogsSubscription {
|
||||
// the type are logs and pending logs subscriptions
|
||||
delete(index[LogsSubscription], f.id)
|
||||
delete(index[PendingLogsSubscription], f.id)
|
||||
} else {
|
||||
delete(index[f.typ], f.id)
|
||||
}
|
||||
close(f.err)
|
||||
}
|
||||
}
|
||||
@@ -386,6 +468,7 @@ func (es *EventSystem) eventLoop() {
|
||||
|
||||
// convertLogs is a helper utility that converts vm.Logs to []filter.Log.
|
||||
func convertLogs(in vm.Logs, removed bool) []Log {
|
||||
|
||||
logs := make([]Log, len(in))
|
||||
for i, l := range in {
|
||||
logs[i] = Log{l, removed}
|
||||
|
||||
@@ -34,13 +34,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
)
|
||||
|
||||
type testBackend struct {
|
||||
mux *event.TypeMux
|
||||
db ethdb.Database
|
||||
@@ -81,6 +74,11 @@ func TestBlockSubscription(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
|
||||
genesis = core.WriteGenesisBlockForTesting(db)
|
||||
chain, _ = core.GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *core.BlockGen) {})
|
||||
chainEvents = []core.ChainEvent{}
|
||||
@@ -130,6 +128,11 @@ func TestPendingTxFilter(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
|
||||
transactions = []*types.Transaction{
|
||||
types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil),
|
||||
types.NewTransaction(1, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil),
|
||||
@@ -150,9 +153,13 @@ func TestPendingTxFilter(t *testing.T) {
|
||||
}
|
||||
|
||||
for {
|
||||
h := api.GetFilterChanges(fid0).([]common.Hash)
|
||||
hashes = append(hashes, h...)
|
||||
results, err := api.GetFilterChanges(fid0)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to retrieve logs: %v", err)
|
||||
}
|
||||
|
||||
h := results.([]common.Hash)
|
||||
hashes = append(hashes, h...)
|
||||
if len(hashes) >= len(transactions) {
|
||||
break
|
||||
}
|
||||
@@ -167,11 +174,86 @@ func TestPendingTxFilter(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestLogFilterCreation test whether a given filter criteria makes sense.
|
||||
// If not it must return an error.
|
||||
func TestLogFilterCreation(t *testing.T) {
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
|
||||
testCases = []struct {
|
||||
crit FilterCriteria
|
||||
success bool
|
||||
}{
|
||||
// defaults
|
||||
{FilterCriteria{}, true},
|
||||
// valid block number range
|
||||
{FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2)}, true},
|
||||
// "mined" block range to pending
|
||||
{FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, true},
|
||||
// new mined and pending blocks
|
||||
{FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, true},
|
||||
// from block "higher" than to block
|
||||
{FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(1)}, false},
|
||||
// from block "higher" than to block
|
||||
{FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false},
|
||||
// from block "higher" than to block
|
||||
{FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false},
|
||||
// from block "higher" than to block
|
||||
{FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, false},
|
||||
}
|
||||
)
|
||||
|
||||
for i, test := range testCases {
|
||||
_, err := api.NewFilter(test.crit)
|
||||
if test.success && err != nil {
|
||||
t.Errorf("expected filter creation for case %d to success, got %v", i, err)
|
||||
}
|
||||
if !test.success && err == nil {
|
||||
t.Errorf("expected testcase %d to fail with an error", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestInvalidLogFilterCreation tests whether invalid filter log criteria results in an error
|
||||
// when the filter is created.
|
||||
func TestInvalidLogFilterCreation(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
)
|
||||
|
||||
// different situations where log filter creation should fail.
|
||||
// Reason: fromBlock > toBlock
|
||||
testCases := []FilterCriteria{
|
||||
0: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())},
|
||||
1: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)},
|
||||
2: {FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)},
|
||||
}
|
||||
|
||||
for i, test := range testCases {
|
||||
if _, err := api.NewFilter(test); err == nil {
|
||||
t.Errorf("Expected NewFilter for case #%d to fail", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestLogFilter tests whether log filters match the correct logs that are posted to the event mux.
|
||||
func TestLogFilter(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
|
||||
firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111")
|
||||
secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222")
|
||||
thirdAddress = common.HexToAddress("0x3333333333333333333333333333333333333333")
|
||||
@@ -180,8 +262,8 @@ func TestLogFilter(t *testing.T) {
|
||||
secondTopic = common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222")
|
||||
notUsedTopic = common.HexToHash("0x9999999999999999999999999999999999999999999999999999999999999999")
|
||||
|
||||
// posted twice, once as vm.Logs and once as core.PendingLogsEvent
|
||||
allLogs = vm.Logs{
|
||||
// Note, these are used for comparison of the test cases.
|
||||
vm.NewLog(firstAddr, []common.Hash{}, []byte(""), 0),
|
||||
vm.NewLog(firstAddr, []common.Hash{firstTopic}, []byte(""), 1),
|
||||
vm.NewLog(secondAddr, []common.Hash{firstTopic}, []byte(""), 1),
|
||||
@@ -189,45 +271,64 @@ func TestLogFilter(t *testing.T) {
|
||||
vm.NewLog(thirdAddress, []common.Hash{secondTopic}, []byte(""), 3),
|
||||
}
|
||||
|
||||
expectedCase7 = vm.Logs{allLogs[3], allLogs[4], allLogs[0], allLogs[1], allLogs[2], allLogs[3], allLogs[4]}
|
||||
expectedCase11 = vm.Logs{allLogs[1], allLogs[2], allLogs[1], allLogs[2]}
|
||||
|
||||
testCases = []struct {
|
||||
crit FilterCriteria
|
||||
expected vm.Logs
|
||||
id rpc.ID
|
||||
}{
|
||||
// match all
|
||||
{FilterCriteria{}, allLogs, ""},
|
||||
0: {FilterCriteria{}, allLogs, ""},
|
||||
// match none due to no matching addresses
|
||||
{FilterCriteria{Addresses: []common.Address{common.Address{}, notUsedAddress}, Topics: [][]common.Hash{allLogs[0].Topics}}, vm.Logs{}, ""},
|
||||
1: {FilterCriteria{Addresses: []common.Address{common.Address{}, notUsedAddress}, Topics: [][]common.Hash{allLogs[0].Topics}}, vm.Logs{}, ""},
|
||||
// match logs based on addresses, ignore topics
|
||||
{FilterCriteria{Addresses: []common.Address{firstAddr}}, allLogs[:2], ""},
|
||||
2: {FilterCriteria{Addresses: []common.Address{firstAddr}}, allLogs[:2], ""},
|
||||
// match none due to no matching topics (match with address)
|
||||
{FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{[]common.Hash{notUsedTopic}}}, vm.Logs{}, ""},
|
||||
3: {FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{[]common.Hash{notUsedTopic}}}, vm.Logs{}, ""},
|
||||
// match logs based on addresses and topics
|
||||
{FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{[]common.Hash{firstTopic, secondTopic}}}, allLogs[3:5], ""},
|
||||
4: {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{[]common.Hash{firstTopic, secondTopic}}}, allLogs[3:5], ""},
|
||||
// match logs based on multiple addresses and "or" topics
|
||||
{FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{[]common.Hash{firstTopic, secondTopic}}}, allLogs[2:5], ""},
|
||||
// block numbers are ignored for filters created with New***Filter, these return all logs that match the given criterias when the state changes
|
||||
{FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(1), ToBlock: big.NewInt(2)}, allLogs[:2], ""},
|
||||
5: {FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{[]common.Hash{firstTopic, secondTopic}}}, allLogs[2:5], ""},
|
||||
// logs in the pending block
|
||||
6: {FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, allLogs[:2], ""},
|
||||
// mined logs with block num >= 2 or pending logs
|
||||
7: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, expectedCase7, ""},
|
||||
// all "mined" logs with block num >= 2
|
||||
8: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs[3:], ""},
|
||||
// all "mined" logs
|
||||
9: {FilterCriteria{ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs, ""},
|
||||
// all "mined" logs with 1>= block num <=2 and topic secondTopic
|
||||
10: {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2), Topics: [][]common.Hash{[]common.Hash{secondTopic}}}, allLogs[3:4], ""},
|
||||
// all "mined" and pending logs with topic firstTopic
|
||||
11: {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), Topics: [][]common.Hash{[]common.Hash{firstTopic}}}, expectedCase11, ""},
|
||||
}
|
||||
|
||||
err error
|
||||
)
|
||||
|
||||
// create all filters
|
||||
for i := range testCases {
|
||||
testCases[i].id = api.NewFilter(testCases[i].crit)
|
||||
testCases[i].id, _ = api.NewFilter(testCases[i].crit)
|
||||
}
|
||||
|
||||
// raise events
|
||||
time.Sleep(1 * time.Second)
|
||||
if err = mux.Post(allLogs); err != nil {
|
||||
if err := mux.Post(allLogs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := mux.Post(core.PendingLogsEvent{Logs: allLogs}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i, tt := range testCases {
|
||||
var fetched []Log
|
||||
for { // fetch all expected logs
|
||||
fetched = append(fetched, api.GetFilterChanges(tt.id).([]Log)...)
|
||||
results, err := api.GetFilterChanges(tt.id)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to fetch logs: %v", err)
|
||||
}
|
||||
|
||||
fetched = append(fetched, results.([]Log)...)
|
||||
if len(fetched) >= len(tt.expected) {
|
||||
break
|
||||
}
|
||||
@@ -247,7 +348,6 @@ func TestLogFilter(t *testing.T) {
|
||||
if !reflect.DeepEqual(fetched[l].Log, tt.expected[l]) {
|
||||
t.Errorf("invalid log on index %d for case %d", l, i)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,6 +357,11 @@ func TestPendingLogsSubscription(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
|
||||
firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111")
|
||||
secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222")
|
||||
thirdAddress = common.HexToAddress("0x3333333333333333333333333333333333333333")
|
||||
@@ -319,7 +424,7 @@ func TestPendingLogsSubscription(t *testing.T) {
|
||||
// (some) events are posted.
|
||||
for i := range testCases {
|
||||
testCases[i].c = make(chan []Log)
|
||||
testCases[i].sub = api.events.SubscribePendingLogs(testCases[i].crit, testCases[i].c)
|
||||
testCases[i].sub, _ = api.events.SubscribeLogs(testCases[i].crit, testCases[i].c)
|
||||
}
|
||||
|
||||
for n, test := range testCases {
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
)
|
||||
|
||||
func makeReceipt(addr common.Address) *types.Receipt {
|
||||
@@ -51,6 +52,7 @@ func BenchmarkMipmaps(b *testing.B) {
|
||||
|
||||
var (
|
||||
db, _ = ethdb.NewLDBDatabase(dir, 0, 0)
|
||||
mux = new(event.TypeMux)
|
||||
backend = &testBackend{mux, db}
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
@@ -126,6 +128,7 @@ func TestFilters(t *testing.T) {
|
||||
|
||||
var (
|
||||
db, _ = ethdb.NewLDBDatabase(dir, 0, 0)
|
||||
mux = new(event.TypeMux)
|
||||
backend = &testBackend{mux, db}
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
@@ -156,9 +157,9 @@ func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (*typ
|
||||
|
||||
// TransactionCount returns the total number of transactions in the given block.
|
||||
func (ec *Client) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) {
|
||||
var num rpc.HexNumber
|
||||
var num hexutil.Uint
|
||||
err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByHash", blockHash)
|
||||
return num.Uint(), err
|
||||
return uint(num), err
|
||||
}
|
||||
|
||||
// TransactionInBlock returns a single transaction at index in the given block.
|
||||
@@ -196,11 +197,11 @@ func toBlockNumArg(number *big.Int) string {
|
||||
}
|
||||
|
||||
type rpcProgress struct {
|
||||
StartingBlock rpc.HexNumber
|
||||
CurrentBlock rpc.HexNumber
|
||||
HighestBlock rpc.HexNumber
|
||||
PulledStates rpc.HexNumber
|
||||
KnownStates rpc.HexNumber
|
||||
StartingBlock hexutil.Uint64
|
||||
CurrentBlock hexutil.Uint64
|
||||
HighestBlock hexutil.Uint64
|
||||
PulledStates hexutil.Uint64
|
||||
KnownStates hexutil.Uint64
|
||||
}
|
||||
|
||||
// SyncProgress retrieves the current progress of the sync algorithm. If there's
|
||||
@@ -220,11 +221,11 @@ func (ec *Client) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, err
|
||||
return nil, err
|
||||
}
|
||||
return ðereum.SyncProgress{
|
||||
StartingBlock: progress.StartingBlock.Uint64(),
|
||||
CurrentBlock: progress.CurrentBlock.Uint64(),
|
||||
HighestBlock: progress.HighestBlock.Uint64(),
|
||||
PulledStates: progress.PulledStates.Uint64(),
|
||||
KnownStates: progress.KnownStates.Uint64(),
|
||||
StartingBlock: uint64(progress.StartingBlock),
|
||||
CurrentBlock: uint64(progress.CurrentBlock),
|
||||
HighestBlock: uint64(progress.HighestBlock),
|
||||
PulledStates: uint64(progress.PulledStates),
|
||||
KnownStates: uint64(progress.KnownStates),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -239,7 +240,7 @@ func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header)
|
||||
// BalanceAt returns the wei balance of the given account.
|
||||
// The block number can be nil, in which case the balance is taken from the latest known block.
|
||||
func (ec *Client) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||
var result rpc.HexNumber
|
||||
var result hexutil.Big
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, toBlockNumArg(blockNumber))
|
||||
return (*big.Int)(&result), err
|
||||
}
|
||||
@@ -247,7 +248,7 @@ func (ec *Client) BalanceAt(ctx context.Context, account common.Address, blockNu
|
||||
// StorageAt returns the value of key in the contract storage of the given account.
|
||||
// The block number can be nil, in which case the value is taken from the latest known block.
|
||||
func (ec *Client) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
|
||||
var result rpc.HexBytes
|
||||
var result hexutil.Bytes
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, toBlockNumArg(blockNumber))
|
||||
return result, err
|
||||
}
|
||||
@@ -255,7 +256,7 @@ func (ec *Client) StorageAt(ctx context.Context, account common.Address, key com
|
||||
// CodeAt returns the contract code of the given account.
|
||||
// The block number can be nil, in which case the code is taken from the latest known block.
|
||||
func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||
var result rpc.HexBytes
|
||||
var result hexutil.Bytes
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getCode", account, toBlockNumArg(blockNumber))
|
||||
return result, err
|
||||
}
|
||||
@@ -263,9 +264,9 @@ func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumbe
|
||||
// NonceAt returns the account nonce of the given account.
|
||||
// The block number can be nil, in which case the nonce is taken from the latest known block.
|
||||
func (ec *Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
|
||||
var result rpc.HexNumber
|
||||
var result hexutil.Uint64
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, toBlockNumArg(blockNumber))
|
||||
return result.Uint64(), err
|
||||
return uint64(result), err
|
||||
}
|
||||
|
||||
// Filters
|
||||
@@ -286,7 +287,7 @@ func toFilterArg(q ethereum.FilterQuery) interface{} {
|
||||
arg := map[string]interface{}{
|
||||
"fromBlock": toBlockNumArg(q.FromBlock),
|
||||
"toBlock": toBlockNumArg(q.ToBlock),
|
||||
"addresses": q.Addresses,
|
||||
"address": q.Addresses,
|
||||
"topics": q.Topics,
|
||||
}
|
||||
if q.FromBlock == nil {
|
||||
@@ -299,21 +300,21 @@ func toFilterArg(q ethereum.FilterQuery) interface{} {
|
||||
|
||||
// PendingBalanceAt returns the wei balance of the given account in the pending state.
|
||||
func (ec *Client) PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) {
|
||||
var result rpc.HexNumber
|
||||
var result hexutil.Big
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, "pending")
|
||||
return (*big.Int)(&result), err
|
||||
}
|
||||
|
||||
// PendingStorageAt returns the value of key in the contract storage of the given account in the pending state.
|
||||
func (ec *Client) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) {
|
||||
var result rpc.HexBytes
|
||||
var result hexutil.Bytes
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, "pending")
|
||||
return result, err
|
||||
}
|
||||
|
||||
// PendingCodeAt returns the contract code of the given account in the pending state.
|
||||
func (ec *Client) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
|
||||
var result rpc.HexBytes
|
||||
var result hexutil.Bytes
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getCode", account, "pending")
|
||||
return result, err
|
||||
}
|
||||
@@ -321,16 +322,16 @@ func (ec *Client) PendingCodeAt(ctx context.Context, account common.Address) ([]
|
||||
// PendingNonceAt returns the account nonce of the given account in the pending state.
|
||||
// This is the nonce that should be used for the next transaction.
|
||||
func (ec *Client) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
|
||||
var result rpc.HexNumber
|
||||
var result hexutil.Uint64
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, "pending")
|
||||
return result.Uint64(), err
|
||||
return uint64(result), err
|
||||
}
|
||||
|
||||
// PendingTransactionCount returns the total number of transactions in the pending state.
|
||||
func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) {
|
||||
var num rpc.HexNumber
|
||||
var num hexutil.Uint
|
||||
err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByNumber", "pending")
|
||||
return num.Uint(), err
|
||||
return uint(num), err
|
||||
}
|
||||
|
||||
// TODO: SubscribePendingTransactions (needs server side)
|
||||
@@ -344,29 +345,29 @@ func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) {
|
||||
// case the code is taken from the latest known block. Note that state from very old
|
||||
// blocks might not be available.
|
||||
func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
||||
var hex string
|
||||
var hex hexutil.Bytes
|
||||
err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), toBlockNumArg(blockNumber))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return common.FromHex(hex), nil
|
||||
return hex, nil
|
||||
}
|
||||
|
||||
// PendingCallContract executes a message call transaction using the EVM.
|
||||
// The state seen by the contract call is the pending state.
|
||||
func (ec *Client) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) {
|
||||
var hex string
|
||||
var hex hexutil.Bytes
|
||||
err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), "pending")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return common.FromHex(hex), nil
|
||||
return hex, nil
|
||||
}
|
||||
|
||||
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
|
||||
// execution of a transaction.
|
||||
func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
||||
var hex rpc.HexNumber
|
||||
var hex hexutil.Big
|
||||
if err := ec.c.CallContext(ctx, &hex, "eth_gasPrice"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -378,7 +379,7 @@ func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
||||
// the true gas limit requirement as other transactions may be added or removed by miners,
|
||||
// but it should provide a basis for setting a reasonable default.
|
||||
func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (*big.Int, error) {
|
||||
var hex rpc.HexNumber
|
||||
var hex hexutil.Big
|
||||
err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -404,16 +405,16 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
|
||||
"to": msg.To,
|
||||
}
|
||||
if len(msg.Data) > 0 {
|
||||
arg["data"] = fmt.Sprintf("%#x", msg.Data)
|
||||
arg["data"] = hexutil.Bytes(msg.Data)
|
||||
}
|
||||
if msg.Value != nil {
|
||||
arg["value"] = fmt.Sprintf("%#x", msg.Value)
|
||||
arg["value"] = (*hexutil.Big)(msg.Value)
|
||||
}
|
||||
if msg.Gas != nil {
|
||||
arg["gas"] = fmt.Sprintf("%#x", msg.Gas)
|
||||
arg["gas"] = (*hexutil.Big)(msg.Gas)
|
||||
}
|
||||
if msg.GasPrice != nil {
|
||||
arg["gasPrice"] = fmt.Sprintf("%#x", msg.GasPrice)
|
||||
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
|
||||
}
|
||||
return arg
|
||||
}
|
||||
|
||||
452
ethstats/ethstats.go
Normal file
452
ethstats/ethstats.go
Normal file
@@ -0,0 +1,452 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package ethstats implements the network stats reporting service.
|
||||
package ethstats
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
// Service implements an Ethereum netstats reporting daemon that pushes local
|
||||
// chain statistics up to a monitoring server.
|
||||
type Service struct {
|
||||
stack *node.Node // Temporary workaround, remove when API finalized
|
||||
|
||||
server *p2p.Server // Peer-to-peer server to retrieve networking infos
|
||||
eth *eth.Ethereum // Full Ethereum service if monitoring a full node
|
||||
les *les.LightEthereum // Light Ethereum service if monitoring a light node
|
||||
|
||||
node string // Name of the node to display on the monitoring page
|
||||
pass string // Password to authorize access to the monitoring page
|
||||
host string // Remote address of the monitoring service
|
||||
}
|
||||
|
||||
// New returns a monitoring service ready for stats reporting.
|
||||
func New(url string, ethServ *eth.Ethereum, lesServ *les.LightEthereum) (*Service, error) {
|
||||
// Parse the netstats connection url
|
||||
re := regexp.MustCompile("([^:@]*)(:([^@]*))?@(.+)")
|
||||
parts := re.FindStringSubmatch(url)
|
||||
if len(parts) != 5 {
|
||||
return nil, fmt.Errorf("invalid netstats url: \"%s\", should be nodename:secret@host:port", url)
|
||||
}
|
||||
// Assemble and return the stats service
|
||||
return &Service{
|
||||
eth: ethServ,
|
||||
les: lesServ,
|
||||
node: parts[1],
|
||||
pass: parts[3],
|
||||
host: parts[4],
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Protocols implements node.Service, returning the P2P network protocols used
|
||||
// by the stats service (nil as it doesn't use the devp2p overlay network).
|
||||
func (s *Service) Protocols() []p2p.Protocol { return nil }
|
||||
|
||||
// APIs implements node.Service, returning the RPC API endpoints provided by the
|
||||
// stats service (nil as it doesn't provide any user callable APIs).
|
||||
func (s *Service) APIs() []rpc.API { return nil }
|
||||
|
||||
// Start implements node.Service, starting up the monitoring and reporting daemon.
|
||||
func (s *Service) Start(server *p2p.Server) error {
|
||||
s.server = server
|
||||
go s.loop()
|
||||
|
||||
glog.V(logger.Info).Infoln("Stats daemon started")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop implements node.Service, terminating the monitoring and reporting daemon.
|
||||
func (s *Service) Stop() error {
|
||||
glog.V(logger.Info).Infoln("Stats daemon stopped")
|
||||
return nil
|
||||
}
|
||||
|
||||
// loop keeps trying to connect to the netstats server, reporting chain events
|
||||
// until termination.
|
||||
func (s *Service) loop() {
|
||||
// Subscribe tso chain events to execute updates on
|
||||
var emux *event.TypeMux
|
||||
if s.eth != nil {
|
||||
emux = s.eth.EventMux()
|
||||
} else {
|
||||
emux = s.les.EventMux()
|
||||
}
|
||||
headSub := emux.Subscribe(core.ChainHeadEvent{})
|
||||
defer headSub.Unsubscribe()
|
||||
|
||||
txSub := emux.Subscribe(core.TxPreEvent{})
|
||||
defer txSub.Unsubscribe()
|
||||
|
||||
// Loop reporting until termination
|
||||
for {
|
||||
// Establish a websocket connection to the server and authenticate the node
|
||||
conn, err := websocket.Dial(fmt.Sprintf("wss://%s/api", s.host), "", "http://localhost/")
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infof("Stats server unreachable: %v", err)
|
||||
time.Sleep(10 * time.Second)
|
||||
continue
|
||||
}
|
||||
in := json.NewDecoder(conn)
|
||||
out := json.NewEncoder(conn)
|
||||
|
||||
if err = s.login(in, out); err != nil {
|
||||
glog.V(logger.Warn).Infof("Stats login failed: %v", err)
|
||||
conn.Close()
|
||||
time.Sleep(10 * time.Second)
|
||||
continue
|
||||
}
|
||||
if err = s.report(in, out); err != nil {
|
||||
glog.V(logger.Warn).Infof("Initial stats report failed: %v", err)
|
||||
conn.Close()
|
||||
continue
|
||||
}
|
||||
// Keep sending status updates until the connection breaks
|
||||
fullReport := time.NewTicker(15 * time.Second)
|
||||
|
||||
for err == nil {
|
||||
select {
|
||||
case <-fullReport.C:
|
||||
if err = s.report(in, out); err != nil {
|
||||
glog.V(logger.Warn).Infof("Full stats report failed: %v", err)
|
||||
}
|
||||
case <-headSub.Chan():
|
||||
// Exhaust events to avoid reporting too frequently
|
||||
for exhausted := false; !exhausted; {
|
||||
select {
|
||||
case <-headSub.Chan():
|
||||
default:
|
||||
exhausted = true
|
||||
}
|
||||
}
|
||||
if err = s.reportBlock(out); err != nil {
|
||||
glog.V(logger.Warn).Infof("Block stats report failed: %v", err)
|
||||
}
|
||||
case <-txSub.Chan():
|
||||
// Exhaust events to avoid reporting too frequently
|
||||
for exhausted := false; !exhausted; {
|
||||
select {
|
||||
case <-headSub.Chan():
|
||||
default:
|
||||
exhausted = true
|
||||
}
|
||||
}
|
||||
if err = s.reportPending(out); err != nil {
|
||||
glog.V(logger.Warn).Infof("Transaction stats report failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Make sure the connection is closed
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// nodeInfo is the collection of metainformation about a node that is displayed
|
||||
// on the monitoring page.
|
||||
type nodeInfo struct {
|
||||
Name string `json:"name"`
|
||||
Node string `json:"node"`
|
||||
Port int `json:"port"`
|
||||
Network string `json:"net"`
|
||||
Protocol string `json:"protocol"`
|
||||
API string `json:"api"`
|
||||
Os string `json:"os"`
|
||||
OsVer string `json:"os_v"`
|
||||
Client string `json:"client"`
|
||||
}
|
||||
|
||||
// authMsg is the authentication infos needed to login to a monitoring server.
|
||||
type authMsg struct {
|
||||
Id string `json:"id"`
|
||||
Info nodeInfo `json:"info"`
|
||||
Secret string `json:"secret"`
|
||||
}
|
||||
|
||||
// login tries to authorize the client at the remote server.
|
||||
func (s *Service) login(in *json.Decoder, out *json.Encoder) error {
|
||||
// Construct and send the login authentication
|
||||
infos := s.server.NodeInfo()
|
||||
|
||||
var network, protocol string
|
||||
if info := infos.Protocols["eth"]; info != nil {
|
||||
network = strconv.Itoa(info.(*eth.EthNodeInfo).Network)
|
||||
protocol = fmt.Sprintf("eth/%d", eth.ProtocolVersions[0])
|
||||
} else {
|
||||
network = strconv.Itoa(infos.Protocols["les"].(*eth.EthNodeInfo).Network)
|
||||
protocol = fmt.Sprintf("les/%d", les.ProtocolVersions[0])
|
||||
}
|
||||
auth := &authMsg{
|
||||
Id: s.node,
|
||||
Info: nodeInfo{
|
||||
Name: s.node,
|
||||
Node: infos.Name,
|
||||
Port: infos.Ports.Listener,
|
||||
Network: network,
|
||||
Protocol: protocol,
|
||||
API: "No",
|
||||
Os: runtime.GOOS,
|
||||
OsVer: runtime.GOARCH,
|
||||
Client: "0.1.1",
|
||||
},
|
||||
Secret: s.pass,
|
||||
}
|
||||
login := map[string][]interface{}{
|
||||
"emit": []interface{}{"hello", auth},
|
||||
}
|
||||
if err := out.Encode(login); err != nil {
|
||||
return err
|
||||
}
|
||||
// Retrieve the remote ack or connection termination
|
||||
var ack map[string][]string
|
||||
if err := in.Decode(&ack); err != nil || len(ack["emit"]) != 1 || ack["emit"][0] != "ready" {
|
||||
return errors.New("unauthorized")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// report collects all possible data to report and send it to the stats server.
|
||||
// This should only be used on reconnects or rarely to avoid overloading the
|
||||
// server. Use the individual methods for reporting subscribed events.
|
||||
func (s *Service) report(in *json.Decoder, out *json.Encoder) error {
|
||||
if err := s.reportLatency(in, out); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.reportBlock(out); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.reportPending(out); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.reportStats(out); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// reportLatency sends a ping request to the server, measures the RTT time and
|
||||
// finally sends a latency update.
|
||||
func (s *Service) reportLatency(in *json.Decoder, out *json.Encoder) error {
|
||||
// Send the current time to the ethstats server
|
||||
start := time.Now()
|
||||
|
||||
ping := map[string][]interface{}{
|
||||
"emit": []interface{}{"node-ping", map[string]string{
|
||||
"id": s.node,
|
||||
"clientTime": start.String(),
|
||||
}},
|
||||
}
|
||||
if err := out.Encode(ping); err != nil {
|
||||
return err
|
||||
}
|
||||
// Wait for the pong request to arrive back
|
||||
var pong map[string][]interface{}
|
||||
if err := in.Decode(&pong); err != nil || len(pong["emit"]) != 2 || pong["emit"][0].(string) != "node-pong" {
|
||||
return errors.New("unexpected ping reply")
|
||||
}
|
||||
// Send back the measured latency
|
||||
latency := map[string][]interface{}{
|
||||
"emit": []interface{}{"latency", map[string]string{
|
||||
"id": s.node,
|
||||
"latency": strconv.Itoa(int((time.Since(start) / time.Duration(2)).Nanoseconds() / 1000000)),
|
||||
}},
|
||||
}
|
||||
if err := out.Encode(latency); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// blockStats is the information to report about individual blocks.
|
||||
type blockStats struct {
|
||||
Number *big.Int `json:"number"`
|
||||
Hash common.Hash `json:"hash"`
|
||||
Diff string `json:"difficulty"`
|
||||
TotalDiff string `json:"totalDifficulty"`
|
||||
Txs txStats `json:"transactions"`
|
||||
Uncles uncleStats `json:"uncles"`
|
||||
}
|
||||
|
||||
// txStats is a custom wrapper around a transaction array to force serializing
|
||||
// empty arrays instead of returning null for them.
|
||||
type txStats []*types.Transaction
|
||||
|
||||
func (s txStats) MarshalJSON() ([]byte, error) {
|
||||
if txs := ([]*types.Transaction)(s); len(txs) > 0 {
|
||||
return json.Marshal(txs)
|
||||
}
|
||||
return []byte("[]"), nil
|
||||
}
|
||||
|
||||
// uncleStats is a custom wrapper around an uncle array to force serializing
|
||||
// empty arrays instead of returning null for them.
|
||||
type uncleStats []*types.Header
|
||||
|
||||
func (s uncleStats) MarshalJSON() ([]byte, error) {
|
||||
if uncles := ([]*types.Header)(s); len(uncles) > 0 {
|
||||
return json.Marshal(uncles)
|
||||
}
|
||||
return []byte("[]"), nil
|
||||
}
|
||||
|
||||
// reportBlock retrieves the current chain head and repors it to the stats server.
|
||||
func (s *Service) reportBlock(out *json.Encoder) error {
|
||||
// Gather the head block infos from the local blockchain
|
||||
var (
|
||||
head *types.Header
|
||||
td *big.Int
|
||||
txs []*types.Transaction
|
||||
uncles []*types.Header
|
||||
)
|
||||
if s.eth != nil {
|
||||
// Full nodes have all needed information available
|
||||
block := s.eth.BlockChain().CurrentBlock()
|
||||
|
||||
head = s.eth.BlockChain().CurrentHeader()
|
||||
td = s.eth.BlockChain().GetTd(head.Hash(), head.Number.Uint64())
|
||||
|
||||
txs = block.Transactions()
|
||||
uncles = block.Uncles()
|
||||
} else {
|
||||
// Light nodes would need on-demand lookups for transactions/uncles, skip
|
||||
head = s.les.BlockChain().CurrentHeader()
|
||||
td = s.les.BlockChain().GetTd(head.Hash(), head.Number.Uint64())
|
||||
}
|
||||
// Assemble the block stats report and send it to the server
|
||||
stats := map[string]interface{}{
|
||||
"id": s.node,
|
||||
"block": &blockStats{
|
||||
Number: head.Number,
|
||||
Hash: head.Hash(),
|
||||
Diff: head.Difficulty.String(),
|
||||
TotalDiff: td.String(),
|
||||
Txs: txs,
|
||||
Uncles: uncles,
|
||||
},
|
||||
}
|
||||
report := map[string][]interface{}{
|
||||
"emit": []interface{}{"block", stats},
|
||||
}
|
||||
if err := out.Encode(report); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// pendStats is the information to report about pending transactions.
|
||||
type pendStats struct {
|
||||
Pending int `json:"pending"`
|
||||
}
|
||||
|
||||
// reportPending retrieves the current number of pending transactions and reports
|
||||
// it to the stats server.
|
||||
func (s *Service) reportPending(out *json.Encoder) error {
|
||||
// Retrieve the pending count from the local blockchain
|
||||
var pending int
|
||||
if s.eth != nil {
|
||||
pending, _ = s.eth.TxPool().Stats()
|
||||
} else {
|
||||
pending = s.les.TxPool().Stats()
|
||||
}
|
||||
// Assemble the transaction stats and send it to the server
|
||||
stats := map[string]interface{}{
|
||||
"id": s.node,
|
||||
"stats": &pendStats{
|
||||
Pending: pending,
|
||||
},
|
||||
}
|
||||
report := map[string][]interface{}{
|
||||
"emit": []interface{}{"pending", stats},
|
||||
}
|
||||
if err := out.Encode(report); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// blockStats is the information to report about the local node.
|
||||
type nodeStats struct {
|
||||
Active bool `json:"active"`
|
||||
Syncing bool `json:"syncing"`
|
||||
Mining bool `json:"mining"`
|
||||
Hashrate int `json:"hashrate"`
|
||||
Peers int `json:"peers"`
|
||||
GasPrice int `json:"gasPrice"`
|
||||
Uptime int `json:"uptime"`
|
||||
}
|
||||
|
||||
// reportPending retrieves various stats about the node at the networking and
|
||||
// mining layer and reports it to the stats server.
|
||||
func (s *Service) reportStats(out *json.Encoder) error {
|
||||
// Gather the syncing and mining infos from the local miner instance
|
||||
var (
|
||||
mining bool
|
||||
hashrate int
|
||||
syncing bool
|
||||
gasprice int
|
||||
)
|
||||
if s.eth != nil {
|
||||
mining = s.eth.Miner().Mining()
|
||||
hashrate = int(s.eth.Miner().HashRate())
|
||||
|
||||
sync := s.eth.Downloader().Progress()
|
||||
syncing = s.eth.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock
|
||||
|
||||
gasprice = int(s.eth.Miner().GasPrice().Uint64())
|
||||
} else {
|
||||
sync := s.les.Downloader().Progress()
|
||||
syncing = s.les.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock
|
||||
}
|
||||
stats := map[string]interface{}{
|
||||
"id": s.node,
|
||||
"stats": &nodeStats{
|
||||
Active: true,
|
||||
Mining: mining,
|
||||
Hashrate: hashrate,
|
||||
Peers: s.server.PeerCount(),
|
||||
GasPrice: gasprice,
|
||||
Syncing: syncing,
|
||||
Uptime: 100,
|
||||
},
|
||||
}
|
||||
report := map[string][]interface{}{
|
||||
"emit": []interface{}{"stats", stats},
|
||||
}
|
||||
if err := out.Encode(report); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -88,11 +89,16 @@ func LocalEnv() Environment {
|
||||
env.Branch = b
|
||||
}
|
||||
}
|
||||
// Note that we don't get the current git tag. It would slow down
|
||||
// builds and isn't used by anything.
|
||||
if env.Tag == "" {
|
||||
env.Tag = firstLine(RunGit("tag", "-l", "--points-at", "HEAD"))
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
func firstLine(s string) string {
|
||||
return strings.Split(s, "\n")[0]
|
||||
}
|
||||
|
||||
func applyEnvFlags(env Environment) Environment {
|
||||
if !flag.Parsed() {
|
||||
panic("you need to call flag.Parse before Env or LocalEnv")
|
||||
|
||||
@@ -76,6 +76,8 @@ func VERSION() string {
|
||||
return string(bytes.TrimSpace(version))
|
||||
}
|
||||
|
||||
var warnedAboutGit bool
|
||||
|
||||
// RunGit runs a git subcommand and returns its output.
|
||||
// The command must complete successfully.
|
||||
func RunGit(args ...string) string {
|
||||
@@ -83,7 +85,10 @@ func RunGit(args ...string) string {
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout, cmd.Stderr = &stdout, &stderr
|
||||
if err := cmd.Run(); err == exec.ErrNotFound {
|
||||
log.Println("no git in PATH")
|
||||
if !warnedAboutGit {
|
||||
log.Println("Warning: can't find 'git' in PATH")
|
||||
warnedAboutGit = true
|
||||
}
|
||||
return ""
|
||||
} else if err != nil {
|
||||
log.Fatal(strings.Join(cmd.Args, " "), ": ", err, "\n", stderr.String())
|
||||
|
||||
@@ -37,12 +37,6 @@ web3._extend({
|
||||
property: 'bzz',
|
||||
methods:
|
||||
[
|
||||
new web3._extend.Method({
|
||||
name: 'blockNetworkRead',
|
||||
call: 'bzz_blockNetworkRead',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'syncEnabled',
|
||||
call: 'bzz_syncEnabled',
|
||||
@@ -68,17 +62,11 @@ web3._extend({
|
||||
inputFormatter: [null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'retrieve',
|
||||
call: 'bzz_retrieve',
|
||||
name: 'resolve',
|
||||
call: 'bzz_resolve',
|
||||
params: 1,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'store',
|
||||
call: 'bzz_store',
|
||||
params: 2,
|
||||
inputFormatter: [null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'get',
|
||||
call: 'bzz_get',
|
||||
@@ -226,41 +214,6 @@ web3._extend({
|
||||
new web3._extend.Method({
|
||||
name: 'stopWS',
|
||||
call: 'admin_stopWS'
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setGlobalRegistrar',
|
||||
call: 'admin_setGlobalRegistrar',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setHashReg',
|
||||
call: 'admin_setHashReg',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'setUrlHint',
|
||||
call: 'admin_setUrlHint',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'saveInfo',
|
||||
call: 'admin_saveInfo',
|
||||
params: 2
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'register',
|
||||
call: 'admin_register',
|
||||
params: 3
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'registerUrl',
|
||||
call: 'admin_registerUrl',
|
||||
params: 3
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'httpGet',
|
||||
call: 'admin_httpGet',
|
||||
params: 2
|
||||
})
|
||||
],
|
||||
properties:
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/compiler"
|
||||
"github.com/ethereum/go-ethereum/common/httpclient"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
@@ -62,7 +61,6 @@ type LightEthereum struct {
|
||||
|
||||
eventMux *event.TypeMux
|
||||
pow *ethash.Ethash
|
||||
httpclient *httpclient.HTTPClient
|
||||
accountManager *accounts.Manager
|
||||
solcPath string
|
||||
solc *compiler.Solidity
|
||||
@@ -96,7 +94,6 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
|
||||
accountManager: ctx.AccountManager,
|
||||
pow: pow,
|
||||
shutdownChan: make(chan bool),
|
||||
httpclient: httpclient.New(config.DocRoot),
|
||||
netVersionId: config.NetworkId,
|
||||
NatSpec: config.NatSpec,
|
||||
PowTest: config.PowTest,
|
||||
@@ -183,6 +180,7 @@ func (s *LightEthereum) BlockChain() *light.LightChain { return s.blockchai
|
||||
func (s *LightEthereum) TxPool() *light.TxPool { return s.txPool }
|
||||
func (s *LightEthereum) LesVersion() int { return int(s.protocolManager.SubProtocols[0].Version) }
|
||||
func (s *LightEthereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader }
|
||||
func (s *LightEthereum) EventMux() *event.TypeMux { return s.eventMux }
|
||||
|
||||
// Protocols implements node.Service, returning all the currently configured
|
||||
// network protocols to start.
|
||||
|
||||
@@ -395,7 +395,7 @@ func makeCht(db ethdb.Database) bool {
|
||||
} else {
|
||||
lastChtNum++
|
||||
|
||||
glog.V(logger.Info).Infoln("CHT %d %064x\n", lastChtNum, root)
|
||||
glog.V(logger.Detail).Infof("cht: %d %064x", lastChtNum, root)
|
||||
|
||||
storeChtRoot(db, lastChtNum, root)
|
||||
var data [8]byte
|
||||
|
||||
@@ -26,9 +26,6 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// StartingNonce determines the default nonce when new accounts are being created.
|
||||
var StartingNonce uint64
|
||||
|
||||
// LightState is a memory representation of a state.
|
||||
// This version is ODR capable, caching only the already accessed part of the
|
||||
// state, retrieving unknown parts on-demand from the ODR backend. Changes are
|
||||
@@ -238,7 +235,6 @@ func (self *LightState) newStateObject(addr common.Address) *StateObject {
|
||||
}
|
||||
|
||||
stateObject := NewStateObject(addr, self.odr)
|
||||
stateObject.SetNonce(StartingNonce)
|
||||
self.stateObjects[addr.Str()] = stateObject
|
||||
|
||||
return stateObject
|
||||
|
||||
@@ -201,14 +201,19 @@ func (self *StateObject) Copy() *StateObject {
|
||||
// Attribute accessors
|
||||
//
|
||||
|
||||
// empty returns whether the account is considered empty.
|
||||
func (self *StateObject) empty() bool {
|
||||
return self.nonce == 0 && self.balance.BitLen() == 0 && bytes.Equal(self.codeHash, emptyCodeHash)
|
||||
}
|
||||
|
||||
// Balance returns the account balance
|
||||
func (self *StateObject) Balance() *big.Int {
|
||||
return self.balance
|
||||
}
|
||||
|
||||
// Address returns the address of the contract/account
|
||||
func (c *StateObject) Address() common.Address {
|
||||
return c.address
|
||||
func (self *StateObject) Address() common.Address {
|
||||
return self.address
|
||||
}
|
||||
|
||||
// Code returns the contract code
|
||||
|
||||
@@ -263,6 +263,13 @@ func (s *VMState) Exist(addr common.Address) bool {
|
||||
return res
|
||||
}
|
||||
|
||||
// Empty returns true if the account at the given address is considered empty
|
||||
func (s *VMState) Empty(addr common.Address) bool {
|
||||
so, err := s.state.GetStateObject(s.ctx, addr)
|
||||
s.errHandler(err)
|
||||
return so == nil || so.empty()
|
||||
}
|
||||
|
||||
// HasSuicided returns true if the given account has been marked for deletion
|
||||
// or false if the account does not exist
|
||||
func (s *VMState) HasSuicided(addr common.Address) bool {
|
||||
|
||||
@@ -50,8 +50,6 @@ type Miner struct {
|
||||
|
||||
worker *worker
|
||||
|
||||
MinAcceptedGasPrice *big.Int
|
||||
|
||||
threads int
|
||||
coinbase common.Address
|
||||
mining int32
|
||||
@@ -107,12 +105,15 @@ out:
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Miner) GasPrice() *big.Int {
|
||||
return new(big.Int).Set(m.worker.gasPrice)
|
||||
}
|
||||
|
||||
func (m *Miner) SetGasPrice(price *big.Int) {
|
||||
// FIXME block tests set a nil gas price. Quick dirty fix
|
||||
if price == nil {
|
||||
return
|
||||
}
|
||||
|
||||
m.worker.setGasPrice(price)
|
||||
}
|
||||
|
||||
|
||||
@@ -262,6 +262,7 @@ func newLocalMinedBlock(blockNumber uint64, prevMinedBlocks *uint64RingBuffer) (
|
||||
|
||||
func (self *worker) wait() {
|
||||
for {
|
||||
mustCommitNewWork := true
|
||||
for result := range self.recv {
|
||||
atomic.AddInt32(&self.atWork, -1)
|
||||
|
||||
@@ -315,6 +316,8 @@ func (self *worker) wait() {
|
||||
core.WriteReceipts(self.chainDb, work.receipts)
|
||||
// Write map map bloom filters
|
||||
core.WriteMipmapBloom(self.chainDb, block.NumberU64(), work.receipts)
|
||||
// implicit by posting ChainHeadEvent
|
||||
mustCommitNewWork = false
|
||||
}
|
||||
|
||||
// broadcast before waiting for validation
|
||||
@@ -343,7 +346,9 @@ func (self *worker) wait() {
|
||||
}
|
||||
glog.V(logger.Info).Infof("🔨 Mined %sblock (#%v / %x). %s", stale, block.Number(), block.Hash().Bytes()[:4], confirm)
|
||||
|
||||
self.commitNewWork()
|
||||
if mustCommitNewWork {
|
||||
self.commitNewWork()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -451,6 +456,7 @@ func (self *worker) commitNewWork() {
|
||||
|
||||
tstart := time.Now()
|
||||
parent := self.chain.CurrentBlock()
|
||||
|
||||
tstamp := tstart.Unix()
|
||||
if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 {
|
||||
tstamp = parent.Time().Int64() + 1
|
||||
@@ -618,7 +624,16 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
|
||||
txs.Shift()
|
||||
}
|
||||
}
|
||||
|
||||
if len(coalescedLogs) > 0 || env.tcount > 0 {
|
||||
// make a copy, the state caches the logs and these logs get "upgraded" from pending to mined
|
||||
// logs by filling in the block hash when the block was mined by the local miner. This can
|
||||
// cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed.
|
||||
cpy := make(vm.Logs, len(coalescedLogs))
|
||||
for i, l := range coalescedLogs {
|
||||
cpy[i] = new(vm.Log)
|
||||
*cpy[i] = *l
|
||||
}
|
||||
go func(logs vm.Logs, tcount int) {
|
||||
if len(logs) > 0 {
|
||||
mux.Post(core.PendingLogsEvent{Logs: logs})
|
||||
@@ -626,7 +641,7 @@ func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsB
|
||||
if tcount > 0 {
|
||||
mux.Post(core.PendingStateEvent{})
|
||||
}
|
||||
}(coalescedLogs, env.tcount)
|
||||
}(cpy, env.tcount)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,11 +25,10 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/ethstats"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
"github.com/ethereum/go-ethereum/light"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
@@ -63,14 +62,16 @@ type NodeConfig struct {
|
||||
// empty genesis state is equivalent to using the mainnet's state.
|
||||
EthereumGenesis string
|
||||
|
||||
// EthereumTestnetNonces specifies whether to use account nonces from the testnet
|
||||
// range (2^20) or from the mainnet one (0).
|
||||
EthereumTestnetNonces bool
|
||||
|
||||
// EthereumDatabaseCache is the system memory in MB to allocate for database caching.
|
||||
// A minimum of 16MB is always reserved.
|
||||
EthereumDatabaseCache int
|
||||
|
||||
// EthereumNetStats is a netstats connection string to use to report various
|
||||
// chain, transaction and node stats to a monitoring server.
|
||||
//
|
||||
// It has the form "nodename:secret@host:port"
|
||||
EthereumNetStats string
|
||||
|
||||
// WhisperEnabled specifies whether the node should run the Whisper protocol.
|
||||
WhisperEnabled bool
|
||||
}
|
||||
@@ -112,6 +113,7 @@ func NewNode(datadir string, config *NodeConfig) (*Node, error) {
|
||||
// Create the empty networking stack
|
||||
nodeConf := &node.Config{
|
||||
Name: clientIdentifier,
|
||||
Version: params.Version,
|
||||
DataDir: datadir,
|
||||
KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores!
|
||||
NoDiscovery: true,
|
||||
@@ -130,6 +132,7 @@ func NewNode(datadir string, config *NodeConfig) (*Node, error) {
|
||||
if config.EthereumEnabled {
|
||||
ethConf := ð.Config{
|
||||
ChainConfig: ¶ms.ChainConfig{
|
||||
ChainId: big.NewInt(config.EthereumChainConfig.ChainID),
|
||||
HomesteadBlock: big.NewInt(config.EthereumChainConfig.HomesteadBlock),
|
||||
DAOForkBlock: big.NewInt(config.EthereumChainConfig.DAOForkBlock),
|
||||
DAOForkSupport: config.EthereumChainConfig.DAOForkSupport,
|
||||
@@ -150,15 +153,22 @@ func NewNode(datadir string, config *NodeConfig) (*Node, error) {
|
||||
GpobaseStepUp: 100,
|
||||
GpobaseCorrectionFactor: 110,
|
||||
}
|
||||
if config.EthereumTestnetNonces {
|
||||
state.StartingNonce = 1048576 // (2**20)
|
||||
light.StartingNonce = 1048576 // (2**20)
|
||||
}
|
||||
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||
return les.New(ctx, ethConf)
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("ethereum init: %v", err)
|
||||
}
|
||||
// If netstats reporting is requested, do it
|
||||
if config.EthereumNetStats != "" {
|
||||
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||
var lesServ *les.LightEthereum
|
||||
ctx.Service(&lesServ)
|
||||
|
||||
return ethstats.New(config.EthereumNetStats, nil, lesServ)
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("netstats init: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Register the Whisper protocol if requested
|
||||
if config.WhisperEnabled {
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
// MainnetChainConfig returns the chain configurations for the main Ethereum network.
|
||||
func MainnetChainConfig() *ChainConfig {
|
||||
return &ChainConfig{
|
||||
ChainID: params.MainNetChainID.Int64(),
|
||||
HomesteadBlock: params.MainNetHomesteadBlock.Int64(),
|
||||
DAOForkBlock: params.MainNetDAOForkBlock.Int64(),
|
||||
DAOForkSupport: true,
|
||||
@@ -46,9 +47,10 @@ func MainnetGenesis() string {
|
||||
// TestnetChainConfig returns the chain configurations for the Ethereum test network.
|
||||
func TestnetChainConfig() *ChainConfig {
|
||||
return &ChainConfig{
|
||||
ChainID: params.TestNetChainID.Int64(),
|
||||
HomesteadBlock: params.TestNetHomesteadBlock.Int64(),
|
||||
DAOForkBlock: 0,
|
||||
DAOForkSupport: false,
|
||||
DAOForkSupport: true,
|
||||
EIP150Block: params.TestNetHomesteadGasRepriceBlock.Int64(),
|
||||
EIP150Hash: Hash{params.TestNetHomesteadGasRepriceHash},
|
||||
EIP155Block: params.TestNetSpuriousDragon.Int64(),
|
||||
@@ -58,11 +60,12 @@ func TestnetChainConfig() *ChainConfig {
|
||||
|
||||
// TestnetGenesis returns the JSON spec to use for the Ethereum test network.
|
||||
func TestnetGenesis() string {
|
||||
return core.TestNetGenesisBlock()
|
||||
return core.DefaultTestnetGenesisBlock()
|
||||
}
|
||||
|
||||
// ChainConfig is the core config which determines the blockchain settings.
|
||||
type ChainConfig struct {
|
||||
ChainID int64 // Chain ID for replay protection
|
||||
HomesteadBlock int64 // Homestead switch block
|
||||
DAOForkBlock int64 // TheDAO hard-fork switch block
|
||||
DAOForkSupport bool // Whether the nodes supports or opposes the DAO hard-fork
|
||||
|
||||
@@ -34,6 +34,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -103,6 +104,10 @@ type Config struct {
|
||||
// Listener address for the V5 discovery protocol UDP traffic.
|
||||
DiscoveryV5Addr string
|
||||
|
||||
// Restrict communication to white listed IP networks.
|
||||
// The whitelist only applies when non-nil.
|
||||
NetRestrict *netutil.Netlist
|
||||
|
||||
// BootstrapNodes used to establish connectivity with the rest of the network.
|
||||
BootstrapNodes []*discover.Node
|
||||
|
||||
@@ -365,12 +370,11 @@ func (c *Config) TrusterNodes() []*discover.Node {
|
||||
|
||||
// parsePersistentNodes parses a list of discovery node URLs loaded from a .json
|
||||
// file from within the data directory.
|
||||
func (c *Config) parsePersistentNodes(file string) []*discover.Node {
|
||||
func (c *Config) parsePersistentNodes(path string) []*discover.Node {
|
||||
// Short circuit if no node config is present
|
||||
if c.DataDir == "" {
|
||||
return nil
|
||||
}
|
||||
path := filepath.Join(c.DataDir, file)
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -165,6 +165,7 @@ func (n *Node) Start() error {
|
||||
TrustedNodes: n.config.TrusterNodes(),
|
||||
NodeDatabase: n.config.NodeDB(),
|
||||
ListenAddr: n.config.ListenAddr,
|
||||
NetRestrict: n.config.NetRestrict,
|
||||
NAT: n.config.NAT,
|
||||
Dialer: n.config.Dialer,
|
||||
NoDial: n.config.NoDial,
|
||||
|
||||
45
p2p/dial.go
45
p2p/dial.go
@@ -19,6 +19,7 @@ package p2p
|
||||
import (
|
||||
"container/heap"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
@@ -26,6 +27,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -48,6 +50,7 @@ const (
|
||||
type dialstate struct {
|
||||
maxDynDials int
|
||||
ntab discoverTable
|
||||
netrestrict *netutil.Netlist
|
||||
|
||||
lookupRunning bool
|
||||
dialing map[discover.NodeID]connFlag
|
||||
@@ -100,10 +103,11 @@ type waitExpireTask struct {
|
||||
time.Duration
|
||||
}
|
||||
|
||||
func newDialState(static []*discover.Node, ntab discoverTable, maxdyn int) *dialstate {
|
||||
func newDialState(static []*discover.Node, ntab discoverTable, maxdyn int, netrestrict *netutil.Netlist) *dialstate {
|
||||
s := &dialstate{
|
||||
maxDynDials: maxdyn,
|
||||
ntab: ntab,
|
||||
netrestrict: netrestrict,
|
||||
static: make(map[discover.NodeID]*dialTask),
|
||||
dialing: make(map[discover.NodeID]connFlag),
|
||||
randomNodes: make([]*discover.Node, maxdyn/2),
|
||||
@@ -128,12 +132,9 @@ func (s *dialstate) removeStatic(n *discover.Node) {
|
||||
|
||||
func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now time.Time) []task {
|
||||
var newtasks []task
|
||||
isDialing := func(id discover.NodeID) bool {
|
||||
_, found := s.dialing[id]
|
||||
return found || peers[id] != nil || s.hist.contains(id)
|
||||
}
|
||||
addDial := func(flag connFlag, n *discover.Node) bool {
|
||||
if isDialing(n.ID) {
|
||||
if err := s.checkDial(n, peers); err != nil {
|
||||
glog.V(logger.Debug).Infof("skipping dial candidate %x@%v:%d: %v", n.ID[:8], n.IP, n.TCP, err)
|
||||
return false
|
||||
}
|
||||
s.dialing[n.ID] = flag
|
||||
@@ -159,7 +160,12 @@ func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now
|
||||
|
||||
// Create dials for static nodes if they are not connected.
|
||||
for id, t := range s.static {
|
||||
if !isDialing(id) {
|
||||
err := s.checkDial(t.dest, peers)
|
||||
switch err {
|
||||
case errNotWhitelisted, errSelf:
|
||||
glog.V(logger.Debug).Infof("removing static dial candidate %x@%v:%d: %v", t.dest.ID[:8], t.dest.IP, t.dest.TCP, err)
|
||||
delete(s.static, t.dest.ID)
|
||||
case nil:
|
||||
s.dialing[id] = t.flags
|
||||
newtasks = append(newtasks, t)
|
||||
}
|
||||
@@ -202,6 +208,31 @@ func (s *dialstate) newTasks(nRunning int, peers map[discover.NodeID]*Peer, now
|
||||
return newtasks
|
||||
}
|
||||
|
||||
var (
|
||||
errSelf = errors.New("is self")
|
||||
errAlreadyDialing = errors.New("already dialing")
|
||||
errAlreadyConnected = errors.New("already connected")
|
||||
errRecentlyDialed = errors.New("recently dialed")
|
||||
errNotWhitelisted = errors.New("not contained in netrestrict whitelist")
|
||||
)
|
||||
|
||||
func (s *dialstate) checkDial(n *discover.Node, peers map[discover.NodeID]*Peer) error {
|
||||
_, dialing := s.dialing[n.ID]
|
||||
switch {
|
||||
case dialing:
|
||||
return errAlreadyDialing
|
||||
case peers[n.ID] != nil:
|
||||
return errAlreadyConnected
|
||||
case s.ntab != nil && n.ID == s.ntab.Self().ID:
|
||||
return errSelf
|
||||
case s.netrestrict != nil && !s.netrestrict.Contains(n.IP):
|
||||
return errNotWhitelisted
|
||||
case s.hist.contains(n.ID):
|
||||
return errRecentlyDialed
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *dialstate) taskDone(t task, now time.Time) {
|
||||
switch t := t.(type) {
|
||||
case *dialTask:
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -86,7 +87,7 @@ func (t fakeTable) ReadRandomNodes(buf []*discover.Node) int { return copy(buf,
|
||||
// This test checks that dynamic dials are launched from discovery results.
|
||||
func TestDialStateDynDial(t *testing.T) {
|
||||
runDialTest(t, dialtest{
|
||||
init: newDialState(nil, fakeTable{}, 5),
|
||||
init: newDialState(nil, fakeTable{}, 5, nil),
|
||||
rounds: []round{
|
||||
// A discovery query is launched.
|
||||
{
|
||||
@@ -233,7 +234,7 @@ func TestDialStateDynDialFromTable(t *testing.T) {
|
||||
}
|
||||
|
||||
runDialTest(t, dialtest{
|
||||
init: newDialState(nil, table, 10),
|
||||
init: newDialState(nil, table, 10, nil),
|
||||
rounds: []round{
|
||||
// 5 out of 8 of the nodes returned by ReadRandomNodes are dialed.
|
||||
{
|
||||
@@ -313,6 +314,36 @@ func TestDialStateDynDialFromTable(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// This test checks that candidates that do not match the netrestrict list are not dialed.
|
||||
func TestDialStateNetRestrict(t *testing.T) {
|
||||
// This table always returns the same random nodes
|
||||
// in the order given below.
|
||||
table := fakeTable{
|
||||
{ID: uintID(1), IP: net.ParseIP("127.0.0.1")},
|
||||
{ID: uintID(2), IP: net.ParseIP("127.0.0.2")},
|
||||
{ID: uintID(3), IP: net.ParseIP("127.0.0.3")},
|
||||
{ID: uintID(4), IP: net.ParseIP("127.0.0.4")},
|
||||
{ID: uintID(5), IP: net.ParseIP("127.0.2.5")},
|
||||
{ID: uintID(6), IP: net.ParseIP("127.0.2.6")},
|
||||
{ID: uintID(7), IP: net.ParseIP("127.0.2.7")},
|
||||
{ID: uintID(8), IP: net.ParseIP("127.0.2.8")},
|
||||
}
|
||||
restrict := new(netutil.Netlist)
|
||||
restrict.Add("127.0.2.0/24")
|
||||
|
||||
runDialTest(t, dialtest{
|
||||
init: newDialState(nil, table, 10, restrict),
|
||||
rounds: []round{
|
||||
{
|
||||
new: []task{
|
||||
&dialTask{flags: dynDialedConn, dest: table[4]},
|
||||
&discoverTask{},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// This test checks that static dials are launched.
|
||||
func TestDialStateStaticDial(t *testing.T) {
|
||||
wantStatic := []*discover.Node{
|
||||
@@ -324,7 +355,7 @@ func TestDialStateStaticDial(t *testing.T) {
|
||||
}
|
||||
|
||||
runDialTest(t, dialtest{
|
||||
init: newDialState(wantStatic, fakeTable{}, 0),
|
||||
init: newDialState(wantStatic, fakeTable{}, 0, nil),
|
||||
rounds: []round{
|
||||
// Static dials are launched for the nodes that
|
||||
// aren't yet connected.
|
||||
@@ -405,7 +436,7 @@ func TestDialStateCache(t *testing.T) {
|
||||
}
|
||||
|
||||
runDialTest(t, dialtest{
|
||||
init: newDialState(wantStatic, fakeTable{}, 0),
|
||||
init: newDialState(wantStatic, fakeTable{}, 0, nil),
|
||||
rounds: []round{
|
||||
// Static dials are launched for the nodes that
|
||||
// aren't yet connected.
|
||||
@@ -467,7 +498,7 @@ func TestDialStateCache(t *testing.T) {
|
||||
func TestDialResolve(t *testing.T) {
|
||||
resolved := discover.NewNode(uintID(1), net.IP{127, 0, 55, 234}, 3333, 4444)
|
||||
table := &resolveMock{answer: resolved}
|
||||
state := newDialState(nil, table, 0)
|
||||
state := newDialState(nil, table, 0, nil)
|
||||
|
||||
// Check that the task is generated with an incomplete ID.
|
||||
dest := discover.NewNode(uintID(1), nil, 0, 0)
|
||||
|
||||
@@ -146,6 +146,7 @@ func fillBucket(tab *Table, ld int) (last *Node) {
|
||||
func nodeAtDistance(base common.Hash, ld int) (n *Node) {
|
||||
n = new(Node)
|
||||
n.sha = hashAtDistance(base, ld)
|
||||
n.IP = net.IP{10, 0, 2, byte(ld)}
|
||||
copy(n.ID[:], n.sha[:]) // ensure the node still has a unique ID
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
@@ -126,8 +127,16 @@ func makeEndpoint(addr *net.UDPAddr, tcpPort uint16) rpcEndpoint {
|
||||
return rpcEndpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort}
|
||||
}
|
||||
|
||||
func nodeFromRPC(rn rpcNode) (*Node, error) {
|
||||
// TODO: don't accept localhost, LAN addresses from internet hosts
|
||||
func (t *udp) nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*Node, error) {
|
||||
if rn.UDP <= 1024 {
|
||||
return nil, errors.New("low port")
|
||||
}
|
||||
if err := netutil.CheckRelayIP(sender.IP, rn.IP); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if t.netrestrict != nil && !t.netrestrict.Contains(rn.IP) {
|
||||
return nil, errors.New("not contained in netrestrict whitelist")
|
||||
}
|
||||
n := NewNode(rn.ID, rn.IP, rn.UDP, rn.TCP)
|
||||
err := n.validateComplete()
|
||||
return n, err
|
||||
@@ -151,6 +160,7 @@ type conn interface {
|
||||
// udp implements the RPC protocol.
|
||||
type udp struct {
|
||||
conn conn
|
||||
netrestrict *netutil.Netlist
|
||||
priv *ecdsa.PrivateKey
|
||||
ourEndpoint rpcEndpoint
|
||||
|
||||
@@ -201,7 +211,7 @@ type reply struct {
|
||||
}
|
||||
|
||||
// ListenUDP returns a new table that listens for UDP packets on laddr.
|
||||
func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBPath string) (*Table, error) {
|
||||
func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBPath string, netrestrict *netutil.Netlist) (*Table, error) {
|
||||
addr, err := net.ResolveUDPAddr("udp", laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -210,7 +220,7 @@ func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBP
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tab, _, err := newUDP(priv, conn, natm, nodeDBPath)
|
||||
tab, _, err := newUDP(priv, conn, natm, nodeDBPath, netrestrict)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -218,13 +228,14 @@ func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBP
|
||||
return tab, nil
|
||||
}
|
||||
|
||||
func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath string) (*Table, *udp, error) {
|
||||
func newUDP(priv *ecdsa.PrivateKey, c conn, natm nat.Interface, nodeDBPath string, netrestrict *netutil.Netlist) (*Table, *udp, error) {
|
||||
udp := &udp{
|
||||
conn: c,
|
||||
priv: priv,
|
||||
closing: make(chan struct{}),
|
||||
gotreply: make(chan reply),
|
||||
addpending: make(chan *pending),
|
||||
conn: c,
|
||||
priv: priv,
|
||||
netrestrict: netrestrict,
|
||||
closing: make(chan struct{}),
|
||||
gotreply: make(chan reply),
|
||||
addpending: make(chan *pending),
|
||||
}
|
||||
realaddr := c.LocalAddr().(*net.UDPAddr)
|
||||
if natm != nil {
|
||||
@@ -281,9 +292,12 @@ func (t *udp) findnode(toid NodeID, toaddr *net.UDPAddr, target NodeID) ([]*Node
|
||||
reply := r.(*neighbors)
|
||||
for _, rn := range reply.Nodes {
|
||||
nreceived++
|
||||
if n, err := nodeFromRPC(rn); err == nil {
|
||||
nodes = append(nodes, n)
|
||||
n, err := t.nodeFromRPC(toaddr, rn)
|
||||
if err != nil {
|
||||
glog.V(logger.Detail).Infof("invalid neighbor node (%v) from %v: %v", rn.IP, toaddr, err)
|
||||
continue
|
||||
}
|
||||
nodes = append(nodes, n)
|
||||
}
|
||||
return nreceived >= bucketSize
|
||||
})
|
||||
@@ -479,13 +493,6 @@ func encodePacket(priv *ecdsa.PrivateKey, ptype byte, req interface{}) ([]byte,
|
||||
return packet, nil
|
||||
}
|
||||
|
||||
func isTemporaryError(err error) bool {
|
||||
tempErr, ok := err.(interface {
|
||||
Temporary() bool
|
||||
})
|
||||
return ok && tempErr.Temporary() || isPacketTooBig(err)
|
||||
}
|
||||
|
||||
// readLoop runs in its own goroutine. it handles incoming UDP packets.
|
||||
func (t *udp) readLoop() {
|
||||
defer t.conn.Close()
|
||||
@@ -495,7 +502,7 @@ func (t *udp) readLoop() {
|
||||
buf := make([]byte, 1280)
|
||||
for {
|
||||
nbytes, from, err := t.conn.ReadFromUDP(buf)
|
||||
if isTemporaryError(err) {
|
||||
if netutil.IsTemporaryError(err) {
|
||||
// Ignore temporary read errors.
|
||||
glog.V(logger.Debug).Infof("Temporary read error: %v", err)
|
||||
continue
|
||||
@@ -602,6 +609,9 @@ func (req *findnode) handle(t *udp, from *net.UDPAddr, fromID NodeID, mac []byte
|
||||
// Send neighbors in chunks with at most maxNeighbors per packet
|
||||
// to stay below the 1280 byte limit.
|
||||
for i, n := range closest {
|
||||
if netutil.CheckRelayIP(from.IP, n.IP) != nil {
|
||||
continue
|
||||
}
|
||||
p.Nodes = append(p.Nodes, nodeToRPC(n))
|
||||
if len(p.Nodes) == maxNeighbors || i == len(closest)-1 {
|
||||
t.send(from, neighborsPacket, p)
|
||||
|
||||
@@ -43,56 +43,6 @@ func init() {
|
||||
spew.Config.DisableMethods = true
|
||||
}
|
||||
|
||||
// This test checks that isPacketTooBig correctly identifies
|
||||
// errors that result from receiving a UDP packet larger
|
||||
// than the supplied receive buffer.
|
||||
func TestIsPacketTooBig(t *testing.T) {
|
||||
listener, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer listener.Close()
|
||||
sender, err := net.Dial("udp", listener.LocalAddr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer sender.Close()
|
||||
|
||||
sendN := 1800
|
||||
recvN := 300
|
||||
for i := 0; i < 20; i++ {
|
||||
go func() {
|
||||
buf := make([]byte, sendN)
|
||||
for i := range buf {
|
||||
buf[i] = byte(i)
|
||||
}
|
||||
sender.Write(buf)
|
||||
}()
|
||||
|
||||
buf := make([]byte, recvN)
|
||||
listener.SetDeadline(time.Now().Add(1 * time.Second))
|
||||
n, _, err := listener.ReadFrom(buf)
|
||||
if err != nil {
|
||||
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
|
||||
continue
|
||||
}
|
||||
if !isPacketTooBig(err) {
|
||||
t.Fatal("unexpected read error:", spew.Sdump(err))
|
||||
}
|
||||
continue
|
||||
}
|
||||
if n != recvN {
|
||||
t.Fatalf("short read: %d, want %d", n, recvN)
|
||||
}
|
||||
for i := range buf {
|
||||
if buf[i] != byte(i) {
|
||||
t.Fatalf("error in pattern")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// shared test variables
|
||||
var (
|
||||
futureExp = uint64(time.Now().Add(10 * time.Hour).Unix())
|
||||
@@ -118,9 +68,9 @@ func newUDPTest(t *testing.T) *udpTest {
|
||||
pipe: newpipe(),
|
||||
localkey: newkey(),
|
||||
remotekey: newkey(),
|
||||
remoteaddr: &net.UDPAddr{IP: net.IP{1, 2, 3, 4}, Port: 30303},
|
||||
remoteaddr: &net.UDPAddr{IP: net.IP{10, 0, 1, 99}, Port: 30303},
|
||||
}
|
||||
test.table, test.udp, _ = newUDP(test.localkey, test.pipe, nil, "")
|
||||
test.table, test.udp, _ = newUDP(test.localkey, test.pipe, nil, "", nil)
|
||||
return test
|
||||
}
|
||||
|
||||
@@ -362,8 +312,9 @@ func TestUDP_findnodeMultiReply(t *testing.T) {
|
||||
// check that the sent neighbors are all returned by findnode
|
||||
select {
|
||||
case result := <-resultc:
|
||||
if !reflect.DeepEqual(result, list) {
|
||||
t.Errorf("neighbors mismatch:\n got: %v\n want: %v", result, list)
|
||||
want := append(list[:2], list[3:]...)
|
||||
if !reflect.DeepEqual(result, want) {
|
||||
t.Errorf("neighbors mismatch:\n got: %v\n want: %v", result, want)
|
||||
}
|
||||
case err := <-errc:
|
||||
t.Errorf("findnode error: %v", err)
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//+build windows
|
||||
|
||||
package discover
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const _WSAEMSGSIZE = syscall.Errno(10040)
|
||||
|
||||
// reports whether err indicates that a UDP packet didn't
|
||||
// fit the receive buffer. On Windows, WSARecvFrom returns
|
||||
// code WSAEMSGSIZE and no data if this happens.
|
||||
func isPacketTooBig(err error) bool {
|
||||
if opErr, ok := err.(*net.OpError); ok {
|
||||
if scErr, ok := opErr.Err.(*os.SyscallError); ok {
|
||||
return scErr.Err == _WSAEMSGSIZE
|
||||
}
|
||||
return opErr.Err == _WSAEMSGSIZE
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
@@ -45,6 +46,7 @@ const (
|
||||
bucketRefreshInterval = 1 * time.Minute
|
||||
seedCount = 30
|
||||
seedMaxAge = 5 * 24 * time.Hour
|
||||
lowPort = 1024
|
||||
)
|
||||
|
||||
const testTopic = "foo"
|
||||
@@ -62,8 +64,9 @@ func debugLog(s string) {
|
||||
|
||||
// Network manages the table and all protocol interaction.
|
||||
type Network struct {
|
||||
db *nodeDB // database of known nodes
|
||||
conn transport
|
||||
db *nodeDB // database of known nodes
|
||||
conn transport
|
||||
netrestrict *netutil.Netlist
|
||||
|
||||
closed chan struct{} // closed when loop is done
|
||||
closeReq chan struct{} // 'request to close'
|
||||
@@ -132,7 +135,7 @@ type timeoutEvent struct {
|
||||
node *Node
|
||||
}
|
||||
|
||||
func newNetwork(conn transport, ourPubkey ecdsa.PublicKey, natm nat.Interface, dbPath string) (*Network, error) {
|
||||
func newNetwork(conn transport, ourPubkey ecdsa.PublicKey, natm nat.Interface, dbPath string, netrestrict *netutil.Netlist) (*Network, error) {
|
||||
ourID := PubkeyID(&ourPubkey)
|
||||
|
||||
var db *nodeDB
|
||||
@@ -147,6 +150,7 @@ func newNetwork(conn transport, ourPubkey ecdsa.PublicKey, natm nat.Interface, d
|
||||
net := &Network{
|
||||
db: db,
|
||||
conn: conn,
|
||||
netrestrict: netrestrict,
|
||||
tab: tab,
|
||||
topictab: newTopicTable(db, tab.self),
|
||||
ticketStore: newTicketStore(),
|
||||
@@ -684,16 +688,22 @@ func (net *Network) internNodeFromDB(dbn *Node) *Node {
|
||||
return n
|
||||
}
|
||||
|
||||
func (net *Network) internNodeFromNeighbours(rn rpcNode) (n *Node, err error) {
|
||||
func (net *Network) internNodeFromNeighbours(sender *net.UDPAddr, rn rpcNode) (n *Node, err error) {
|
||||
if rn.ID == net.tab.self.ID {
|
||||
return nil, errors.New("is self")
|
||||
}
|
||||
if rn.UDP <= lowPort {
|
||||
return nil, errors.New("low port")
|
||||
}
|
||||
n = net.nodes[rn.ID]
|
||||
if n == nil {
|
||||
// We haven't seen this node before.
|
||||
n, err = nodeFromRPC(rn)
|
||||
n.state = unknown
|
||||
n, err = nodeFromRPC(sender, rn)
|
||||
if net.netrestrict != nil && !net.netrestrict.Contains(n.IP) {
|
||||
return n, errors.New("not contained in netrestrict whitelist")
|
||||
}
|
||||
if err == nil {
|
||||
n.state = unknown
|
||||
net.nodes[n.ID] = n
|
||||
}
|
||||
return n, err
|
||||
@@ -1095,7 +1105,7 @@ func (net *Network) handleQueryEvent(n *Node, ev nodeEvent, pkt *ingressPacket)
|
||||
net.conn.sendNeighbours(n, results)
|
||||
return n.state, nil
|
||||
case neighborsPacket:
|
||||
err := net.handleNeighboursPacket(n, pkt.data.(*neighbors))
|
||||
err := net.handleNeighboursPacket(n, pkt)
|
||||
return n.state, err
|
||||
case neighboursTimeout:
|
||||
if n.pendingNeighbours != nil {
|
||||
@@ -1182,17 +1192,18 @@ func rlpHash(x interface{}) (h common.Hash) {
|
||||
return h
|
||||
}
|
||||
|
||||
func (net *Network) handleNeighboursPacket(n *Node, req *neighbors) error {
|
||||
func (net *Network) handleNeighboursPacket(n *Node, pkt *ingressPacket) error {
|
||||
if n.pendingNeighbours == nil {
|
||||
return errNoQuery
|
||||
}
|
||||
net.abortTimedEvent(n, neighboursTimeout)
|
||||
|
||||
req := pkt.data.(*neighbors)
|
||||
nodes := make([]*Node, len(req.Nodes))
|
||||
for i, rn := range req.Nodes {
|
||||
nn, err := net.internNodeFromNeighbours(rn)
|
||||
nn, err := net.internNodeFromNeighbours(pkt.remoteAddr, rn)
|
||||
if err != nil {
|
||||
glog.V(logger.Debug).Infof("invalid neighbour from %x: %v", n.ID[:8], err)
|
||||
glog.V(logger.Debug).Infof("invalid neighbour (%v) from %x@%v: %v", rn.IP, n.ID[:8], pkt.remoteAddr, err)
|
||||
continue
|
||||
}
|
||||
nodes[i] = nn
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
|
||||
func TestNetwork_Lookup(t *testing.T) {
|
||||
key, _ := crypto.GenerateKey()
|
||||
network, err := newNetwork(lookupTestnet, key.PublicKey, nil, "")
|
||||
network, err := newNetwork(lookupTestnet, key.PublicKey, nil, "", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func TestNetwork_Lookup(t *testing.T) {
|
||||
// t.Fatalf("lookup on empty table returned %d results: %#v", len(results), results)
|
||||
// }
|
||||
// seed table with initial node (otherwise lookup will terminate immediately)
|
||||
seeds := []*Node{NewNode(lookupTestnet.dists[256][0], net.IP{}, 256, 999)}
|
||||
seeds := []*Node{NewNode(lookupTestnet.dists[256][0], net.IP{10, 0, 2, 99}, lowPort+256, 999)}
|
||||
if err := network.SetFallbackNodes(seeds); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -272,13 +272,13 @@ func (tn *preminedTestnet) sendFindnode(to *Node, target NodeID) {
|
||||
func (tn *preminedTestnet) sendFindnodeHash(to *Node, target common.Hash) {
|
||||
// current log distance is encoded in port number
|
||||
// fmt.Println("findnode query at dist", toaddr.Port)
|
||||
if to.UDP == 0 {
|
||||
panic("query to node at distance 0")
|
||||
if to.UDP <= lowPort {
|
||||
panic("query to node at or below distance 0")
|
||||
}
|
||||
next := to.UDP - 1
|
||||
var result []rpcNode
|
||||
for i, id := range tn.dists[to.UDP] {
|
||||
result = append(result, nodeToRPC(NewNode(id, net.ParseIP("127.0.0.1"), next, uint16(i)+1)))
|
||||
for i, id := range tn.dists[to.UDP-lowPort] {
|
||||
result = append(result, nodeToRPC(NewNode(id, net.ParseIP("10.0.2.99"), next, uint16(i)+1+lowPort)))
|
||||
}
|
||||
injectResponse(tn.net, to, neighborsPacket, &neighbors{Nodes: result})
|
||||
}
|
||||
@@ -296,14 +296,14 @@ func (tn *preminedTestnet) send(to *Node, ptype nodeEvent, data interface{}) (ha
|
||||
// ignored
|
||||
case findnodeHashPacket:
|
||||
// current log distance is encoded in port number
|
||||
// fmt.Println("findnode query at dist", toaddr.Port)
|
||||
if to.UDP == 0 {
|
||||
panic("query to node at distance 0")
|
||||
// fmt.Println("findnode query at dist", toaddr.Port-lowPort)
|
||||
if to.UDP <= lowPort {
|
||||
panic("query to node at or below distance 0")
|
||||
}
|
||||
next := to.UDP - 1
|
||||
var result []rpcNode
|
||||
for i, id := range tn.dists[to.UDP] {
|
||||
result = append(result, nodeToRPC(NewNode(id, net.ParseIP("127.0.0.1"), next, uint16(i)+1)))
|
||||
for i, id := range tn.dists[to.UDP-lowPort] {
|
||||
result = append(result, nodeToRPC(NewNode(id, net.ParseIP("10.0.2.99"), next, uint16(i)+1+lowPort)))
|
||||
}
|
||||
injectResponse(tn.net, to, neighborsPacket, &neighbors{Nodes: result})
|
||||
default:
|
||||
@@ -328,8 +328,11 @@ func (tn *preminedTestnet) sendTopicRegister(to *Node, topics []Topic, idx int,
|
||||
panic("sendTopicRegister called")
|
||||
}
|
||||
|
||||
func (*preminedTestnet) Close() {}
|
||||
func (*preminedTestnet) localAddr() *net.UDPAddr { return new(net.UDPAddr) }
|
||||
func (*preminedTestnet) Close() {}
|
||||
|
||||
func (*preminedTestnet) localAddr() *net.UDPAddr {
|
||||
return &net.UDPAddr{IP: net.ParseIP("10.0.1.1"), Port: 40000}
|
||||
}
|
||||
|
||||
// mine generates a testnet struct literal with nodes at
|
||||
// various distances to the given target.
|
||||
|
||||
@@ -290,7 +290,7 @@ func (s *simulation) launchNode(log bool) *Network {
|
||||
addr := &net.UDPAddr{IP: ip, Port: 30303}
|
||||
|
||||
transport := &simTransport{joinTime: time.Now(), sender: id, senderAddr: addr, sim: s, priv: key}
|
||||
net, err := newNetwork(transport, key.PublicKey, nil, "<no database>")
|
||||
net, err := newNetwork(transport, key.PublicKey, nil, "<no database>", nil)
|
||||
if err != nil {
|
||||
panic("cannot launch new node: " + err.Error())
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
@@ -198,8 +199,10 @@ func (e1 rpcEndpoint) equal(e2 rpcEndpoint) bool {
|
||||
return e1.UDP == e2.UDP && e1.TCP == e2.TCP && bytes.Equal(e1.IP, e2.IP)
|
||||
}
|
||||
|
||||
func nodeFromRPC(rn rpcNode) (*Node, error) {
|
||||
// TODO: don't accept localhost, LAN addresses from internet hosts
|
||||
func nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*Node, error) {
|
||||
if err := netutil.CheckRelayIP(sender.IP, rn.IP); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n := NewNode(rn.ID, rn.IP, rn.UDP, rn.TCP)
|
||||
err := n.validateComplete()
|
||||
return n, err
|
||||
@@ -235,12 +238,12 @@ type udp struct {
|
||||
}
|
||||
|
||||
// ListenUDP returns a new table that listens for UDP packets on laddr.
|
||||
func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBPath string) (*Network, error) {
|
||||
func ListenUDP(priv *ecdsa.PrivateKey, laddr string, natm nat.Interface, nodeDBPath string, netrestrict *netutil.Netlist) (*Network, error) {
|
||||
transport, err := listenUDP(priv, laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
net, err := newNetwork(transport, priv.PublicKey, natm, nodeDBPath)
|
||||
net, err := newNetwork(transport, priv.PublicKey, natm, nodeDBPath, netrestrict)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -327,6 +330,9 @@ func (t *udp) sendTopicNodes(remote *Node, queryHash common.Hash, nodes []*Node)
|
||||
return
|
||||
}
|
||||
for i, result := range nodes {
|
||||
if netutil.CheckRelayIP(remote.IP, result.IP) != nil {
|
||||
continue
|
||||
}
|
||||
p.Nodes = append(p.Nodes, nodeToRPC(result))
|
||||
if len(p.Nodes) == maxTopicNodes || i == len(nodes)-1 {
|
||||
t.sendPacket(remote.ID, remote.addr(), byte(topicNodesPacket), p)
|
||||
@@ -385,7 +391,7 @@ func (t *udp) readLoop() {
|
||||
buf := make([]byte, 1280)
|
||||
for {
|
||||
nbytes, from, err := t.conn.ReadFromUDP(buf)
|
||||
if isTemporaryError(err) {
|
||||
if netutil.IsTemporaryError(err) {
|
||||
// Ignore temporary read errors.
|
||||
glog.V(logger.Debug).Infof("Temporary read error: %v", err)
|
||||
continue
|
||||
@@ -398,13 +404,6 @@ func (t *udp) readLoop() {
|
||||
}
|
||||
}
|
||||
|
||||
func isTemporaryError(err error) bool {
|
||||
tempErr, ok := err.(interface {
|
||||
Temporary() bool
|
||||
})
|
||||
return ok && tempErr.Temporary() || isPacketTooBig(err)
|
||||
}
|
||||
|
||||
func (t *udp) handlePacket(from *net.UDPAddr, buf []byte) error {
|
||||
pkt := ingressPacket{remoteAddr: from}
|
||||
if err := decodePacket(buf, &pkt); err != nil {
|
||||
|
||||
@@ -36,56 +36,6 @@ func init() {
|
||||
spew.Config.DisableMethods = true
|
||||
}
|
||||
|
||||
// This test checks that isPacketTooBig correctly identifies
|
||||
// errors that result from receiving a UDP packet larger
|
||||
// than the supplied receive buffer.
|
||||
func TestIsPacketTooBig(t *testing.T) {
|
||||
listener, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer listener.Close()
|
||||
sender, err := net.Dial("udp", listener.LocalAddr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer sender.Close()
|
||||
|
||||
sendN := 1800
|
||||
recvN := 300
|
||||
for i := 0; i < 20; i++ {
|
||||
go func() {
|
||||
buf := make([]byte, sendN)
|
||||
for i := range buf {
|
||||
buf[i] = byte(i)
|
||||
}
|
||||
sender.Write(buf)
|
||||
}()
|
||||
|
||||
buf := make([]byte, recvN)
|
||||
listener.SetDeadline(time.Now().Add(1 * time.Second))
|
||||
n, _, err := listener.ReadFrom(buf)
|
||||
if err != nil {
|
||||
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
|
||||
continue
|
||||
}
|
||||
if !isPacketTooBig(err) {
|
||||
t.Fatal("unexpected read error:", spew.Sdump(err))
|
||||
}
|
||||
continue
|
||||
}
|
||||
if n != recvN {
|
||||
t.Fatalf("short read: %d, want %d", n, recvN)
|
||||
}
|
||||
for i := range buf {
|
||||
if buf[i] != byte(i) {
|
||||
t.Fatalf("error in pattern")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// shared test variables
|
||||
var (
|
||||
futureExp = uint64(time.Now().Add(10 * time.Hour).Unix())
|
||||
|
||||
@@ -14,13 +14,12 @@
|
||||
// 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 !windows
|
||||
package netutil
|
||||
|
||||
package discv5
|
||||
|
||||
// reports whether err indicates that a UDP packet didn't
|
||||
// fit the receive buffer. There is no such error on
|
||||
// non-Windows platforms.
|
||||
func isPacketTooBig(err error) bool {
|
||||
return false
|
||||
// IsTemporaryError checks whether the given error should be considered temporary.
|
||||
func IsTemporaryError(err error) bool {
|
||||
tempErr, ok := err.(interface {
|
||||
Temporary() bool
|
||||
})
|
||||
return ok && tempErr.Temporary() || isPacketTooBig(err)
|
||||
}
|
||||
73
p2p/netutil/error_test.go
Normal file
73
p2p/netutil/error_test.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// This test checks that isPacketTooBig correctly identifies
|
||||
// errors that result from receiving a UDP packet larger
|
||||
// than the supplied receive buffer.
|
||||
func TestIsPacketTooBig(t *testing.T) {
|
||||
listener, err := net.ListenPacket("udp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer listener.Close()
|
||||
sender, err := net.Dial("udp", listener.LocalAddr().String())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer sender.Close()
|
||||
|
||||
sendN := 1800
|
||||
recvN := 300
|
||||
for i := 0; i < 20; i++ {
|
||||
go func() {
|
||||
buf := make([]byte, sendN)
|
||||
for i := range buf {
|
||||
buf[i] = byte(i)
|
||||
}
|
||||
sender.Write(buf)
|
||||
}()
|
||||
|
||||
buf := make([]byte, recvN)
|
||||
listener.SetDeadline(time.Now().Add(1 * time.Second))
|
||||
n, _, err := listener.ReadFrom(buf)
|
||||
if err != nil {
|
||||
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
|
||||
continue
|
||||
}
|
||||
if !isPacketTooBig(err) {
|
||||
t.Fatalf("unexpected read error: %v", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if n != recvN {
|
||||
t.Fatalf("short read: %d, want %d", n, recvN)
|
||||
}
|
||||
for i := range buf {
|
||||
if buf[i] != byte(i) {
|
||||
t.Fatalf("error in pattern")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
166
p2p/netutil/net.go
Normal file
166
p2p/netutil/net.go
Normal file
@@ -0,0 +1,166 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package netutil contains extensions to the net package.
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var lan4, lan6, special4, special6 Netlist
|
||||
|
||||
func init() {
|
||||
// Lists from RFC 5735, RFC 5156,
|
||||
// https://www.iana.org/assignments/iana-ipv4-special-registry/
|
||||
lan4.Add("0.0.0.0/8") // "This" network
|
||||
lan4.Add("10.0.0.0/8") // Private Use
|
||||
lan4.Add("172.16.0.0/12") // Private Use
|
||||
lan4.Add("192.168.0.0/16") // Private Use
|
||||
lan6.Add("fe80::/10") // Link-Local
|
||||
lan6.Add("fc00::/7") // Unique-Local
|
||||
special4.Add("192.0.0.0/29") // IPv4 Service Continuity
|
||||
special4.Add("192.0.0.9/32") // PCP Anycast
|
||||
special4.Add("192.0.0.170/32") // NAT64/DNS64 Discovery
|
||||
special4.Add("192.0.0.171/32") // NAT64/DNS64 Discovery
|
||||
special4.Add("192.0.2.0/24") // TEST-NET-1
|
||||
special4.Add("192.31.196.0/24") // AS112
|
||||
special4.Add("192.52.193.0/24") // AMT
|
||||
special4.Add("192.88.99.0/24") // 6to4 Relay Anycast
|
||||
special4.Add("192.175.48.0/24") // AS112
|
||||
special4.Add("198.18.0.0/15") // Device Benchmark Testing
|
||||
special4.Add("198.51.100.0/24") // TEST-NET-2
|
||||
special4.Add("203.0.113.0/24") // TEST-NET-3
|
||||
special4.Add("255.255.255.255/32") // Limited Broadcast
|
||||
|
||||
// http://www.iana.org/assignments/iana-ipv6-special-registry/
|
||||
special6.Add("100::/64")
|
||||
special6.Add("2001::/32")
|
||||
special6.Add("2001:1::1/128")
|
||||
special6.Add("2001:2::/48")
|
||||
special6.Add("2001:3::/32")
|
||||
special6.Add("2001:4:112::/48")
|
||||
special6.Add("2001:5::/32")
|
||||
special6.Add("2001:10::/28")
|
||||
special6.Add("2001:20::/28")
|
||||
special6.Add("2001:db8::/32")
|
||||
special6.Add("2002::/16")
|
||||
}
|
||||
|
||||
// Netlist is a list of IP networks.
|
||||
type Netlist []net.IPNet
|
||||
|
||||
// ParseNetlist parses a comma-separated list of CIDR masks.
|
||||
// Whitespace and extra commas are ignored.
|
||||
func ParseNetlist(s string) (*Netlist, error) {
|
||||
ws := strings.NewReplacer(" ", "", "\n", "", "\t", "")
|
||||
masks := strings.Split(ws.Replace(s), ",")
|
||||
l := make(Netlist, 0)
|
||||
for _, mask := range masks {
|
||||
if mask == "" {
|
||||
continue
|
||||
}
|
||||
_, n, err := net.ParseCIDR(mask)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l = append(l, *n)
|
||||
}
|
||||
return &l, nil
|
||||
}
|
||||
|
||||
// Add parses a CIDR mask and appends it to the list. It panics for invalid masks and is
|
||||
// intended to be used for setting up static lists.
|
||||
func (l *Netlist) Add(cidr string) {
|
||||
_, n, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
*l = append(*l, *n)
|
||||
}
|
||||
|
||||
// Contains reports whether the given IP is contained in the list.
|
||||
func (l *Netlist) Contains(ip net.IP) bool {
|
||||
if l == nil {
|
||||
return false
|
||||
}
|
||||
for _, net := range *l {
|
||||
if net.Contains(ip) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsLAN reports whether an IP is a local network address.
|
||||
func IsLAN(ip net.IP) bool {
|
||||
if ip.IsLoopback() {
|
||||
return true
|
||||
}
|
||||
if v4 := ip.To4(); v4 != nil {
|
||||
return lan4.Contains(v4)
|
||||
}
|
||||
return lan6.Contains(ip)
|
||||
}
|
||||
|
||||
// IsSpecialNetwork reports whether an IP is located in a special-use network range
|
||||
// This includes broadcast, multicast and documentation addresses.
|
||||
func IsSpecialNetwork(ip net.IP) bool {
|
||||
if ip.IsMulticast() {
|
||||
return true
|
||||
}
|
||||
if v4 := ip.To4(); v4 != nil {
|
||||
return special4.Contains(v4)
|
||||
}
|
||||
return special6.Contains(ip)
|
||||
}
|
||||
|
||||
var (
|
||||
errInvalid = errors.New("invalid IP")
|
||||
errUnspecified = errors.New("zero address")
|
||||
errSpecial = errors.New("special network")
|
||||
errLoopback = errors.New("loopback address from non-loopback host")
|
||||
errLAN = errors.New("LAN address from WAN host")
|
||||
)
|
||||
|
||||
// CheckRelayIP reports whether an IP relayed from the given sender IP
|
||||
// is a valid connection target.
|
||||
//
|
||||
// There are four rules:
|
||||
// - Special network addresses are never valid.
|
||||
// - Loopback addresses are OK if relayed by a loopback host.
|
||||
// - LAN addresses are OK if relayed by a LAN host.
|
||||
// - All other addresses are always acceptable.
|
||||
func CheckRelayIP(sender, addr net.IP) error {
|
||||
if len(addr) != net.IPv4len && len(addr) != net.IPv6len {
|
||||
return errInvalid
|
||||
}
|
||||
if addr.IsUnspecified() {
|
||||
return errUnspecified
|
||||
}
|
||||
if IsSpecialNetwork(addr) {
|
||||
return errSpecial
|
||||
}
|
||||
if addr.IsLoopback() && !sender.IsLoopback() {
|
||||
return errLoopback
|
||||
}
|
||||
if IsLAN(addr) && !IsLAN(sender) {
|
||||
return errLAN
|
||||
}
|
||||
return nil
|
||||
}
|
||||
173
p2p/netutil/net_test.go
Normal file
173
p2p/netutil/net_test.go
Normal file
@@ -0,0 +1,173 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
func TestParseNetlist(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input string
|
||||
wantErr error
|
||||
wantList *Netlist
|
||||
}{
|
||||
{
|
||||
input: "",
|
||||
wantList: &Netlist{},
|
||||
},
|
||||
{
|
||||
input: "127.0.0.0/8",
|
||||
wantErr: nil,
|
||||
wantList: &Netlist{{IP: net.IP{127, 0, 0, 0}, Mask: net.CIDRMask(8, 32)}},
|
||||
},
|
||||
{
|
||||
input: "127.0.0.0/44",
|
||||
wantErr: &net.ParseError{Type: "CIDR address", Text: "127.0.0.0/44"},
|
||||
},
|
||||
{
|
||||
input: "127.0.0.0/16, 23.23.23.23/24,",
|
||||
wantList: &Netlist{
|
||||
{IP: net.IP{127, 0, 0, 0}, Mask: net.CIDRMask(16, 32)},
|
||||
{IP: net.IP{23, 23, 23, 0}, Mask: net.CIDRMask(24, 32)},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
l, err := ParseNetlist(test.input)
|
||||
if !reflect.DeepEqual(err, test.wantErr) {
|
||||
t.Errorf("%q: got error %q, want %q", test.input, err, test.wantErr)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(l, test.wantList) {
|
||||
spew.Dump(l)
|
||||
spew.Dump(test.wantList)
|
||||
t.Errorf("%q: got %v, want %v", test.input, l, test.wantList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNilNetListContains(t *testing.T) {
|
||||
var list *Netlist
|
||||
checkContains(t, list.Contains, nil, []string{"1.2.3.4"})
|
||||
}
|
||||
|
||||
func TestIsLAN(t *testing.T) {
|
||||
checkContains(t, IsLAN,
|
||||
[]string{ // included
|
||||
"0.0.0.0",
|
||||
"0.2.0.8",
|
||||
"127.0.0.1",
|
||||
"10.0.1.1",
|
||||
"10.22.0.3",
|
||||
"172.31.252.251",
|
||||
"192.168.1.4",
|
||||
"fe80::f4a1:8eff:fec5:9d9d",
|
||||
"febf::ab32:2233",
|
||||
"fc00::4",
|
||||
},
|
||||
[]string{ // excluded
|
||||
"192.0.2.1",
|
||||
"1.0.0.0",
|
||||
"172.32.0.1",
|
||||
"fec0::2233",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestIsSpecialNetwork(t *testing.T) {
|
||||
checkContains(t, IsSpecialNetwork,
|
||||
[]string{ // included
|
||||
"192.0.2.1",
|
||||
"192.0.2.44",
|
||||
"2001:db8:85a3:8d3:1319:8a2e:370:7348",
|
||||
"255.255.255.255",
|
||||
"224.0.0.22", // IPv4 multicast
|
||||
"ff05::1:3", // IPv6 multicast
|
||||
},
|
||||
[]string{ // excluded
|
||||
"192.0.3.1",
|
||||
"1.0.0.0",
|
||||
"172.32.0.1",
|
||||
"fec0::2233",
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func checkContains(t *testing.T, fn func(net.IP) bool, inc, exc []string) {
|
||||
for _, s := range inc {
|
||||
if !fn(parseIP(s)) {
|
||||
t.Error("returned false for included address", s)
|
||||
}
|
||||
}
|
||||
for _, s := range exc {
|
||||
if fn(parseIP(s)) {
|
||||
t.Error("returned true for excluded address", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func parseIP(s string) net.IP {
|
||||
ip := net.ParseIP(s)
|
||||
if ip == nil {
|
||||
panic("invalid " + s)
|
||||
}
|
||||
return ip
|
||||
}
|
||||
|
||||
func TestCheckRelayIP(t *testing.T) {
|
||||
tests := []struct {
|
||||
sender, addr string
|
||||
want error
|
||||
}{
|
||||
{"127.0.0.1", "0.0.0.0", errUnspecified},
|
||||
{"192.168.0.1", "0.0.0.0", errUnspecified},
|
||||
{"23.55.1.242", "0.0.0.0", errUnspecified},
|
||||
{"127.0.0.1", "255.255.255.255", errSpecial},
|
||||
{"192.168.0.1", "255.255.255.255", errSpecial},
|
||||
{"23.55.1.242", "255.255.255.255", errSpecial},
|
||||
{"192.168.0.1", "127.0.2.19", errLoopback},
|
||||
{"23.55.1.242", "192.168.0.1", errLAN},
|
||||
|
||||
{"127.0.0.1", "127.0.2.19", nil},
|
||||
{"127.0.0.1", "192.168.0.1", nil},
|
||||
{"127.0.0.1", "23.55.1.242", nil},
|
||||
{"192.168.0.1", "192.168.0.1", nil},
|
||||
{"192.168.0.1", "23.55.1.242", nil},
|
||||
{"23.55.1.242", "23.55.1.242", nil},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
err := CheckRelayIP(parseIP(test.sender), parseIP(test.addr))
|
||||
if err != test.want {
|
||||
t.Errorf("%s from %s: got %q, want %q", test.addr, test.sender, err, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCheckRelayIP(b *testing.B) {
|
||||
sender := parseIP("23.55.1.242")
|
||||
addr := parseIP("23.55.1.2")
|
||||
for i := 0; i < b.N; i++ {
|
||||
CheckRelayIP(sender, addr)
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
//+build !windows
|
||||
|
||||
package discover
|
||||
package netutil
|
||||
|
||||
// reports whether err indicates that a UDP packet didn't
|
||||
// isPacketTooBig reports whether err indicates that a UDP packet didn't
|
||||
// fit the receive buffer. There is no such error on
|
||||
// non-Windows platforms.
|
||||
func isPacketTooBig(err error) bool {
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
//+build windows
|
||||
|
||||
package discv5
|
||||
package netutil
|
||||
|
||||
import (
|
||||
"net"
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
|
||||
const _WSAEMSGSIZE = syscall.Errno(10040)
|
||||
|
||||
// reports whether err indicates that a UDP packet didn't
|
||||
// isPacketTooBig reports whether err indicates that a UDP packet didn't
|
||||
// fit the receive buffer. On Windows, WSARecvFrom returns
|
||||
// code WSAEMSGSIZE and no data if this happens.
|
||||
func isPacketTooBig(err error) bool {
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -101,6 +102,11 @@ type Config struct {
|
||||
// allowed to connect, even above the peer limit.
|
||||
TrustedNodes []*discover.Node
|
||||
|
||||
// Connectivity can be restricted to certain IP networks.
|
||||
// If this option is set to a non-nil value, only hosts which match one of the
|
||||
// IP networks contained in the list are considered.
|
||||
NetRestrict *netutil.Netlist
|
||||
|
||||
// NodeDatabase is the path to the database containing the previously seen
|
||||
// live nodes in the network.
|
||||
NodeDatabase string
|
||||
@@ -356,7 +362,7 @@ func (srv *Server) Start() (err error) {
|
||||
|
||||
// node table
|
||||
if srv.Discovery {
|
||||
ntab, err := discover.ListenUDP(srv.PrivateKey, srv.ListenAddr, srv.NAT, srv.NodeDatabase)
|
||||
ntab, err := discover.ListenUDP(srv.PrivateKey, srv.ListenAddr, srv.NAT, srv.NodeDatabase, srv.NetRestrict)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -367,7 +373,7 @@ func (srv *Server) Start() (err error) {
|
||||
}
|
||||
|
||||
if srv.DiscoveryV5 {
|
||||
ntab, err := discv5.ListenUDP(srv.PrivateKey, srv.DiscoveryV5Addr, srv.NAT, "") //srv.NodeDatabase)
|
||||
ntab, err := discv5.ListenUDP(srv.PrivateKey, srv.DiscoveryV5Addr, srv.NAT, "", srv.NetRestrict) //srv.NodeDatabase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -381,7 +387,7 @@ func (srv *Server) Start() (err error) {
|
||||
if !srv.Discovery {
|
||||
dynPeers = 0
|
||||
}
|
||||
dialer := newDialState(srv.StaticNodes, srv.ntab, dynPeers)
|
||||
dialer := newDialState(srv.StaticNodes, srv.ntab, dynPeers, srv.NetRestrict)
|
||||
|
||||
// handshake
|
||||
srv.ourHandshake = &protoHandshake{Version: baseProtocolVersion, Name: srv.Name, ID: discover.PubkeyID(&srv.PrivateKey.PublicKey)}
|
||||
@@ -634,8 +640,19 @@ func (srv *Server) listenLoop() {
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Reject connections that do not match NetRestrict.
|
||||
if srv.NetRestrict != nil {
|
||||
if tcp, ok := fd.RemoteAddr().(*net.TCPAddr); ok && !srv.NetRestrict.Contains(tcp.IP) {
|
||||
glog.V(logger.Debug).Infof("Rejected conn %v because it is not whitelisted in NetRestrict", fd.RemoteAddr())
|
||||
fd.Close()
|
||||
slots <- struct{}{}
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
fd = newMeteredConn(fd, true)
|
||||
glog.V(logger.Debug).Infof("Accepted conn %v\n", fd.RemoteAddr())
|
||||
glog.V(logger.Debug).Infof("Accepted conn %v", fd.RemoteAddr())
|
||||
|
||||
// Spawn the handler. It will give the slot back when the connection
|
||||
// has been established.
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package params
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -24,6 +25,7 @@ import (
|
||||
|
||||
// MainnetChainConfig is the chain parameters to run a node on the main network.
|
||||
var MainnetChainConfig = &ChainConfig{
|
||||
ChainId: MainNetChainID,
|
||||
HomesteadBlock: MainNetHomesteadBlock,
|
||||
DAOForkBlock: MainNetDAOForkBlock,
|
||||
DAOForkSupport: true,
|
||||
@@ -35,13 +37,14 @@ var MainnetChainConfig = &ChainConfig{
|
||||
|
||||
// TestnetChainConfig is the chain parameters to run a node on the test network.
|
||||
var TestnetChainConfig = &ChainConfig{
|
||||
HomesteadBlock: TestNetHomesteadBlock,
|
||||
DAOForkBlock: TestNetDAOForkBlock,
|
||||
DAOForkSupport: false,
|
||||
EIP150Block: TestNetHomesteadGasRepriceBlock,
|
||||
EIP150Hash: TestNetHomesteadGasRepriceHash,
|
||||
EIP155Block: TestNetSpuriousDragon,
|
||||
EIP158Block: TestNetSpuriousDragon,
|
||||
ChainId: big.NewInt(3),
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
DAOForkBlock: nil,
|
||||
DAOForkSupport: true,
|
||||
EIP150Block: big.NewInt(0),
|
||||
EIP150Hash: common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d"),
|
||||
EIP155Block: big.NewInt(10),
|
||||
EIP158Block: big.NewInt(10),
|
||||
}
|
||||
|
||||
// ChainConfig is the core config which determines the blockchain settings.
|
||||
@@ -64,6 +67,19 @@ type ChainConfig struct {
|
||||
EIP158Block *big.Int `json:"eip158Block"` // EIP158 HF block
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (c *ChainConfig) String() string {
|
||||
return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v}",
|
||||
c.ChainId,
|
||||
c.HomesteadBlock,
|
||||
c.DAOForkBlock,
|
||||
c.DAOForkSupport,
|
||||
c.EIP150Block,
|
||||
c.EIP155Block,
|
||||
c.EIP158Block,
|
||||
)
|
||||
}
|
||||
|
||||
var (
|
||||
TestChainConfig = &ChainConfig{big.NewInt(1), new(big.Int), new(big.Int), true, new(big.Int), common.Hash{}, new(big.Int), new(big.Int)}
|
||||
TestRules = TestChainConfig.Rules(new(big.Int))
|
||||
|
||||
@@ -23,21 +23,21 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
TestNetGenesisHash = common.HexToHash("0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303") // Testnet genesis hash to enforce below configs on
|
||||
TestNetGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d") // Testnet genesis hash to enforce below configs on
|
||||
MainNetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3") // Mainnet genesis hash to enforce below configs on
|
||||
|
||||
TestNetHomesteadBlock = big.NewInt(494000) // Testnet homestead block
|
||||
TestNetHomesteadBlock = big.NewInt(0) // Testnet homestead block
|
||||
MainNetHomesteadBlock = big.NewInt(1150000) // Mainnet homestead block
|
||||
|
||||
TestNetHomesteadGasRepriceBlock = big.NewInt(1783000) // Testnet gas reprice block
|
||||
TestNetHomesteadGasRepriceBlock = big.NewInt(0) // Testnet gas reprice block
|
||||
MainNetHomesteadGasRepriceBlock = big.NewInt(2463000) // Mainnet gas reprice block
|
||||
|
||||
TestNetHomesteadGasRepriceHash = common.HexToHash("0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145") // Testnet gas reprice block hash (used by fast sync)
|
||||
TestNetHomesteadGasRepriceHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d") // Testnet gas reprice block hash (used by fast sync)
|
||||
MainNetHomesteadGasRepriceHash = common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0") // Mainnet gas reprice block hash (used by fast sync)
|
||||
|
||||
TestNetSpuriousDragon = big.NewInt(1885000)
|
||||
TestNetSpuriousDragon = big.NewInt(10)
|
||||
MainNetSpuriousDragon = big.NewInt(2675000)
|
||||
|
||||
TestNetChainID = big.NewInt(2) // Test net default chain ID
|
||||
TestNetChainID = big.NewInt(3) // Test net default chain ID
|
||||
MainNetChainID = big.NewInt(1) // main net default chain ID
|
||||
)
|
||||
|
||||
@@ -14,23 +14,14 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package utils contains internal helper functions for go-ethereum commands.
|
||||
package utils
|
||||
package params
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
VersionMajor = 1 // Major version component of the current release
|
||||
VersionMinor = 5 // Minor version component of the current release
|
||||
VersionPatch = 0 // Patch version component of the current release
|
||||
VersionPatch = 4 // Patch version component of the current release
|
||||
VersionMeta = "stable" // Version metadata to append to the version string
|
||||
)
|
||||
|
||||
@@ -42,23 +33,3 @@ var Version = func() string {
|
||||
}
|
||||
return v
|
||||
}()
|
||||
|
||||
// MakeDefaultExtraData returns the default Ethereum block extra data blob.
|
||||
func MakeDefaultExtraData(clientIdentifier string) []byte {
|
||||
var clientInfo = struct {
|
||||
Version uint
|
||||
Name string
|
||||
GoVersion string
|
||||
Os string
|
||||
}{uint(VersionMajor<<16 | VersionMinor<<8 | VersionPatch), clientIdentifier, runtime.Version(), runtime.GOOS}
|
||||
extra, err := rlp.EncodeToBytes(clientInfo)
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information:", err)
|
||||
}
|
||||
if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize)
|
||||
glog.V(logger.Debug).Infof("extra: %x\n", extra)
|
||||
return nil
|
||||
}
|
||||
return extra
|
||||
}
|
||||
@@ -54,11 +54,12 @@ type Config struct {
|
||||
PublicKey string
|
||||
BzzKey string
|
||||
EnsRoot common.Address
|
||||
NetworkId uint64
|
||||
}
|
||||
|
||||
// config is agnostic to where private key is coming from
|
||||
// so managing accounts is outside swarm and left to wrappers
|
||||
func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey) (self *Config, err error) {
|
||||
func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey, networkId uint64) (self *Config, err error) {
|
||||
address := crypto.PubkeyToAddress(prvKey.PublicKey) // default beneficiary address
|
||||
dirpath := filepath.Join(path, "bzz-"+common.Bytes2Hex(address.Bytes()))
|
||||
err = os.MkdirAll(dirpath, os.ModePerm)
|
||||
@@ -82,6 +83,7 @@ func NewConfig(path string, contract common.Address, prvKey *ecdsa.PrivateKey) (
|
||||
PublicKey: pubkeyhex,
|
||||
BzzKey: keyhex,
|
||||
EnsRoot: toyNetEnsRoot,
|
||||
NetworkId: networkId,
|
||||
}
|
||||
data, err = ioutil.ReadFile(confpath)
|
||||
if err != nil {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user