all: resolved conflicts

This commit is contained in:
Anton Evangelatov 2018-02-20 17:14:41 +01:00
commit 05e09eb520
414 changed files with 27057 additions and 9038 deletions

View file

@ -2,7 +2,7 @@
Before you do a feature request please check and make sure that it isn't possible Before you do a feature request please check and make sure that it isn't possible
through some other means. The JavaScript enabled console is a powerful feature through some other means. The JavaScript enabled console is a powerful feature
in the right hands. Please check our [Bitchin' tricks](https://github.com/ethereum/go-ethereum/wiki/bitchin-tricks) wiki page for more info in the right hands. Please check our [Wiki page](https://github.com/ethereum/go-ethereum/wiki) for more info
and help. and help.
## Contributing ## Contributing

View file

@ -65,7 +65,8 @@ Enrique Fynn <enriquefynn@gmail.com>
Vincent G <caktux@gmail.com> Vincent G <caktux@gmail.com>
RJ Catalano <rj@erisindustries.com> RJ Catalano <catalanor0220@gmail.com>
RJ Catalano <catalanor0220@gmail.com> <rj@erisindustries.com>
Nchinda Nchinda <nchinda2@gmail.com> Nchinda Nchinda <nchinda2@gmail.com>
@ -109,3 +110,14 @@ Frank Wang <eternnoir@gmail.com>
Gary Rong <garyrong0905@gmail.com> Gary Rong <garyrong0905@gmail.com>
Guillaume Nicolas <guin56@gmail.com> Guillaume Nicolas <guin56@gmail.com>
Sorin Neacsu <sorin.neacsu@gmail.com>
Sorin Neacsu <sorin.neacsu@gmail.com> <sorin@users.noreply.github.com>
Valentin Wüstholz <wuestholz@gmail.com>
Valentin Wüstholz <wuestholz@gmail.com> <wuestholz@users.noreply.github.com>
Armin Braun <me@obrown.io>
Ernesto del Toro <ernesto.deltoro@gmail.com>
Ernesto del Toro <ernesto.deltoro@gmail.com> <ernestodeltoro@users.noreply.github.com>

View file

@ -3,17 +3,6 @@ go_import_path: github.com/ethereum/go-ethereum
sudo: false sudo: false
matrix: matrix:
include: include:
- os: linux
dist: trusty
sudo: required
go: 1.7.x
script:
- sudo modprobe fuse
- sudo chmod 666 /dev/fuse
- sudo chown root:$USER /etc/fuse.conf
- go run build/ci.go install
- go run build/ci.go test -coverage
- os: linux - os: linux
dist: trusty dist: trusty
sudo: required sudo: required
@ -25,7 +14,6 @@ matrix:
- go run build/ci.go install - go run build/ci.go install
- go run build/ci.go test -coverage - go run build/ci.go test -coverage
# These are the latest Go versions.
- os: linux - os: linux
dist: trusty dist: trusty
sudo: required sudo: required
@ -47,6 +35,28 @@ matrix:
- go run build/ci.go install - go run build/ci.go install
- go run build/ci.go test -coverage - go run build/ci.go test -coverage
# These are the latest Go versions.
- os: linux
dist: trusty
sudo: required
go: "1.10"
script:
- sudo modprobe fuse
- sudo chmod 666 /dev/fuse
- sudo chown root:$USER /etc/fuse.conf
- go run build/ci.go install
- go run build/ci.go test -coverage
- os: osx
go: "1.10"
script:
- unset -f cd # workaround for https://github.com/travis-ci/travis-ci/issues/8703
- brew update
- brew install caskroom/cask/brew-cask
- brew cask install osxfuse
- go run build/ci.go install
- go run build/ci.go test -coverage
# This builder only tests code linters on latest version of Go # This builder only tests code linters on latest version of Go
- os: linux - os: linux
dist: trusty dist: trusty
@ -185,6 +195,8 @@ matrix:
- xctool -version - xctool -version
- xcrun simctl list - xcrun simctl list
# Workaround for https://github.com/golang/go/issues/23749
- export CGO_CFLAGS_ALLOW='-fmodules|-fblocks|-fobjc-arc'
- 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
# This builder does the Azure archive purges to avoid accumulating junk # This builder does the Azure archive purges to avoid accumulating junk

92
AUTHORS
View file

@ -1,85 +1,173 @@
# This is the official list of go-ethereum authors for copyright purposes. # This is the official list of go-ethereum authors for copyright purposes.
Afri Schoedon <5chdn@users.noreply.github.com>
Agustin Armellini Fischer <armellini13@gmail.com>
Airead <fgh1987168@gmail.com>
Alan Chen <alanchchen@users.noreply.github.com>
Alejandro Isaza <alejandro.isaza@gmail.com>
Ales Katona <ales@coinbase.com> Ales Katona <ales@coinbase.com>
Alex Leverington <alex@ethdev.com> Alex Leverington <alex@ethdev.com>
Alex Wu <wuyiding@gmail.com>
Alexandre Van de Sande <alex.vandesande@ethdev.com> Alexandre Van de Sande <alex.vandesande@ethdev.com>
Ali Hajimirza <Ali92hm@users.noreply.github.com>
Anton Evangelatov <anton.evangelatov@gmail.com>
Arba Sasmoyo <arba.sasmoyo@gmail.com>
Armani Ferrante <armaniferrante@berkeley.edu>
Armin Braun <me@obrown.io>
Aron Fischer <github@aron.guru> Aron Fischer <github@aron.guru>
Bas van Kervel <bas@ethdev.com> Bas van Kervel <bas@ethdev.com>
Benjamin Brent <benjamin@benjaminbrent.com> Benjamin Brent <benjamin@benjaminbrent.com>
Benoit Verkindt <benoit.verkindt@gmail.com>
Bo <bohende@gmail.com>
Bo Ye <boy.e.computer.1982@outlook.com>
Bob Glickstein <bobg@users.noreply.github.com>
Brian Schroeder <bts@gmail.com> Brian Schroeder <bts@gmail.com>
Casey Detrio <cdetrio@gmail.com> Casey Detrio <cdetrio@gmail.com>
Chase Wright <mysticryuujin@gmail.com>
Christoph Jentzsch <jentzsch.software@gmail.com> Christoph Jentzsch <jentzsch.software@gmail.com>
Daniel A. Nagy <nagy.da@gmail.com> Daniel A. Nagy <nagy.da@gmail.com>
Daniel Sloof <goapsychadelic@gmail.com>
Darrel Herbst <dherbst@gmail.com>
Dave Appleton <calistralabs@gmail.com>
Diego Siqueira <DiSiqueira@users.noreply.github.com> Diego Siqueira <DiSiqueira@users.noreply.github.com>
Dmitry Shulyak <yashulyak@gmail.com>
Egon Elbre <egonelbre@gmail.com>
Elias Naur <elias.naur@gmail.com>
Elliot Shepherd <elliot@identitii.com> Elliot Shepherd <elliot@identitii.com>
Enrique Fynn <enriquefynn@gmail.com> Enrique Fynn <enriquefynn@gmail.com>
Ernesto del Toro <ernesto.deltoro@gmail.com>
Ethan Buchman <ethan@coinculture.info> Ethan Buchman <ethan@coinculture.info>
Eugene Valeyev <evgen.povt@gmail.com>
Evangelos Pappas <epappas@evalonlabs.com>
Evgeny Danilenko <6655321@bk.ru>
Fabian Vogelsteller <fabian@frozeman.de> Fabian Vogelsteller <fabian@frozeman.de>
Fabio Barone <fabio.barone.co@gmail.com>
Fabio Berger <fabioberger1991@gmail.com> Fabio Berger <fabioberger1991@gmail.com>
FaceHo <facehoshi@gmail.com>
Felix Lange <fjl@twurst.com> Felix Lange <fjl@twurst.com>
Fiisio <liangcszzu@163.com>
Frank Wang <eternnoir@gmail.com> Frank Wang <eternnoir@gmail.com>
Furkan KAMACI <furkankamaci@gmail.com>
Gary Rong <garyrong0905@gmail.com> Gary Rong <garyrong0905@gmail.com>
George Ornbo <george@shapeshed.com>
Gregg Dourgarian <greggd@tempworks.com> Gregg Dourgarian <greggd@tempworks.com>
Guillaume Ballet <gballet@gmail.com>
Guillaume Nicolas <guin56@gmail.com> Guillaume Nicolas <guin56@gmail.com>
Gustav Simonsson <gustav.simonsson@gmail.com> Gustav Simonsson <gustav.simonsson@gmail.com>
Hao Bryan Cheng <haobcheng@gmail.com> Hao Bryan Cheng <haobcheng@gmail.com>
Henning Diedrich <hd@eonblast.com> Henning Diedrich <hd@eonblast.com>
Isidoro Ghezzi <isidoro.ghezzi@icloud.com> Isidoro Ghezzi <isidoro.ghezzi@icloud.com>
Ivan Daniluk <ivan.daniluk@gmail.com>
Jae Kwon <jkwon.work@gmail.com> Jae Kwon <jkwon.work@gmail.com>
Jamie Pitts <james.pitts@gmail.com> Jamie Pitts <james.pitts@gmail.com>
Janoš Guljaš <janos@users.noreply.github.com>
Jason Carver <jacarver@linkedin.com> Jason Carver <jacarver@linkedin.com>
Jay Guo <guojiannan1101@gmail.com>
Jeff R. Allen <jra@nella.org> Jeff R. Allen <jra@nella.org>
Jeffrey Wilcke <jeffrey@ethereum.org> Jeffrey Wilcke <jeffrey@ethereum.org>
Jens Agerberg <github@agerberg.me> Jens Agerberg <github@agerberg.me>
Jia Chenhui <jiachenhui1989@gmail.com>
Jim McDonald <Jim@mcdee.net>
Joel Burget <joelburget@gmail.com>
Jonathan Brown <jbrown@bluedroplet.com> Jonathan Brown <jbrown@bluedroplet.com>
Joseph Chow <ethereum@outlook.com> Joseph Chow <ethereum@outlook.com>
Justin Clark-Casey <justincc@justincc.org> Justin Clark-Casey <justincc@justincc.org>
Justin Drake <drakefjustin@gmail.com> Justin Drake <drakefjustin@gmail.com>
Kenji Siu <kenji@isuntv.com> Kenji Siu <kenji@isuntv.com>
Kobi Gurkan <kobigurk@gmail.com> Kobi Gurkan <kobigurk@gmail.com>
Konrad Feldmeier <konrad@brainbot.com>
Kurkó Mihály <kurkomisi@users.noreply.github.com>
Kyuntae Ethan Kim <ethan.kyuntae.kim@gmail.com>
Lefteris Karapetsas <lefteris@refu.co> Lefteris Karapetsas <lefteris@refu.co>
Leif Jurvetson <leijurv@gmail.com> Leif Jurvetson <leijurv@gmail.com>
Leo Shklovskii <leo@thermopylae.net>
Lewis Marshall <lewis@lmars.net> Lewis Marshall <lewis@lmars.net>
Lio李欧 <lionello@users.noreply.github.com>
Louis Holbrook <dev@holbrook.no> Louis Holbrook <dev@holbrook.no>
Luca Zeug <luclu@users.noreply.github.com> Luca Zeug <luclu@users.noreply.github.com>
Magicking <s@6120.eu>
Maran Hidskes <maran.hidskes@gmail.com> Maran Hidskes <maran.hidskes@gmail.com>
Marek Kotewicz <marek.kotewicz@gmail.com> Marek Kotewicz <marek.kotewicz@gmail.com>
Mark <markya0616@gmail.com>
Martin Holst Swende <martin@swende.se> Martin Holst Swende <martin@swende.se>
Matthew Di Ferrante <mattdf@users.noreply.github.com> Matthew Di Ferrante <mattdf@users.noreply.github.com>
Matthew Wampler-Doty <matthew.wampler.doty@gmail.com> Matthew Wampler-Doty <matthew.wampler.doty@gmail.com>
Maximilian Meister <mmeister@suse.de>
Micah Zoltu <micah@zoltu.net> Micah Zoltu <micah@zoltu.net>
Michael Ruminer <michael.ruminer+github@gmail.com>
Miguel Mota <miguelmota2@gmail.com>
Miya Chen <miyatlchen@gmail.com>
Nchinda Nchinda <nchinda2@gmail.com> Nchinda Nchinda <nchinda2@gmail.com>
Nick Dodson <silentcicero@outlook.com> Nick Dodson <silentcicero@outlook.com>
Nick Johnson <arachnid@notdot.net> Nick Johnson <arachnid@notdot.net>
Nicolas Guillaume <gunicolas@sqli.com>
Noman <noman@noman.land>
Oli Bye <olibye@users.noreply.github.com>
Paul Litvak <litvakpol@012.net.il>
Paulo L F Casaretto <pcasaretto@gmail.com> Paulo L F Casaretto <pcasaretto@gmail.com>
Paweł Bylica <chfast@gmail.com>
Peter Pratscher <pratscher@gmail.com> Peter Pratscher <pratscher@gmail.com>
Petr Mikusek <petr@mikusek.info>
Péter Szilágyi <peterke@gmail.com> Péter Szilágyi <peterke@gmail.com>
RJ Catalano <rj@erisindustries.com> RJ Catalano <catalanor0220@gmail.com>
Ramesh Nair <ram@hiddentao.com> Ramesh Nair <ram@hiddentao.com>
Ricardo Catalinas Jiménez <r@untroubled.be> Ricardo Catalinas Jiménez <r@untroubled.be>
Ricardo Domingos <ricardohsd@gmail.com>
Richard Hart <richardhart92@gmail.com>
Rob <robert@rojotek.com>
Robert Zaremba <robert.zaremba@scale-it.pl>
Russ Cox <rsc@golang.org>
Rémy Roy <remyroy@remyroy.com> Rémy Roy <remyroy@remyroy.com>
S. Matthew English <s-matthew-english@users.noreply.github.com>
Shintaro Kaneko <kaneshin0120@gmail.com> Shintaro Kaneko <kaneshin0120@gmail.com>
Sorin Neacsu <sorin.neacsu@gmail.com>
Stein Dekker <dekker.stein@gmail.com> Stein Dekker <dekker.stein@gmail.com>
Steve Waldman <swaldman@mchange.com>
Steven Roose <stevenroose@gmail.com> Steven Roose <stevenroose@gmail.com>
Taylor Gerring <taylor.gerring@gmail.com> Taylor Gerring <taylor.gerring@gmail.com>
Thomas Bocek <tom@tomp2p.net> Thomas Bocek <tom@tomp2p.net>
Ti Zhou <tizhou1986@gmail.com>
Tosh Camille <tochecamille@gmail.com> Tosh Camille <tochecamille@gmail.com>
Valentin Wüstholz <wuestholz@users.noreply.github.com> Valentin Wüstholz <wuestholz@gmail.com>
Victor Farazdagi <simple.square@gmail.com> Victor Farazdagi <simple.square@gmail.com>
Victor Tran <vu.tran54@gmail.com> Victor Tran <vu.tran54@gmail.com>
Viktor Trón <viktor.tron@gmail.com> Viktor Trón <viktor.tron@gmail.com>
Ville Sundell <github@solarius.fi> Ville Sundell <github@solarius.fi>
Vincent G <caktux@gmail.com> Vincent G <caktux@gmail.com>
Vitalik Buterin <v@buterin.com> Vitalik Buterin <v@buterin.com>
Vitaly V <vvelikodny@gmail.com>
Vivek Anand <vivekanand1101@users.noreply.github.com> Vivek Anand <vivekanand1101@users.noreply.github.com>
Vlad Gluhovsky <gluk256@users.noreply.github.com> Vlad Gluhovsky <gluk256@users.noreply.github.com>
Yohann Léon <sybiload@gmail.com> Yohann Léon <sybiload@gmail.com>
Yoichi Hirai <i@yoichihirai.com> Yoichi Hirai <i@yoichihirai.com>
Yondon Fu <yondon.fu@gmail.com>
Zach <zach.ramsay@gmail.com>
Zahoor Mohamed <zahoor@zahoor.in> Zahoor Mohamed <zahoor@zahoor.in>
Zoe Nolan <github@zoenolan.org>
Zsolt Felföldi <zsfelfoldi@gmail.com> Zsolt Felföldi <zsfelfoldi@gmail.com>
am2rican5 <am2rican5@gmail.com>
ayeowch <ayeowch@gmail.com>
b00ris <b00ris@mail.ru>
bailantaotao <Edwin@maicoin.com>
baizhenxuan <nkbai@163.com>
bloonfield <bloonfield@163.com>
changhong <changhong.yu@shanbay.com>
evgk <evgeniy.kamyshev@gmail.com>
ferhat elmas <elmas.ferhat@gmail.com>
holisticode <holistic.computing@gmail.com> holisticode <holistic.computing@gmail.com>
jtakalai <juuso.takalainen@streamr.com>
ken10100147 <sunhongping@kanjian.com> ken10100147 <sunhongping@kanjian.com>
ligi <ligi@ligi.de> ligi <ligi@ligi.de>
mark.lin <mark@maicoin.com>
necaremus <necaremus@gmail.com>
njupt-moon <1015041018@njupt.edu.cn>
nkbai <nkbai@163.com>
rhaps107 <dod-source@yandex.ru>
slumber1122 <slumber1122@gmail.com>
sunxiaojun2014 <sunxiaojun-xy@360.cn>
terasum <terasum@163.com>
tsarpaul <Litvakpol@012.net.il>
xiekeyang <xiekeyang@users.noreply.github.com> xiekeyang <xiekeyang@users.noreply.github.com>
yoza <yoza.is12s@gmail.com>
ΞTHΞЯSPHΞЯΞ <{viktor.tron,nagydani,zsfelfoldi}@gmail.com> ΞTHΞЯSPHΞЯΞ <{viktor.tron,nagydani,zsfelfoldi}@gmail.com>
Максим Чусовлянов <mchusovlianov@gmail.com> Максим Чусовлянов <mchusovlianov@gmail.com>

View file

@ -56,16 +56,14 @@ the user doesn't care about years-old historical data, so we can fast-sync quick
state of the network. To do so: state of the network. To do so:
``` ```
$ geth --fast --cache=512 console $ geth console
``` ```
This command will: This command will:
* Start geth in fast sync mode (`--fast`), causing it to download more data in exchange for avoiding * Start geth in fast sync mode (default, can be changed with the `--syncmode` flag), causing it to
processing the entire history of the Ethereum network, which is very CPU intensive. download more data in exchange for avoiding processing the entire history of the Ethereum network,
* Bump the memory allowance of the database to 512MB (`--cache=512`), which can help significantly in which is very CPU intensive.
sync times especially for HDD users. This flag is optional and you can set it as high or as low as
you'd like, though we'd recommend the 512MB - 2GB range.
* Start up Geth's built-in interactive [JavaScript console](https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console), * Start up Geth's built-in interactive [JavaScript console](https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console),
(via the trailing `console` subcommand) through which you can invoke all official [`web3` methods](https://github.com/ethereum/wiki/wiki/JavaScript-API) (via the trailing `console` subcommand) through which you can invoke all official [`web3` methods](https://github.com/ethereum/wiki/wiki/JavaScript-API)
as well as Geth's own [management APIs](https://github.com/ethereum/go-ethereum/wiki/Management-APIs). as well as Geth's own [management APIs](https://github.com/ethereum/go-ethereum/wiki/Management-APIs).
@ -80,12 +78,11 @@ entire system. In other words, instead of attaching to the main network, you wan
network with your node, which is fully equivalent to the main network, but with play-Ether only. network with your node, which is fully equivalent to the main network, but with play-Ether only.
``` ```
$ geth --testnet --fast --cache=512 console $ geth --testnet console
``` ```
The `--fast`, `--cache` flags and `console` subcommand have the exact same meaning as above and they The `console` subcommand have the exact same meaning as above and they are equally useful on the
are equally useful on the testnet too. Please see above for their explanations if you've skipped to testnet too. Please see above for their explanations if you've skipped to here.
here.
Specifying the `--testnet` flag however will reconfigure your Geth instance a bit: Specifying the `--testnet` flag however will reconfigure your Geth instance a bit:
@ -102,6 +99,14 @@ over between the main network and test network, you should make sure to always u
for play-money and real-money. Unless you manually move accounts, Geth will by default correctly for play-money and real-money. Unless you manually move accounts, Geth will by default correctly
separate the two networks and will not make any accounts available between them.* separate the two networks and will not make any accounts available between them.*
### Full node on the Rinkeby test network
The above test network is a cross client one based on the ethash proof-of-work consensus algorithm. As such, it has certain extra overhead and is more susceptible to reorganization attacks due to the network's low difficulty / security. Go Ethereum also supports connecting to a proof-of-authority based test network called [*Rinkeby*](https://www.rinkeby.io) (operated by members of the community). This network is lighter, more secure, but is only supported by go-ethereum.
```
$ geth --rinkeby console
```
### Configuration ### Configuration
As an alternative to passing the numerous flags to the `geth` binary, you can also pass a configuration file via: As an alternative to passing the numerous flags to the `geth` binary, you can also pass a configuration file via:
@ -125,10 +130,10 @@ One of the quickest ways to get Ethereum up and running on your machine is by us
``` ```
docker run -d --name ethereum-node -v /Users/alice/ethereum:/root \ docker run -d --name ethereum-node -v /Users/alice/ethereum:/root \
-p 8545:8545 -p 30303:30303 \ -p 8545:8545 -p 30303:30303 \
ethereum/client-go --fast --cache=512 ethereum/client-go
``` ```
This will start geth in fast sync mode with a DB memory allowance of 512MB just as the above command does. It will also create a persistent volume in your home directory for saving your blockchain as well as map the default ports. There is also an `alpine` tag available for a slim version of the image. This will start geth in fast-sync mode with a DB memory allowance of 1GB just as the above command does. It will also create a persistent volume in your home directory for saving your blockchain as well as map the default ports. There is also an `alpine` tag available for a slim version of the image.
Do not forget `--rpcaddr 0.0.0.0`, if you want to access RPC from other containers and/or hosts. By default, `geth` binds to the local interface and RPC endpoints is not accessible from the outside. Do not forget `--rpcaddr 0.0.0.0`, if you want to access RPC from other containers and/or hosts. By default, `geth` binds to the local interface and RPC endpoints is not accessible from the outside.

View file

@ -1 +1 @@
1.8.0 1.8.2

View file

@ -97,7 +97,6 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
Type string Type string
Name string Name string
Constant bool Constant bool
Indexed bool
Anonymous bool Anonymous bool
Inputs []Argument Inputs []Argument
Outputs []Argument Outputs []Argument

View file

@ -52,12 +52,6 @@ type ContractCaller interface {
CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error)
} }
// DeployBackend wraps the operations needed by WaitMined and WaitDeployed.
type DeployBackend interface {
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error)
}
// PendingContractCaller defines methods to perform contract calls on the pending state. // PendingContractCaller defines methods to perform contract calls on the pending state.
// Call will try to discover this interface when access to the pending state is requested. // Call will try to discover this interface when access to the pending state is requested.
// If the backend does not support the pending state, Call returns ErrNoPendingState. // If the backend does not support the pending state, Call returns ErrNoPendingState.
@ -90,8 +84,29 @@ type ContractTransactor interface {
SendTransaction(ctx context.Context, tx *types.Transaction) error SendTransaction(ctx context.Context, tx *types.Transaction) error
} }
// ContractFilterer defines the methods needed to access log events using one-off
// queries or continuous event subscriptions.
type ContractFilterer interface {
// FilterLogs executes a log filter operation, blocking during execution and
// returning all the results in one batch.
//
// TODO(karalabe): Deprecate when the subscription one can return past data too.
FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error)
// SubscribeFilterLogs creates a background log filtering operation, returning
// a subscription immediately, which can be used to stream the found events.
SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error)
}
// DeployBackend wraps the operations needed by WaitMined and WaitDeployed.
type DeployBackend interface {
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error)
}
// ContractBackend defines the methods needed to work with contracts on a read-write basis. // ContractBackend defines the methods needed to work with contracts on a read-write basis.
type ContractBackend interface { type ContractBackend interface {
ContractCaller ContractCaller
ContractTransactor ContractTransactor
ContractFilterer
} }

View file

@ -30,11 +30,15 @@ import (
"github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
) )
// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend. // This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
@ -53,6 +57,8 @@ type SimulatedBackend struct {
pendingBlock *types.Block // Currently pending block that will be imported on request pendingBlock *types.Block // Currently pending block that will be imported on request
pendingState *state.StateDB // Currently pending state that will be the active on on request pendingState *state.StateDB // Currently pending state that will be the active on on request
events *filters.EventSystem // Event system for filtering log events live
config *params.ChainConfig config *params.ChainConfig
} }
@ -62,8 +68,14 @@ func NewSimulatedBackend(alloc core.GenesisAlloc) *SimulatedBackend {
database, _ := ethdb.NewMemDatabase() database, _ := ethdb.NewMemDatabase()
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, Alloc: alloc} genesis := core.Genesis{Config: params.AllEthashProtocolChanges, Alloc: alloc}
genesis.MustCommit(database) genesis.MustCommit(database)
blockchain, _ := core.NewBlockChain(database, genesis.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{})
backend := &SimulatedBackend{database: database, blockchain: blockchain, config: genesis.Config}
backend := &SimulatedBackend{
database: database,
blockchain: blockchain,
config: genesis.Config,
events: filters.NewEventSystem(new(event.TypeMux), &filterBackend{database, blockchain}, false),
}
backend.rollback() backend.rollback()
return backend return backend
} }
@ -90,8 +102,10 @@ func (b *SimulatedBackend) Rollback() {
func (b *SimulatedBackend) rollback() { func (b *SimulatedBackend) rollback() {
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {}) blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {})
statedb, _ := b.blockchain.State()
b.pendingBlock = blocks[0] b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), state.NewDatabase(b.database)) b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
} }
// CodeAt returns the code associated with a certain account in the blockchain. // CodeAt returns the code associated with a certain account in the blockchain.
@ -248,7 +262,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
return hi, nil return hi, nil
} }
// callContract implemens common code between normal and pending contract calls. // callContract implements common code between normal and pending contract calls.
// state is modified during execution, make sure to copy it if necessary. // state is modified during execution, make sure to copy it if necessary.
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, uint64, bool, error) { func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, uint64, bool, error) {
// Ensure message is initialized properly. // Ensure message is initialized properly.
@ -297,12 +311,76 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
} }
block.AddTx(tx) block.AddTx(tx)
}) })
statedb, _ := b.blockchain.State()
b.pendingBlock = blocks[0] b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), state.NewDatabase(b.database)) b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
return nil return nil
} }
// JumpTimeInSeconds adds skip seconds to the clock // FilterLogs executes a log filter operation, blocking during execution and
// returning all the results in one batch.
//
// TODO(karalabe): Deprecate when the subscription one can return past data too.
func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) {
// Initialize unset filter boundaried to run from genesis to chain head
from := int64(0)
if query.FromBlock != nil {
from = query.FromBlock.Int64()
}
to := int64(-1)
if query.ToBlock != nil {
to = query.ToBlock.Int64()
}
// Construct and execute the filter
filter := filters.New(&filterBackend{b.database, b.blockchain}, from, to, query.Addresses, query.Topics)
logs, err := filter.Logs(ctx)
if err != nil {
return nil, err
}
res := make([]types.Log, len(logs))
for i, log := range logs {
res[i] = *log
}
return res, nil
}
// SubscribeFilterLogs creates a background log filtering operation, returning a
// subscription immediately, which can be used to stream the found events.
func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
// Subscribe to contract events
sink := make(chan []*types.Log)
sub, err := b.events.SubscribeLogs(query, sink)
if err != nil {
return nil, err
}
// Since we're getting logs in batches, we need to flatten them into a plain stream
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case logs := <-sink:
for _, log := range logs {
select {
case ch <- *log:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// AdjustTime adds a time shift to the simulated clock.
func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error { func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
@ -312,8 +390,10 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
} }
block.OffsetTime(int64(adjustment.Seconds())) block.OffsetTime(int64(adjustment.Seconds()))
}) })
statedb, _ := b.blockchain.State()
b.pendingBlock = blocks[0] b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), state.NewDatabase(b.database)) b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
return nil return nil
} }
@ -331,3 +411,44 @@ func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
func (m callmsg) Gas() uint64 { return m.CallMsg.Gas } func (m callmsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callmsg) Value() *big.Int { return m.CallMsg.Value } func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
func (m callmsg) Data() []byte { return m.CallMsg.Data } func (m callmsg) Data() []byte { return m.CallMsg.Data }
// filterBackend implements filters.Backend to support filtering for logs without
// taking bloom-bits acceleration structures into account.
type filterBackend struct {
db ethdb.Database
bc *core.BlockChain
}
func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db }
func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") }
func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumber) (*types.Header, error) {
if block == rpc.LatestBlockNumber {
return fb.bc.CurrentHeader(), nil
}
return fb.bc.GetHeaderByNumber(uint64(block.Int64())), nil
}
func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
return core.GetBlockReceipts(fb.db, hash, core.GetBlockNumber(fb.db, hash)), nil
}
func (fb *filterBackend) SubscribeTxPreEvent(ch chan<- core.TxPreEvent) event.Subscription {
return event.NewSubscription(func(quit <-chan struct{}) error {
<-quit
return nil
})
}
func (fb *filterBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
return fb.bc.SubscribeChainEvent(ch)
}
func (fb *filterBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
return fb.bc.SubscribeRemovedLogsEvent(ch)
}
func (fb *filterBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
return fb.bc.SubscribeLogsEvent(ch)
}
func (fb *filterBackend) BloomStatus() (uint64, uint64) { return 4096, 0 }
func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.MatcherSession) {
panic("not supported")
}

View file

@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event"
) )
// SignerFn is a signer function callback when a contract requires a method to // SignerFn is a signer function callback when a contract requires a method to
@ -55,6 +56,22 @@ type TransactOpts struct {
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
} }
// FilterOpts is the collection of options to fine tune filtering for events
// within a bound contract.
type FilterOpts struct {
Start uint64 // Start of the queried range
End *uint64 // End of the range (nil = latest)
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
}
// WatchOpts is the collection of options to fine tune subscribing for events
// within a bound contract.
type WatchOpts struct {
Start *uint64 // Start of the queried range (nil = latest)
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
}
// BoundContract is the base wrapper object that reflects a contract on the // BoundContract is the base wrapper object that reflects a contract on the
// Ethereum network. It contains a collection of methods that are used by the // Ethereum network. It contains a collection of methods that are used by the
// higher level contract bindings to operate. // higher level contract bindings to operate.
@ -63,16 +80,18 @@ type BoundContract struct {
abi abi.ABI // Reflect based ABI to access the correct Ethereum methods abi abi.ABI // Reflect based ABI to access the correct Ethereum methods
caller ContractCaller // Read interface to interact with the blockchain caller ContractCaller // Read interface to interact with the blockchain
transactor ContractTransactor // Write interface to interact with the blockchain transactor ContractTransactor // Write interface to interact with the blockchain
filterer ContractFilterer // Event filtering to interact with the blockchain
} }
// NewBoundContract creates a low level contract interface through which calls // NewBoundContract creates a low level contract interface through which calls
// and transactions may be made through. // and transactions may be made through.
func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor) *BoundContract { func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller, transactor ContractTransactor, filterer ContractFilterer) *BoundContract {
return &BoundContract{ return &BoundContract{
address: address, address: address,
abi: abi, abi: abi,
caller: caller, caller: caller,
transactor: transactor, transactor: transactor,
filterer: filterer,
} }
} }
@ -80,7 +99,7 @@ func NewBoundContract(address common.Address, abi abi.ABI, caller ContractCaller
// deployment address with a Go wrapper. // deployment address with a Go wrapper.
func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (common.Address, *types.Transaction, *BoundContract, error) { func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend ContractBackend, params ...interface{}) (common.Address, *types.Transaction, *BoundContract, error) {
// Otherwise try to deploy the contract // Otherwise try to deploy the contract
c := NewBoundContract(common.Address{}, abi, backend, backend) c := NewBoundContract(common.Address{}, abi, backend, backend, backend)
input, err := c.abi.Pack("", params...) input, err := c.abi.Pack("", params...)
if err != nil { if err != nil {
@ -225,6 +244,104 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
return signedTx, nil return signedTx, nil
} }
// FilterLogs filters contract logs for past blocks, returning the necessary
// channels to construct a strongly typed bound iterator on top of them.
func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
// Don't crash on a lazy user
if opts == nil {
opts = new(FilterOpts)
}
// Append the event selector to the query parameters and construct the topic set
query = append([][]interface{}{{c.abi.Events[name].Id()}}, query...)
topics, err := makeTopics(query...)
if err != nil {
return nil, nil, err
}
// Start the background filtering
logs := make(chan types.Log, 128)
config := ethereum.FilterQuery{
Addresses: []common.Address{c.address},
Topics: topics,
FromBlock: new(big.Int).SetUint64(opts.Start),
}
if opts.End != nil {
config.ToBlock = new(big.Int).SetUint64(*opts.End)
}
/* TODO(karalabe): Replace the rest of the method below with this when supported
sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
*/
buff, err := c.filterer.FilterLogs(ensureContext(opts.Context), config)
if err != nil {
return nil, nil, err
}
sub, err := event.NewSubscription(func(quit <-chan struct{}) error {
for _, log := range buff {
select {
case logs <- log:
case <-quit:
return nil
}
}
return nil
}), nil
if err != nil {
return nil, nil, err
}
return logs, sub, nil
}
// WatchLogs filters subscribes to contract logs for future blocks, returning a
// subscription object that can be used to tear down the watcher.
func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]interface{}) (chan types.Log, event.Subscription, error) {
// Don't crash on a lazy user
if opts == nil {
opts = new(WatchOpts)
}
// Append the event selector to the query parameters and construct the topic set
query = append([][]interface{}{{c.abi.Events[name].Id()}}, query...)
topics, err := makeTopics(query...)
if err != nil {
return nil, nil, err
}
// Start the background filtering
logs := make(chan types.Log, 128)
config := ethereum.FilterQuery{
Addresses: []common.Address{c.address},
Topics: topics,
}
if opts.Start != nil {
config.FromBlock = new(big.Int).SetUint64(*opts.Start)
}
sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
if err != nil {
return nil, nil, err
}
return logs, sub, nil
}
// UnpackLog unpacks a retrieved log into the provided output structure.
func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error {
if len(log.Data) > 0 {
if err := c.abi.Unpack(out, event, log.Data); err != nil {
return err
}
}
var indexed abi.Arguments
for _, arg := range c.abi.Events[event].Inputs {
if arg.Indexed {
indexed = append(indexed, arg)
}
}
return parseTopics(out, indexed, log.Topics[1:])
}
// ensureContext is a helper method to ensure a context is not nil, even if the
// user specified it as such.
func ensureContext(ctx context.Context) context.Context { func ensureContext(ctx context.Context) context.Context {
if ctx == nil { if ctx == nil {
return context.TODO() return context.TODO()

View file

@ -63,10 +63,11 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
return r return r
}, abis[i]) }, abis[i])
// Extract the call and transact methods, and sort them alphabetically // Extract the call and transact methods; events; and sort them alphabetically
var ( var (
calls = make(map[string]*tmplMethod) calls = make(map[string]*tmplMethod)
transacts = make(map[string]*tmplMethod) transacts = make(map[string]*tmplMethod)
events = make(map[string]*tmplEvent)
) )
for _, original := range evmABI.Methods { for _, original := range evmABI.Methods {
// Normalize the method for capital cases and non-anonymous inputs/outputs // Normalize the method for capital cases and non-anonymous inputs/outputs
@ -89,11 +90,33 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
} }
// Append the methods to the call or transact lists // Append the methods to the call or transact lists
if original.Const { if original.Const {
calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original)} calls[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
} else { } else {
transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original)} transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
} }
} }
for _, original := range evmABI.Events {
// Skip anonymous events as they don't support explicit filtering
if original.Anonymous {
continue
}
// Normalize the event for capital cases and non-anonymous outputs
normalized := original
normalized.Name = methodNormalizer[lang](original.Name)
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
copy(normalized.Inputs, original.Inputs)
for j, input := range normalized.Inputs {
// Indexed fields are input, non-indexed ones are outputs
if input.Indexed {
if input.Name == "" {
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
}
}
}
// Append the event to the accumulator list
events[original.Name] = &tmplEvent{Original: original, Normalized: normalized}
}
contracts[types[i]] = &tmplContract{ contracts[types[i]] = &tmplContract{
Type: capitalise(types[i]), Type: capitalise(types[i]),
InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1), InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1),
@ -101,6 +124,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
Constructor: evmABI.Constructor, Constructor: evmABI.Constructor,
Calls: calls, Calls: calls,
Transacts: transacts, Transacts: transacts,
Events: events,
} }
} }
// Generate the contract template data content and render it // Generate the contract template data content and render it
@ -111,10 +135,11 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
buffer := new(bytes.Buffer) buffer := new(bytes.Buffer)
funcs := map[string]interface{}{ funcs := map[string]interface{}{
"bindtype": bindType[lang], "bindtype": bindType[lang],
"namedtype": namedType[lang], "bindtopictype": bindTopicType[lang],
"capitalise": capitalise, "namedtype": namedType[lang],
"decapitalise": decapitalise, "capitalise": capitalise,
"decapitalise": decapitalise,
} }
tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang])) tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
if err := tmpl.Execute(buffer, data); err != nil { if err := tmpl.Execute(buffer, data); err != nil {
@ -133,7 +158,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
} }
// bindType is a set of type binders that convert Solidity types to some supported // bindType is a set of type binders that convert Solidity types to some supported
// programming language. // programming language types.
var bindType = map[Lang]func(kind abi.Type) string{ var bindType = map[Lang]func(kind abi.Type) string{
LangGo: bindTypeGo, LangGo: bindTypeGo,
LangJava: bindTypeJava, LangJava: bindTypeJava,
@ -254,6 +279,33 @@ func bindTypeJava(kind abi.Type) string {
} }
} }
// bindTopicType is a set of type binders that convert Solidity types to some
// supported programming language topic types.
var bindTopicType = map[Lang]func(kind abi.Type) string{
LangGo: bindTopicTypeGo,
LangJava: bindTopicTypeJava,
}
// bindTypeGo converts a Solidity topic type to a Go one. It is almost the same
// funcionality as for simple types, but dynamic types get converted to hashes.
func bindTopicTypeGo(kind abi.Type) string {
bound := bindTypeGo(kind)
if bound == "string" || bound == "[]byte" {
bound = "common.Hash"
}
return bound
}
// bindTypeGo converts a Solidity topic type to a Java one. It is almost the same
// funcionality as for simple types, but dynamic types get converted to hashes.
func bindTopicTypeJava(kind abi.Type) string {
bound := bindTypeJava(kind)
if bound == "String" || bound == "Bytes" {
bound = "Hash"
}
return bound
}
// namedType is a set of functions that transform language specific types to // namedType is a set of functions that transform language specific types to
// named versions that my be used inside method names. // named versions that my be used inside method names.
var namedType = map[Lang]func(string, abi.Type) string{ var namedType = map[Lang]func(string, abi.Type) string{
@ -321,14 +373,14 @@ func decapitalise(input string) string {
return strings.ToLower(input[:1]) + input[1:] return strings.ToLower(input[:1]) + input[1:]
} }
// structured checks whether a method has enough information to return a proper // structured checks whether a list of ABI data types has enough information to
// Go struct or if flat returns are needed. // operate through a proper Go struct or if flat returns are needed.
func structured(method abi.Method) bool { func structured(args abi.Arguments) bool {
if len(method.Outputs) < 2 { if len(args) < 2 {
return false return false
} }
exists := make(map[string]bool) exists := make(map[string]bool)
for _, out := range method.Outputs { for _, out := range args {
// If the name is anonymous, we can't organize into a struct // If the name is anonymous, we can't organize into a struct
if out.Name == "" { if out.Name == "" {
return false return false

View file

@ -148,6 +148,64 @@ var bindTests = []struct {
fmt.Println(str1, str2, res.Str1, res.Str2, err) fmt.Println(str1, str2, res.Str1, res.Str2, err)
}`, }`,
}, },
// Tests that named, anonymous and indexed events are handled correctly
{
`EventChecker`, ``, ``,
`
[
{"type":"event","name":"empty","inputs":[]},
{"type":"event","name":"indexed","inputs":[{"name":"addr","type":"address","indexed":true},{"name":"num","type":"int256","indexed":true}]},
{"type":"event","name":"mixed","inputs":[{"name":"addr","type":"address","indexed":true},{"name":"num","type":"int256"}]},
{"type":"event","name":"anonymous","anonymous":true,"inputs":[]},
{"type":"event","name":"dynamic","inputs":[{"name":"idxStr","type":"string","indexed":true},{"name":"idxDat","type":"bytes","indexed":true},{"name":"str","type":"string"},{"name":"dat","type":"bytes"}]}
]
`,
`if e, err := NewEventChecker(common.Address{}, nil); e == nil || err != nil {
t.Fatalf("binding (%v) nil or error (%v) not nil", e, nil)
} else if false { // Don't run, just compile and test types
var (
err error
res bool
str string
dat []byte
hash common.Hash
)
_, err = e.FilterEmpty(nil)
_, err = e.FilterIndexed(nil, []common.Address{}, []*big.Int{})
mit, err := e.FilterMixed(nil, []common.Address{})
res = mit.Next() // Make sure the iterator has a Next method
err = mit.Error() // Make sure the iterator has an Error method
err = mit.Close() // Make sure the iterator has a Close method
fmt.Println(mit.Event.Raw.BlockHash) // Make sure the raw log is contained within the results
fmt.Println(mit.Event.Num) // Make sure the unpacked non-indexed fields are present
fmt.Println(mit.Event.Addr) // Make sure the reconstructed indexed fields are present
dit, err := e.FilterDynamic(nil, []string{}, [][]byte{})
str = dit.Event.Str // Make sure non-indexed strings retain their type
dat = dit.Event.Dat // Make sure non-indexed bytes retain their type
hash = dit.Event.IdxStr // Make sure indexed strings turn into hashes
hash = dit.Event.IdxDat // Make sure indexed bytes turn into hashes
sink := make(chan *EventCheckerMixed)
sub, err := e.WatchMixed(nil, sink, []common.Address{})
defer sub.Unsubscribe()
event := <-sink
fmt.Println(event.Raw.BlockHash) // Make sure the raw log is contained within the results
fmt.Println(event.Num) // Make sure the unpacked non-indexed fields are present
fmt.Println(event.Addr) // Make sure the reconstructed indexed fields are present
fmt.Println(res, str, dat, hash, err)
}
// Run a tiny reflection test to ensure disallowed methods don't appear
if _, ok := reflect.TypeOf(&EventChecker{}).MethodByName("FilterAnonymous"); ok {
t.Errorf("binding has disallowed method (FilterAnonymous)")
}`,
},
// Test that contract interactions (deploy, transact and call) generate working code // Test that contract interactions (deploy, transact and call) generate working code
{ {
`Interactor`, `Interactor`,
@ -508,6 +566,177 @@ var bindTests = []struct {
fmt.Println(a, b, err) fmt.Println(a, b, err)
`, `,
}, },
// Tests that logs can be successfully filtered and decoded.
{
`Eventer`,
`
contract Eventer {
event SimpleEvent (
address indexed Addr,
bytes32 indexed Id,
bool indexed Flag,
uint Value
);
function raiseSimpleEvent(address addr, bytes32 id, bool flag, uint value) {
SimpleEvent(addr, id, flag, value);
}
event NodataEvent (
uint indexed Number,
int16 indexed Short,
uint32 indexed Long
);
function raiseNodataEvent(uint number, int16 short, uint32 long) {
NodataEvent(number, short, long);
}
event DynamicEvent (
string indexed IndexedString,
bytes indexed IndexedBytes,
string NonIndexedString,
bytes NonIndexedBytes
);
function raiseDynamicEvent(string str, bytes blob) {
DynamicEvent(str, blob, str, blob);
}
}
`,
`6060604052341561000f57600080fd5b61042c8061001e6000396000f300606060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063528300ff1461005c578063630c31e2146100fc578063c7d116dd14610156575b600080fd5b341561006757600080fd5b6100fa600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610194565b005b341561010757600080fd5b610154600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035600019169060200190919080351515906020019091908035906020019091905050610367565b005b341561016157600080fd5b610192600480803590602001909190803560010b90602001909190803563ffffffff169060200190919050506103c3565b005b806040518082805190602001908083835b6020831015156101ca57805182526020820191506020810190506020830392506101a5565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020826040518082805190602001908083835b60208310151561022d5780518252602082019150602081019050602083039250610208565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f3281fd4f5e152dd3385df49104a3f633706e21c9e80672e88d3bcddf33101f008484604051808060200180602001838103835285818151815260200191508051906020019080838360005b838110156102c15780820151818401526020810190506102a6565b50505050905090810190601f1680156102ee5780820380516001836020036101000a031916815260200191505b50838103825284818151815260200191508051906020019080838360005b8381101561032757808201518184015260208101905061030c565b50505050905090810190601f1680156103545780820380516001836020036101000a031916815260200191505b5094505050505060405180910390a35050565b81151583600019168573ffffffffffffffffffffffffffffffffffffffff167f1f097de4289df643bd9c11011cc61367aa12983405c021056e706eb5ba1250c8846040518082815260200191505060405180910390a450505050565b8063ffffffff168260010b847f3ca7f3a77e5e6e15e781850bc82e32adfa378a2a609370db24b4d0fae10da2c960405160405180910390a45050505600a165627a7a72305820d1f8a8bbddbc5bb29f285891d6ae1eef8420c52afdc05e1573f6114d8e1714710029`,
`[{"constant":false,"inputs":[{"name":"str","type":"string"},{"name":"blob","type":"bytes"}],"name":"raiseDynamicEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"id","type":"bytes32"},{"name":"flag","type":"bool"},{"name":"value","type":"uint256"}],"name":"raiseSimpleEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"number","type":"uint256"},{"name":"short","type":"int16"},{"name":"long","type":"uint32"}],"name":"raiseNodataEvent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"Addr","type":"address"},{"indexed":true,"name":"Id","type":"bytes32"},{"indexed":true,"name":"Flag","type":"bool"},{"indexed":false,"name":"Value","type":"uint256"}],"name":"SimpleEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"Number","type":"uint256"},{"indexed":true,"name":"Short","type":"int16"},{"indexed":true,"name":"Long","type":"uint32"}],"name":"NodataEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"IndexedString","type":"string"},{"indexed":true,"name":"IndexedBytes","type":"bytes"},{"indexed":false,"name":"NonIndexedString","type":"string"},{"indexed":false,"name":"NonIndexedBytes","type":"bytes"}],"name":"DynamicEvent","type":"event"}]`,
`
// Generate a new random account and a funded simulator
key, _ := crypto.GenerateKey()
auth := bind.NewKeyedTransactor(key)
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}})
// Deploy an eventer contract
_, _, eventer, err := DeployEventer(auth, sim)
if err != nil {
t.Fatalf("Failed to deploy eventer contract: %v", err)
}
sim.Commit()
// Inject a few events into the contract, gradually more in each block
for i := 1; i <= 3; i++ {
for j := 1; j <= i; j++ {
if _, err := eventer.RaiseSimpleEvent(auth, common.Address{byte(j)}, [32]byte{byte(j)}, true, big.NewInt(int64(10*i+j))); err != nil {
t.Fatalf("block %d, event %d: raise failed: %v", i, j, err)
}
}
sim.Commit()
}
// Test filtering for certain events and ensure they can be found
sit, err := eventer.FilterSimpleEvent(nil, []common.Address{common.Address{1}, common.Address{3}}, [][32]byte{{byte(1)}, {byte(2)}, {byte(3)}}, []bool{true})
if err != nil {
t.Fatalf("failed to filter for simple events: %v", err)
}
defer sit.Close()
sit.Next()
if sit.Event.Value.Uint64() != 11 || !sit.Event.Flag {
t.Errorf("simple log content mismatch: have %v, want {11, true}", sit.Event)
}
sit.Next()
if sit.Event.Value.Uint64() != 21 || !sit.Event.Flag {
t.Errorf("simple log content mismatch: have %v, want {21, true}", sit.Event)
}
sit.Next()
if sit.Event.Value.Uint64() != 31 || !sit.Event.Flag {
t.Errorf("simple log content mismatch: have %v, want {31, true}", sit.Event)
}
sit.Next()
if sit.Event.Value.Uint64() != 33 || !sit.Event.Flag {
t.Errorf("simple log content mismatch: have %v, want {33, true}", sit.Event)
}
if sit.Next() {
t.Errorf("unexpected simple event found: %+v", sit.Event)
}
if err = sit.Error(); err != nil {
t.Fatalf("simple event iteration failed: %v", err)
}
// Test raising and filtering for an event with no data component
if _, err := eventer.RaiseNodataEvent(auth, big.NewInt(314), 141, 271); err != nil {
t.Fatalf("failed to raise nodata event: %v", err)
}
sim.Commit()
nit, err := eventer.FilterNodataEvent(nil, []*big.Int{big.NewInt(314)}, []int16{140, 141, 142}, []uint32{271})
if err != nil {
t.Fatalf("failed to filter for nodata events: %v", err)
}
defer nit.Close()
if !nit.Next() {
t.Fatalf("nodata log not found: %v", nit.Error())
}
if nit.Event.Number.Uint64() != 314 {
t.Errorf("nodata log content mismatch: have %v, want 314", nit.Event.Number)
}
if nit.Next() {
t.Errorf("unexpected nodata event found: %+v", nit.Event)
}
if err = nit.Error(); err != nil {
t.Fatalf("nodata event iteration failed: %v", err)
}
// Test raising and filtering for events with dynamic indexed components
if _, err := eventer.RaiseDynamicEvent(auth, "Hello", []byte("World")); err != nil {
t.Fatalf("failed to raise dynamic event: %v", err)
}
sim.Commit()
dit, err := eventer.FilterDynamicEvent(nil, []string{"Hi", "Hello", "Bye"}, [][]byte{[]byte("World")})
if err != nil {
t.Fatalf("failed to filter for dynamic events: %v", err)
}
defer dit.Close()
if !dit.Next() {
t.Fatalf("dynamic log not found: %v", dit.Error())
}
if dit.Event.NonIndexedString != "Hello" || string(dit.Event.NonIndexedBytes) != "World" || dit.Event.IndexedString != common.HexToHash("0x06b3dfaec148fb1bb2b066f10ec285e7c9bf402ab32aa78a5d38e34566810cd2") || dit.Event.IndexedBytes != common.HexToHash("0xf2208c967df089f60420785795c0a9ba8896b0f6f1867fa7f1f12ad6f79c1a18") {
t.Errorf("dynamic log content mismatch: have %v, want {'0x06b3dfaec148fb1bb2b066f10ec285e7c9bf402ab32aa78a5d38e34566810cd2, '0xf2208c967df089f60420785795c0a9ba8896b0f6f1867fa7f1f12ad6f79c1a18', 'Hello', 'World'}", dit.Event)
}
if dit.Next() {
t.Errorf("unexpected dynamic event found: %+v", dit.Event)
}
if err = dit.Error(); err != nil {
t.Fatalf("dynamic event iteration failed: %v", err)
}
// Test subscribing to an event and raising it afterwards
ch := make(chan *EventerSimpleEvent, 16)
sub, err := eventer.WatchSimpleEvent(nil, ch, nil, nil, nil)
if err != nil {
t.Fatalf("failed to subscribe to simple events: %v", err)
}
if _, err := eventer.RaiseSimpleEvent(auth, common.Address{255}, [32]byte{255}, true, big.NewInt(255)); err != nil {
t.Fatalf("failed to raise subscribed simple event: %v", err)
}
sim.Commit()
select {
case event := <-ch:
if event.Value.Uint64() != 255 {
t.Errorf("simple log content mismatch: have %v, want 255", event)
}
case <-time.After(250 * time.Millisecond):
t.Fatalf("subscribed simple event didn't arrive")
}
// Unsubscribe from the event and make sure we're not delivered more
sub.Unsubscribe()
if _, err := eventer.RaiseSimpleEvent(auth, common.Address{254}, [32]byte{254}, true, big.NewInt(254)); err != nil {
t.Fatalf("failed to raise subscribed simple event: %v", err)
}
sim.Commit()
select {
case event := <-ch:
t.Fatalf("unsubscribed simple event arrived: %v", event)
case <-time.After(250 * time.Millisecond):
}
`,
},
} }
// Tests that packages generated by the binder can be successfully compiled and // Tests that packages generated by the binder can be successfully compiled and
@ -559,7 +788,7 @@ func TestBindings(t *testing.T) {
} }
} }
// Test the entire package and report any failures // Test the entire package and report any failures
cmd := exec.Command(gocmd, "test", "-v") cmd := exec.Command(gocmd, "test", "-v", "-count", "1")
cmd.Dir = pkg cmd.Dir = pkg
if out, err := cmd.CombinedOutput(); err != nil { if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("failed to run binding test: %v\n%s", err, out) t.Fatalf("failed to run binding test: %v\n%s", err, out)

View file

@ -32,6 +32,7 @@ type tmplContract struct {
Constructor abi.Method // Contract constructor for deploy parametrization Constructor abi.Method // Contract constructor for deploy parametrization
Calls map[string]*tmplMethod // Contract calls that only read state data Calls map[string]*tmplMethod // Contract calls that only read state data
Transacts map[string]*tmplMethod // Contract calls that write state data Transacts map[string]*tmplMethod // Contract calls that write state data
Events map[string]*tmplEvent // Contract events accessors
} }
// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed // tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
@ -39,7 +40,13 @@ type tmplContract struct {
type tmplMethod struct { type tmplMethod struct {
Original abi.Method // Original method as parsed by the abi package Original abi.Method // Original method as parsed by the abi package
Normalized abi.Method // Normalized version of the parsed method (capitalized names, non-anonymous args/returns) Normalized abi.Method // Normalized version of the parsed method (capitalized names, non-anonymous args/returns)
Structured bool // Whether the returns should be accumulated into a contract Structured bool // Whether the returns should be accumulated into a struct
}
// tmplEvent is a wrapper around an a
type tmplEvent struct {
Original abi.Event // Original event as parsed by the abi package
Normalized abi.Event // Normalized version of the parsed fields
} }
// tmplSource is language to template mapping containing all the supported // tmplSource is language to template mapping containing all the supported
@ -75,7 +82,7 @@ package {{.Package}}
if err != nil { if err != nil {
return common.Address{}, nil, nil, err return common.Address{}, nil, nil, err
} }
return address, tx, &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract} }, nil return address, tx, &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
} }
{{end}} {{end}}
@ -83,6 +90,7 @@ package {{.Package}}
type {{.Type}} struct { type {{.Type}} struct {
{{.Type}}Caller // Read-only binding to the contract {{.Type}}Caller // Read-only binding to the contract
{{.Type}}Transactor // Write-only binding to the contract {{.Type}}Transactor // Write-only binding to the contract
{{.Type}}Filterer // Log filterer for contract events
} }
// {{.Type}}Caller is an auto generated read-only Go binding around an Ethereum contract. // {{.Type}}Caller is an auto generated read-only Go binding around an Ethereum contract.
@ -95,6 +103,11 @@ package {{.Package}}
contract *bind.BoundContract // Generic contract wrapper for the low level calls contract *bind.BoundContract // Generic contract wrapper for the low level calls
} }
// {{.Type}}Filterer is an auto generated log filtering Go binding around an Ethereum contract events.
type {{.Type}}Filterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// {{.Type}}Session is an auto generated Go binding around an Ethereum contract, // {{.Type}}Session is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options. // with pre-set call and transact options.
type {{.Type}}Session struct { type {{.Type}}Session struct {
@ -134,16 +147,16 @@ package {{.Package}}
// New{{.Type}} creates a new instance of {{.Type}}, bound to a specific deployed contract. // New{{.Type}} creates a new instance of {{.Type}}, bound to a specific deployed contract.
func New{{.Type}}(address common.Address, backend bind.ContractBackend) (*{{.Type}}, error) { func New{{.Type}}(address common.Address, backend bind.ContractBackend) (*{{.Type}}, error) {
contract, err := bind{{.Type}}(address, backend, backend) contract, err := bind{{.Type}}(address, backend, backend, backend)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract} }, nil return &{{.Type}}{ {{.Type}}Caller: {{.Type}}Caller{contract: contract}, {{.Type}}Transactor: {{.Type}}Transactor{contract: contract}, {{.Type}}Filterer: {{.Type}}Filterer{contract: contract} }, nil
} }
// New{{.Type}}Caller creates a new read-only instance of {{.Type}}, bound to a specific deployed contract. // New{{.Type}}Caller creates a new read-only instance of {{.Type}}, bound to a specific deployed contract.
func New{{.Type}}Caller(address common.Address, caller bind.ContractCaller) (*{{.Type}}Caller, error) { func New{{.Type}}Caller(address common.Address, caller bind.ContractCaller) (*{{.Type}}Caller, error) {
contract, err := bind{{.Type}}(address, caller, nil) contract, err := bind{{.Type}}(address, caller, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -152,20 +165,29 @@ package {{.Package}}
// New{{.Type}}Transactor creates a new write-only instance of {{.Type}}, bound to a specific deployed contract. // New{{.Type}}Transactor creates a new write-only instance of {{.Type}}, bound to a specific deployed contract.
func New{{.Type}}Transactor(address common.Address, transactor bind.ContractTransactor) (*{{.Type}}Transactor, error) { func New{{.Type}}Transactor(address common.Address, transactor bind.ContractTransactor) (*{{.Type}}Transactor, error) {
contract, err := bind{{.Type}}(address, nil, transactor) contract, err := bind{{.Type}}(address, nil, transactor, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &{{.Type}}Transactor{contract: contract}, nil return &{{.Type}}Transactor{contract: contract}, nil
} }
// New{{.Type}}Filterer creates a new log filterer instance of {{.Type}}, bound to a specific deployed contract.
func New{{.Type}}Filterer(address common.Address, filterer bind.ContractFilterer) (*{{.Type}}Filterer, error) {
contract, err := bind{{.Type}}(address, nil, nil, filterer)
if err != nil {
return nil, err
}
return &{{.Type}}Filterer{contract: contract}, nil
}
// bind{{.Type}} binds a generic wrapper to an already deployed contract. // bind{{.Type}} binds a generic wrapper to an already deployed contract.
func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI)) parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI))
if err != nil { if err != nil {
return nil, err return nil, err
} }
return bind.NewBoundContract(address, parsed, caller, transactor), nil return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
} }
// Call invokes the (constant) contract method with params as input values and // Call invokes the (constant) contract method with params as input values and
@ -263,6 +285,137 @@ package {{.Package}}
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}}) return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
} }
{{end}} {{end}}
{{range .Events}}
// {{$contract.Type}}{{.Normalized.Name}}Iterator is returned from Filter{{.Normalized.Name}} and is used to iterate over the raw logs and unpacked data for {{.Normalized.Name}} events raised by the {{$contract.Type}} contract.
type {{$contract.Type}}{{.Normalized.Name}}Iterator struct {
Event *{{$contract.Type}}{{.Normalized.Name}} // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Next() bool {
// If the iterator failed, stop iterating
if (it.fail != nil) {
return false
}
// If the iterator completed, deliver directly whatever's available
if (it.done) {
select {
case log := <-it.logs:
it.Event = new({{$contract.Type}}{{.Normalized.Name}})
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new({{$contract.Type}}{{.Normalized.Name}})
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error returns any retrieval or parsing error occurred during filtering.
func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *{{$contract.Type}}{{.Normalized.Name}}Iterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// {{$contract.Type}}{{.Normalized.Name}} represents a {{.Normalized.Name}} event raised by the {{$contract.Type}} contract.
type {{$contract.Type}}{{.Normalized.Name}} struct { {{range .Normalized.Inputs}}
{{capitalise .Name}} {{if .Indexed}}{{bindtopictype .Type}}{{else}}{{bindtype .Type}}{{end}}; {{end}}
Raw types.Log // Blockchain specific contextual infos
}
// Filter{{.Normalized.Name}} is a free log retrieval operation binding the contract event 0x{{printf "%x" .Original.Id}}.
//
// Solidity: {{.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Filter{{.Normalized.Name}}(opts *bind.FilterOpts{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type}}{{end}}{{end}}) (*{{$contract.Type}}{{.Normalized.Name}}Iterator, error) {
{{range .Normalized.Inputs}}
{{if .Indexed}}var {{.Name}}Rule []interface{}
for _, {{.Name}}Item := range {{.Name}} {
{{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
}{{end}}{{end}}
logs, sub, err := _{{$contract.Type}}.contract.FilterLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
if err != nil {
return nil, err
}
return &{{$contract.Type}}{{.Normalized.Name}}Iterator{contract: _{{$contract.Type}}.contract, event: "{{.Original.Name}}", logs: logs, sub: sub}, nil
}
// Watch{{.Normalized.Name}} is a free log subscription operation binding the contract event 0x{{printf "%x" .Original.Id}}.
//
// Solidity: {{.Original.String}}
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Watch{{.Normalized.Name}}(opts *bind.WatchOpts, sink chan<- *{{$contract.Type}}{{.Normalized.Name}}{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type}}{{end}}{{end}}) (event.Subscription, error) {
{{range .Normalized.Inputs}}
{{if .Indexed}}var {{.Name}}Rule []interface{}
for _, {{.Name}}Item := range {{.Name}} {
{{.Name}}Rule = append({{.Name}}Rule, {{.Name}}Item)
}{{end}}{{end}}
logs, sub, err := _{{$contract.Type}}.contract.WatchLogs(opts, "{{.Original.Name}}"{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}}Rule{{end}}{{end}})
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new({{$contract.Type}}{{.Normalized.Name}})
if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
{{end}}
{{end}} {{end}}
` `

189
accounts/abi/bind/topics.go Normal file
View file

@ -0,0 +1,189 @@
// Copyright 2018 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 bind
import (
"errors"
"fmt"
"math/big"
"reflect"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
// makeTopics converts a filter query argument list into a filter topic set.
func makeTopics(query ...[]interface{}) ([][]common.Hash, error) {
topics := make([][]common.Hash, len(query))
for i, filter := range query {
for _, rule := range filter {
var topic common.Hash
// Try to generate the topic based on simple types
switch rule := rule.(type) {
case common.Hash:
copy(topic[:], rule[:])
case common.Address:
copy(topic[common.HashLength-common.AddressLength:], rule[:])
case *big.Int:
blob := rule.Bytes()
copy(topic[common.HashLength-len(blob):], blob)
case bool:
if rule {
topic[common.HashLength-1] = 1
}
case int8:
blob := big.NewInt(int64(rule)).Bytes()
copy(topic[common.HashLength-len(blob):], blob)
case int16:
blob := big.NewInt(int64(rule)).Bytes()
copy(topic[common.HashLength-len(blob):], blob)
case int32:
blob := big.NewInt(int64(rule)).Bytes()
copy(topic[common.HashLength-len(blob):], blob)
case int64:
blob := big.NewInt(rule).Bytes()
copy(topic[common.HashLength-len(blob):], blob)
case uint8:
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
copy(topic[common.HashLength-len(blob):], blob)
case uint16:
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
copy(topic[common.HashLength-len(blob):], blob)
case uint32:
blob := new(big.Int).SetUint64(uint64(rule)).Bytes()
copy(topic[common.HashLength-len(blob):], blob)
case uint64:
blob := new(big.Int).SetUint64(rule).Bytes()
copy(topic[common.HashLength-len(blob):], blob)
case string:
hash := crypto.Keccak256Hash([]byte(rule))
copy(topic[:], hash[:])
case []byte:
hash := crypto.Keccak256Hash(rule)
copy(topic[:], hash[:])
default:
// Attempt to generate the topic from funky types
val := reflect.ValueOf(rule)
switch {
case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8:
reflect.Copy(reflect.ValueOf(topic[common.HashLength-val.Len():]), val)
default:
return nil, fmt.Errorf("unsupported indexed type: %T", rule)
}
}
topics[i] = append(topics[i], topic)
}
}
return topics, nil
}
// Big batch of reflect types for topic reconstruction.
var (
reflectHash = reflect.TypeOf(common.Hash{})
reflectAddress = reflect.TypeOf(common.Address{})
reflectBigInt = reflect.TypeOf(new(big.Int))
)
// parseTopics converts the indexed topic fields into actual log field values.
//
// Note, dynamic types cannot be reconstructed since they get mapped to Keccak256
// hashes as the topic value!
func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) error {
// Sanity check that the fields and topics match up
if len(fields) != len(topics) {
return errors.New("topic/field count mismatch")
}
// Iterate over all the fields and reconstruct them from topics
for _, arg := range fields {
if !arg.Indexed {
return errors.New("non-indexed field in topic reconstruction")
}
field := reflect.ValueOf(out).Elem().FieldByName(capitalise(arg.Name))
// Try to parse the topic back into the fields based on primitive types
switch field.Kind() {
case reflect.Bool:
if topics[0][common.HashLength-1] == 1 {
field.Set(reflect.ValueOf(true))
}
case reflect.Int8:
num := new(big.Int).SetBytes(topics[0][:])
field.Set(reflect.ValueOf(int8(num.Int64())))
case reflect.Int16:
num := new(big.Int).SetBytes(topics[0][:])
field.Set(reflect.ValueOf(int16(num.Int64())))
case reflect.Int32:
num := new(big.Int).SetBytes(topics[0][:])
field.Set(reflect.ValueOf(int32(num.Int64())))
case reflect.Int64:
num := new(big.Int).SetBytes(topics[0][:])
field.Set(reflect.ValueOf(num.Int64()))
case reflect.Uint8:
num := new(big.Int).SetBytes(topics[0][:])
field.Set(reflect.ValueOf(uint8(num.Uint64())))
case reflect.Uint16:
num := new(big.Int).SetBytes(topics[0][:])
field.Set(reflect.ValueOf(uint16(num.Uint64())))
case reflect.Uint32:
num := new(big.Int).SetBytes(topics[0][:])
field.Set(reflect.ValueOf(uint32(num.Uint64())))
case reflect.Uint64:
num := new(big.Int).SetBytes(topics[0][:])
field.Set(reflect.ValueOf(num.Uint64()))
default:
// Ran out of plain primitive types, try custom types
switch field.Type() {
case reflectHash: // Also covers all dynamic types
field.Set(reflect.ValueOf(topics[0]))
case reflectAddress:
var addr common.Address
copy(addr[:], topics[0][common.HashLength-common.AddressLength:])
field.Set(reflect.ValueOf(addr))
case reflectBigInt:
num := new(big.Int).SetBytes(topics[0][:])
field.Set(reflect.ValueOf(num))
default:
// Ran out of custom types, try the crazies
switch {
case arg.Type.T == abi.FixedBytesTy:
reflect.Copy(field, reflect.ValueOf(topics[0][common.HashLength-arg.Type.Size:]))
default:
return fmt.Errorf("unsupported indexed type: %v", arg.Type)
}
}
}
topics = topics[1:]
}
return nil
}

View file

@ -33,6 +33,17 @@ type Event struct {
Inputs Arguments Inputs Arguments
} }
func (event Event) String() string {
inputs := make([]string, len(event.Inputs))
for i, input := range event.Inputs {
inputs[i] = fmt.Sprintf("%v %v", input.Name, input.Type)
if input.Indexed {
inputs[i] = fmt.Sprintf("%v indexed %v", input.Name, input.Type)
}
}
return fmt.Sprintf("event %v(%v)", event.Name, strings.Join(inputs, ", "))
}
// Id returns the canonical representation of the event's signature used by the // Id returns the canonical representation of the event's signature used by the
// abi definition to identify event names and types. // abi definition to identify event names and types.
func (e Event) Id() common.Hash { func (e Event) Id() common.Hash {

View file

@ -1,4 +1,4 @@
// Copyright 2015 The go-ethereum Authors // Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library. // This file is part of the go-ethereum library.
// //
// The go-ethereum library is free software: you can redistribute it and/or modify // The go-ethereum library is free software: you can redistribute it and/or modify

View file

@ -1,4 +1,4 @@
// Copyright 2015 The go-ethereum Authors // Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library. // This file is part of the go-ethereum library.
// //
// The go-ethereum library is free software: you can redistribute it and/or modify // The go-ethereum library is free software: you can redistribute it and/or modify

View file

@ -1,4 +1,4 @@
// Copyright 2015 The go-ethereum Authors // Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library. // This file is part of the go-ethereum library.
// //
// The go-ethereum library is free software: you can redistribute it and/or modify // The go-ethereum library is free software: you can redistribute it and/or modify

View file

@ -62,7 +62,7 @@ func NewAuthNeededError(needed string) error {
} }
} }
// Error implements the standard error interfacel. // Error implements the standard error interface.
func (err *AuthNeededError) Error() string { func (err *AuthNeededError) Error() string {
return fmt.Sprintf("authentication needed: %s", err.Needed) return fmt.Sprintf("authentication needed: %s", err.Needed)
} }

View file

@ -121,7 +121,8 @@ var (
// Note: vivid is unsupported because there is no golang-1.6 package for it. // Note: vivid is unsupported because there is no golang-1.6 package for it.
// Note: wily is unsupported because it was officially deprecated on lanchpad. // Note: wily is unsupported because it was officially deprecated on lanchpad.
// Note: yakkety is unsupported because it was officially deprecated on lanchpad. // Note: yakkety is unsupported because it was officially deprecated on lanchpad.
debDistros = []string{"trusty", "xenial", "zesty", "artful"} // Note: zesty is unsupported because it was officially deprecated on lanchpad.
debDistros = []string{"trusty", "xenial", "artful", "bionic"}
) )
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin")) var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))

View file

@ -55,10 +55,9 @@ var (
"crypto/sha3/", "crypto/sha3/",
"internal/jsre/deps", "internal/jsre/deps",
"log/", "log/",
"common/bitutil/bitutil",
// don't license generated files // don't license generated files
"contracts/chequebook/contract/", "contracts/chequebook/contract/code.go",
"contracts/ens/contract/",
"contracts/release/contract.go",
} }
// paths with this prefix are licensed as GPL. all other files are LGPL. // paths with this prefix are licensed as GPL. all other files are LGPL.

View file

@ -21,6 +21,7 @@ import (
"crypto/ecdsa" "crypto/ecdsa"
"flag" "flag"
"fmt" "fmt"
"net"
"os" "os"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
@ -96,12 +97,37 @@ func main() {
} }
} }
addr, err := net.ResolveUDPAddr("udp", *listenAddr)
if err != nil {
utils.Fatalf("-ResolveUDPAddr: %v", err)
}
conn, err := net.ListenUDP("udp", addr)
if err != nil {
utils.Fatalf("-ListenUDP: %v", err)
}
realaddr := conn.LocalAddr().(*net.UDPAddr)
if natm != nil {
if !realaddr.IP.IsLoopback() {
go nat.Map(natm, nil, "udp", realaddr.Port, realaddr.Port, "ethereum discovery")
}
// TODO: react to external IP changes over time.
if ext, err := natm.ExternalIP(); err == nil {
realaddr = &net.UDPAddr{IP: ext, Port: realaddr.Port}
}
}
if *runv5 { if *runv5 {
if _, err := discv5.ListenUDP(nodeKey, *listenAddr, natm, "", restrictList); err != nil { if _, err := discv5.ListenUDP(nodeKey, conn, realaddr, "", restrictList); err != nil {
utils.Fatalf("%v", err) utils.Fatalf("%v", err)
} }
} else { } else {
if _, err := discover.ListenUDP(nodeKey, *listenAddr, natm, "", restrictList); err != nil { cfg := discover.Config{
PrivateKey: nodeKey,
AnnounceAddr: realaddr,
NetRestrict: restrictList,
}
if _, err := discover.ListenUDP(conn, cfg); err != nil {
utils.Fatalf("%v", err) utils.Fatalf("%v", err)
} }
} }

View file

@ -1,8 +1,23 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main package main
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"crypto/rand"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@ -26,16 +41,16 @@ var commandGenerate = cli.Command{
ArgsUsage: "[ <keyfile> ]", ArgsUsage: "[ <keyfile> ]",
Description: ` Description: `
Generate a new keyfile. Generate a new keyfile.
If you want to use an existing private key to use in the keyfile, it can be
specified by setting --privatekey with the location of the file containing the If you want to encrypt an existing private key, it can be specified by setting
private key.`, --privatekey with the location of the file containing the private key.
`,
Flags: []cli.Flag{ Flags: []cli.Flag{
passphraseFlag, passphraseFlag,
jsonFlag, jsonFlag,
cli.StringFlag{ cli.StringFlag{
Name: "privatekey", Name: "privatekey",
Usage: "the file from where to read the private key to " + Usage: "file containing a raw private key to encrypt",
"generate a keyfile for",
}, },
}, },
Action: func(ctx *cli.Context) error { Action: func(ctx *cli.Context) error {
@ -51,32 +66,19 @@ private key.`,
} }
var privateKey *ecdsa.PrivateKey var privateKey *ecdsa.PrivateKey
var err error
// First check if a private key file is provided. if file := ctx.String("privatekey"); file != "" {
privateKeyFile := ctx.String("privatekey") // Load private key from file.
if privateKeyFile != "" { privateKey, err = crypto.LoadECDSA(file)
privateKeyBytes, err := ioutil.ReadFile(privateKeyFile)
if err != nil { if err != nil {
utils.Fatalf("Failed to read the private key file '%s': %v", utils.Fatalf("Can't load private key: %v", err)
privateKeyFile, err)
} }
} else {
pk, err := crypto.HexToECDSA(string(privateKeyBytes)) // If not loaded, generate random.
if err != nil { privateKey, err = crypto.GenerateKey()
utils.Fatalf(
"Could not construct ECDSA private key from file content: %v",
err)
}
privateKey = pk
}
// If not loaded, generate random.
if privateKey == nil {
pk, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
if err != nil { if err != nil {
utils.Fatalf("Failed to generate random private key: %v", err) utils.Fatalf("Failed to generate random private key: %v", err)
} }
privateKey = pk
} }
// Create the keyfile object with a random UUID. // Create the keyfile object with a random UUID.
@ -89,8 +91,7 @@ private key.`,
// Encrypt key with passphrase. // Encrypt key with passphrase.
passphrase := getPassPhrase(ctx, true) passphrase := getPassPhrase(ctx, true)
keyjson, err := keystore.EncryptKey(key, passphrase, keyjson, err := keystore.EncryptKey(key, passphrase, keystore.StandardScryptN, keystore.StandardScryptP)
keystore.StandardScryptN, keystore.StandardScryptP)
if err != nil { if err != nil {
utils.Fatalf("Error encrypting key: %v", err) utils.Fatalf("Error encrypting key: %v", err)
} }
@ -110,7 +111,7 @@ private key.`,
if ctx.Bool(jsonFlag.Name) { if ctx.Bool(jsonFlag.Name) {
mustPrintJSON(out) mustPrintJSON(out)
} else { } else {
fmt.Println("Address: ", out.Address) fmt.Println("Address:", out.Address)
} }
return nil return nil
}, },

View file

@ -1,3 +1,19 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main package main
import ( import (
@ -23,6 +39,7 @@ var commandInspect = cli.Command{
ArgsUsage: "<keyfile>", ArgsUsage: "<keyfile>",
Description: ` Description: `
Print various information about the keyfile. Print various information about the keyfile.
Private key information can be printed by using the --private flag; Private key information can be printed by using the --private flag;
make sure to use this feature with great caution!`, make sure to use this feature with great caution!`,
Flags: []cli.Flag{ Flags: []cli.Flag{

View file

@ -28,30 +28,11 @@ const (
defaultKeyfileName = "keyfile.json" defaultKeyfileName = "keyfile.json"
) )
var ( // Git SHA1 commit hash of the release (set via linker flags)
gitCommit = "" // Git SHA1 commit hash of the release (set via linker flags) var gitCommit = ""
app *cli.App // the main app instance var app *cli.App
)
var ( // Commonly used command line flags.
passphraseFlag = cli.StringFlag{
Name: "passwordfile",
Usage: "the file that contains the passphrase for the keyfile",
}
jsonFlag = cli.BoolFlag{
Name: "json",
Usage: "output JSON instead of human-readable format",
}
messageFlag = cli.StringFlag{
Name: "message",
Usage: "the file that contains the message to sign/verify",
}
)
// Configure the app instance.
func init() { func init() {
app = utils.NewApp(gitCommit, "an Ethereum key manager") app = utils.NewApp(gitCommit, "an Ethereum key manager")
app.Commands = []cli.Command{ app.Commands = []cli.Command{
@ -62,6 +43,22 @@ func init() {
} }
} }
// Commonly used command line flags.
var (
passphraseFlag = cli.StringFlag{
Name: "passwordfile",
Usage: "the file that contains the passphrase for the keyfile",
}
jsonFlag = cli.BoolFlag{
Name: "json",
Usage: "output JSON instead of human-readable format",
}
messageFlag = cli.StringFlag{
Name: "message",
Usage: "the file that contains the message to sign/verify",
}
)
func main() { func main() {
if err := app.Run(os.Args); err != nil { if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)

View file

@ -1,11 +1,25 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main package main
import ( import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os"
"strings"
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
@ -18,26 +32,33 @@ type outputSign struct {
Signature string Signature string
} }
var msgfileFlag = cli.StringFlag{
Name: "msgfile",
Usage: "file containing the message to sign/verify",
}
var commandSignMessage = cli.Command{ var commandSignMessage = cli.Command{
Name: "signmessage", Name: "signmessage",
Usage: "sign a message", Usage: "sign a message",
ArgsUsage: "<keyfile> <message/file>", ArgsUsage: "<keyfile> <message>",
Description: ` Description: `
Sign the message with a keyfile. Sign the message with a keyfile.
It is possible to refer to a file containing the message.`,
To sign a message contained in a file, use the --msgfile flag.
`,
Flags: []cli.Flag{ Flags: []cli.Flag{
passphraseFlag, passphraseFlag,
jsonFlag, jsonFlag,
msgfileFlag,
}, },
Action: func(ctx *cli.Context) error { Action: func(ctx *cli.Context) error {
keyfilepath := ctx.Args().First() message := getMessage(ctx, 1)
message := []byte(ctx.Args().Get(1))
// Load the keyfile. // Load the keyfile.
keyfilepath := ctx.Args().First()
keyjson, err := ioutil.ReadFile(keyfilepath) keyjson, err := ioutil.ReadFile(keyfilepath)
if err != nil { if err != nil {
utils.Fatalf("Failed to read the keyfile at '%s': %v", utils.Fatalf("Failed to read the keyfile at '%s': %v", keyfilepath, err)
keyfilepath, err)
} }
// Decrypt key with passphrase. // Decrypt key with passphrase.
@ -47,29 +68,15 @@ It is possible to refer to a file containing the message.`,
utils.Fatalf("Error decrypting key: %v", err) utils.Fatalf("Error decrypting key: %v", err)
} }
if len(message) == 0 {
utils.Fatalf("A message must be provided")
}
// Read message if file.
if _, err := os.Stat(string(message)); err == nil {
message, err = ioutil.ReadFile(string(message))
if err != nil {
utils.Fatalf("Failed to read the message file: %v", err)
}
}
signature, err := crypto.Sign(signHash(message), key.PrivateKey) signature, err := crypto.Sign(signHash(message), key.PrivateKey)
if err != nil { if err != nil {
utils.Fatalf("Failed to sign message: %v", err) utils.Fatalf("Failed to sign message: %v", err)
} }
out := outputSign{Signature: hex.EncodeToString(signature)}
out := outputSign{
Signature: hex.EncodeToString(signature),
}
if ctx.Bool(jsonFlag.Name) { if ctx.Bool(jsonFlag.Name) {
mustPrintJSON(out) mustPrintJSON(out)
} else { } else {
fmt.Println("Signature: ", out.Signature) fmt.Println("Signature:", out.Signature)
} }
return nil return nil
}, },
@ -84,53 +91,40 @@ type outputVerify struct {
var commandVerifyMessage = cli.Command{ var commandVerifyMessage = cli.Command{
Name: "verifymessage", Name: "verifymessage",
Usage: "verify the signature of a signed message", Usage: "verify the signature of a signed message",
ArgsUsage: "<address> <signature> <message/file>", ArgsUsage: "<address> <signature> <message>",
Description: ` Description: `
Verify the signature of the message. Verify the signature of the message.
It is possible to refer to a file containing the message.`, It is possible to refer to a file containing the message.`,
Flags: []cli.Flag{ Flags: []cli.Flag{
jsonFlag, jsonFlag,
msgfileFlag,
}, },
Action: func(ctx *cli.Context) error { Action: func(ctx *cli.Context) error {
addressStr := ctx.Args().First() addressStr := ctx.Args().First()
signatureHex := ctx.Args().Get(1) signatureHex := ctx.Args().Get(1)
message := []byte(ctx.Args().Get(2)) message := getMessage(ctx, 2)
// Determine whether it is a keyfile, public key or address.
if !common.IsHexAddress(addressStr) { if !common.IsHexAddress(addressStr) {
utils.Fatalf("Invalid address: %s", addressStr) utils.Fatalf("Invalid address: %s", addressStr)
} }
address := common.HexToAddress(addressStr) address := common.HexToAddress(addressStr)
signature, err := hex.DecodeString(signatureHex) signature, err := hex.DecodeString(signatureHex)
if err != nil { if err != nil {
utils.Fatalf("Signature encoding is not hexadecimal: %v", err) utils.Fatalf("Signature encoding is not hexadecimal: %v", err)
} }
if len(message) == 0 {
utils.Fatalf("A message must be provided")
}
// Read message if file.
if _, err := os.Stat(string(message)); err == nil {
message, err = ioutil.ReadFile(string(message))
if err != nil {
utils.Fatalf("Failed to read the message file: %v", err)
}
}
recoveredPubkey, err := crypto.SigToPub(signHash(message), signature) recoveredPubkey, err := crypto.SigToPub(signHash(message), signature)
if err != nil || recoveredPubkey == nil { if err != nil || recoveredPubkey == nil {
utils.Fatalf("Signature verification failed: %v", err) utils.Fatalf("Signature verification failed: %v", err)
} }
recoveredPubkeyBytes := crypto.FromECDSAPub(recoveredPubkey) recoveredPubkeyBytes := crypto.FromECDSAPub(recoveredPubkey)
recoveredAddress := crypto.PubkeyToAddress(*recoveredPubkey) recoveredAddress := crypto.PubkeyToAddress(*recoveredPubkey)
success := address == recoveredAddress success := address == recoveredAddress
out := outputVerify{ out := outputVerify{
Success: success, Success: success,
RecoveredPublicKey: hex.EncodeToString(recoveredPubkeyBytes), RecoveredPublicKey: hex.EncodeToString(recoveredPubkeyBytes),
RecoveredAddress: strings.ToLower(recoveredAddress.Hex()), RecoveredAddress: recoveredAddress.Hex(),
} }
if ctx.Bool(jsonFlag.Name) { if ctx.Bool(jsonFlag.Name) {
mustPrintJSON(out) mustPrintJSON(out)
@ -140,9 +134,26 @@ It is possible to refer to a file containing the message.`,
} else { } else {
fmt.Println("Signature verification failed!") fmt.Println("Signature verification failed!")
} }
fmt.Println("Recovered public key: ", out.RecoveredPublicKey) fmt.Println("Recovered public key:", out.RecoveredPublicKey)
fmt.Println("Recovered address: ", out.RecoveredAddress) fmt.Println("Recovered address:", out.RecoveredAddress)
} }
return nil return nil
}, },
} }
func getMessage(ctx *cli.Context, msgarg int) []byte {
if file := ctx.String("msgfile"); file != "" {
if len(ctx.Args()) > msgarg {
utils.Fatalf("Can't use --msgfile and message argument at the same time.")
}
msg, err := ioutil.ReadFile(file)
if err != nil {
utils.Fatalf("Can't read message file: %v", err)
}
return msg
} else if len(ctx.Args()) == msgarg+1 {
return []byte(ctx.Args().Get(msgarg))
}
utils.Fatalf("Invalid number of arguments: want %d, got %d", msgarg+1, len(ctx.Args()))
return nil
}

View file

@ -0,0 +1,70 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
)
func TestMessageSignVerify(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "ethkey-test")
if err != nil {
t.Fatal("Can't create temporary directory:", err)
}
defer os.RemoveAll(tmpdir)
keyfile := filepath.Join(tmpdir, "the-keyfile")
message := "test message"
// Create the key.
generate := runEthkey(t, "generate", keyfile)
generate.Expect(`
!! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "foobar"}}
Repeat passphrase: {{.InputLine "foobar"}}
`)
_, matches := generate.ExpectRegexp(`Address: (0x[0-9a-fA-F]{40})\n`)
address := matches[1]
generate.ExpectExit()
// Sign a message.
sign := runEthkey(t, "signmessage", keyfile, message)
sign.Expect(`
!! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "foobar"}}
`)
_, matches = sign.ExpectRegexp(`Signature: ([0-9a-f]+)\n`)
signature := matches[1]
sign.ExpectExit()
// Verify the message.
verify := runEthkey(t, "verifymessage", address, signature, message)
_, matches = verify.ExpectRegexp(`
Signature verification successful!
Recovered public key: [0-9a-f]+
Recovered address: (0x[0-9a-fA-F]{40})
`)
recovered := matches[1]
verify.ExpectExit()
if recovered != address {
t.Error("recovered address doesn't match generated key")
}
}

54
cmd/ethkey/run_test.go Normal file
View file

@ -0,0 +1,54 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"fmt"
"os"
"testing"
"github.com/docker/docker/pkg/reexec"
"github.com/ethereum/go-ethereum/internal/cmdtest"
)
type testEthkey struct {
*cmdtest.TestCmd
}
// spawns ethkey with the given command line args.
func runEthkey(t *testing.T, args ...string) *testEthkey {
tt := new(testEthkey)
tt.TestCmd = cmdtest.NewTestCmd(t, tt)
tt.Run("ethkey-test", args...)
return tt
}
func TestMain(m *testing.M) {
// Run the app if we've been exec'd as "ethkey-test" in runEthkey.
reexec.Register("ethkey-test", func() {
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
os.Exit(0)
})
// check if we have been reexec'd
if reexec.Init() {
return
}
os.Exit(m.Run())
}

View file

@ -1,18 +1,18 @@
// Copyright 2017 The go-ethereum Authors // Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library. // This file is part of go-ethereum.
// //
// The go-ethereum library is free software: you can redistribute it and/or modify // go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// The go-ethereum library is distributed in the hope that it will be useful, // go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details. // GNU General Public License for more details.
// //
// You should have received a copy of the GNU Lesser General Public License // You should have received a copy of the GNU General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main package main

View file

@ -96,7 +96,9 @@ func runCmd(ctx *cli.Context) error {
} }
if ctx.GlobalString(GenesisFlag.Name) != "" { if ctx.GlobalString(GenesisFlag.Name) != "" {
gen := readGenesis(ctx.GlobalString(GenesisFlag.Name)) gen := readGenesis(ctx.GlobalString(GenesisFlag.Name))
_, statedb = gen.ToBlock() db, _ := ethdb.NewMemDatabase()
genesis := gen.ToBlock(db)
statedb, _ = state.New(genesis.Root(), state.NewDatabase(db))
chainConfig = gen.Config chainConfig = gen.Config
} else { } else {
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()

View file

@ -223,7 +223,6 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u
NoDiscovery: true, NoDiscovery: true,
DiscoveryV5: true, DiscoveryV5: true,
ListenAddr: fmt.Sprintf(":%d", port), ListenAddr: fmt.Sprintf(":%d", port),
DiscoveryV5Addr: fmt.Sprintf(":%d", port+1),
MaxPeers: 25, MaxPeers: 25,
BootstrapNodesV5: enodes, BootstrapNodesV5: enodes,
}, },

View file

@ -67,6 +67,9 @@ It expects the genesis file as argument.`,
utils.DataDirFlag, utils.DataDirFlag,
utils.CacheFlag, utils.CacheFlag,
utils.LightModeFlag, utils.LightModeFlag,
utils.GCModeFlag,
utils.CacheDatabaseFlag,
utils.CacheGCFlag,
}, },
Category: "BLOCKCHAIN COMMANDS", Category: "BLOCKCHAIN COMMANDS",
Description: ` Description: `
@ -202,7 +205,7 @@ func importChain(ctx *cli.Context) error {
if len(ctx.Args()) == 1 { if len(ctx.Args()) == 1 {
if err := utils.ImportChain(chain, ctx.Args().First()); err != nil { if err := utils.ImportChain(chain, ctx.Args().First()); err != nil {
utils.Fatalf("Import error: %v", err) log.Error("Import error", "err", err)
} }
} else { } else {
for _, arg := range ctx.Args() { for _, arg := range ctx.Args() {
@ -211,7 +214,7 @@ func importChain(ctx *cli.Context) error {
} }
} }
} }
chain.Stop()
fmt.Printf("Import done in %v.\n\n", time.Since(start)) fmt.Printf("Import done in %v.\n\n", time.Since(start))
// Output pre-compaction stats mostly to see the import trashing // Output pre-compaction stats mostly to see the import trashing

View file

@ -18,7 +18,6 @@ package main
import ( import (
"bufio" "bufio"
"encoding/hex"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -29,7 +28,6 @@ import (
cli "gopkg.in/urfave/cli.v1" cli "gopkg.in/urfave/cli.v1"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/contracts/release"
"github.com/ethereum/go-ethereum/dashboard" "github.com/ethereum/go-ethereum/dashboard"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
@ -177,21 +175,6 @@ func makeFullNode(ctx *cli.Context) *node.Node {
if cfg.Ethstats.URL != "" { if cfg.Ethstats.URL != "" {
utils.RegisterEthStatsService(stack, cfg.Ethstats.URL) utils.RegisterEthStatsService(stack, cfg.Ethstats.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(params.VersionMajor),
Minor: uint32(params.VersionMinor),
Patch: uint32(params.VersionPatch),
}
commit, _ := hex.DecodeString(gitCommit)
copy(config.Commit[:], commit)
return release.NewReleaseService(ctx, config)
}); err != nil {
utils.Fatalf("Failed to register the Geth release oracle service: %v", err)
}
return stack return stack
} }

View file

@ -85,10 +85,13 @@ var (
utils.FastSyncFlag, utils.FastSyncFlag,
utils.LightModeFlag, utils.LightModeFlag,
utils.SyncModeFlag, utils.SyncModeFlag,
utils.GCModeFlag,
utils.LightServFlag, utils.LightServFlag,
utils.LightPeersFlag, utils.LightPeersFlag,
utils.LightKDFFlag, utils.LightKDFFlag,
utils.CacheFlag, utils.CacheFlag,
utils.CacheDatabaseFlag,
utils.CacheGCFlag,
utils.TrieCacheGenFlag, utils.TrieCacheGenFlag,
utils.ListenPortFlag, utils.ListenPortFlag,
utils.MaxPeersFlag, utils.MaxPeersFlag,
@ -111,6 +114,7 @@ var (
utils.VMEnableDebugFlag, utils.VMEnableDebugFlag,
utils.NetworkIdFlag, utils.NetworkIdFlag,
utils.RPCCORSDomainFlag, utils.RPCCORSDomainFlag,
utils.RPCVirtualHostsFlag,
utils.EthStatsURLFlag, utils.EthStatsURLFlag,
utils.MetricsEnabledFlag, utils.MetricsEnabledFlag,
utils.FakePoWFlag, utils.FakePoWFlag,

View file

@ -22,10 +22,11 @@ import (
"io" "io"
"sort" "sort"
"strings"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/internal/debug"
"gopkg.in/urfave/cli.v1" "gopkg.in/urfave/cli.v1"
"strings"
) )
// AppHelpTemplate is the test template for the default, global app help topic. // AppHelpTemplate is the test template for the default, global app help topic.
@ -74,6 +75,7 @@ var AppHelpFlagGroups = []flagGroup{
utils.TestnetFlag, utils.TestnetFlag,
utils.RinkebyFlag, utils.RinkebyFlag,
utils.SyncModeFlag, utils.SyncModeFlag,
utils.GCModeFlag,
utils.EthStatsURLFlag, utils.EthStatsURLFlag,
utils.IdentityFlag, utils.IdentityFlag,
utils.LightServFlag, utils.LightServFlag,
@ -127,6 +129,8 @@ var AppHelpFlagGroups = []flagGroup{
Name: "PERFORMANCE TUNING", Name: "PERFORMANCE TUNING",
Flags: []cli.Flag{ Flags: []cli.Flag{
utils.CacheFlag, utils.CacheFlag,
utils.CacheDatabaseFlag,
utils.CacheGCFlag,
utils.TrieCacheGenFlag, utils.TrieCacheGenFlag,
}, },
}, },
@ -152,6 +156,7 @@ var AppHelpFlagGroups = []flagGroup{
utils.IPCDisabledFlag, utils.IPCDisabledFlag,
utils.IPCPathFlag, utils.IPCPathFlag,
utils.RPCCORSDomainFlag, utils.RPCCORSDomainFlag,
utils.RPCVirtualHostsFlag,
utils.JSpathFlag, utils.JSpathFlag,
utils.ExecFlag, utils.ExecFlag,
utils.PreloadJSFlag, utils.PreloadJSFlag,

View file

@ -1,3 +1,19 @@
// Copyright 2017 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
// p2psim provides a command-line client for a simulation HTTP API. // p2psim provides a command-line client for a simulation HTTP API.
// //
// Here is an example of creating a 2 node network with the first node // Here is an example of creating a 2 node network with the first node

View file

@ -117,7 +117,7 @@ var dashboardContent = `
<br/> <br/>
<p>To run an archive node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with: <p>To run an archive node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with:
<pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre> <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
<pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=1024 --syncmode=full{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFullFlat}}</pre> <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=1024 --syncmode=full{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFlat}}</pre>
</p> </p>
<br/> <br/>
<p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p> <p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p>
@ -136,7 +136,7 @@ var dashboardContent = `
<br/> <br/>
<p>To run a full node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with: <p>To run a full node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with:
<pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre> <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
<pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=512{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFullFlat}}</pre> <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=512{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFlat}}</pre>
</p> </p>
<br/> <br/>
<p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p> <p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p>
@ -158,7 +158,7 @@ var dashboardContent = `
<br/> <br/>
<p>To run a light node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with: <p>To run a light node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with:
<pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre> <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
<pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesLightFlat}}</pre> <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFlat}}</pre>
</p> </p>
<br/> <br/>
<p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p> <p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p>
@ -177,7 +177,7 @@ var dashboardContent = `
<br/> <br/>
<p>To run an embedded node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with: <p>To run an embedded node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with:
<pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre> <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
<pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=16 --ethash.cachesinmem=1 --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesLightFlat}}</pre> <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=16 --ethash.cachesinmem=1 --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFlat}}</pre>
</p> </p>
<br/> <br/>
<p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p> <p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p>
@ -208,7 +208,7 @@ var dashboardContent = `
<pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre> <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
</p> </p>
<p>With your local chain initialized, you can start the Ethereum Wallet: <p>With your local chain initialized, you can start the Ethereum Wallet:
<pre>ethereumwallet --rpc $HOME/.{{.Network}}/geth.ipc --node-networkid={{.NetworkID}} --node-datadir=$HOME/.{{.Network}}{{if .Ethstats}} --node-ethstats='{{.Ethstats}}'{{end}} --node-bootnodes={{.BootnodesFullFlat}}</pre> <pre>ethereumwallet --rpc $HOME/.{{.Network}}/geth.ipc --node-networkid={{.NetworkID}} --node-datadir=$HOME/.{{.Network}}{{if .Ethstats}} --node-ethstats='{{.Ethstats}}'{{end}} --node-bootnodes={{.BootnodesFlat}}</pre>
<p> <p>
<br/> <br/>
<p>You can download the Ethereum Wallet from <a href="https://github.com/ethereum/mist/releases" target="about:blank">https://github.com/ethereum/mist/releases</a>.</p> <p>You can download the Ethereum Wallet from <a href="https://github.com/ethereum/mist/releases" target="about:blank">https://github.com/ethereum/mist/releases</a>.</p>
@ -229,7 +229,7 @@ var dashboardContent = `
<pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre> <pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
</p> </p>
<p>With your local chain initialized, you can start Mist: <p>With your local chain initialized, you can start Mist:
<pre>mist --rpc $HOME/.{{.Network}}/geth.ipc --node-networkid={{.NetworkID}} --node-datadir=$HOME/.{{.Network}}{{if .Ethstats}} --node-ethstats='{{.Ethstats}}'{{end}} --node-bootnodes={{.BootnodesFullFlat}}</pre> <pre>mist --rpc $HOME/.{{.Network}}/geth.ipc --node-networkid={{.NetworkID}} --node-datadir=$HOME/.{{.Network}}{{if .Ethstats}} --node-ethstats='{{.Ethstats}}'{{end}} --node-bootnodes={{.BootnodesFlat}}</pre>
<p> <p>
<br/> <br/>
<p>You can download the Mist browser from <a href="https://github.com/ethereum/mist/releases" target="about:blank">https://github.com/ethereum/mist/releases</a>.</p> <p>You can download the Mist browser from <a href="https://github.com/ethereum/mist/releases" target="about:blank">https://github.com/ethereum/mist/releases</a>.</p>
@ -261,7 +261,7 @@ var dashboardContent = `
<p>Inside your Java code you can now import the geth archive and connect to Ethereum: <p>Inside your Java code you can now import the geth archive and connect to Ethereum:
<pre>import org.ethereum.geth.*;</pre> <pre>import org.ethereum.geth.*;</pre>
<pre> <pre>
Enodes bootnodes = new Enodes();{{range .BootnodesLight}} Enodes bootnodes = new Enodes();{{range .Bootnodes}}
bootnodes.append(new Enode("{{.}}"));{{end}} bootnodes.append(new Enode("{{.}}"));{{end}}
NodeConfig config = new NodeConfig(); NodeConfig config = new NodeConfig();
@ -294,7 +294,7 @@ node.start();
<pre> <pre>
var error: NSError? var error: NSError?
let bootnodes = GethNewEnodesEmpty(){{range .BootnodesLight}} let bootnodes = GethNewEnodesEmpty(){{range .Bootnodes}}
bootnodes?.append(GethNewEnode("{{.}}", &error)){{end}} bootnodes?.append(GethNewEnode("{{.}}", &error)){{end}}
let config = GethNewNodeConfig() let config = GethNewNodeConfig()
@ -595,44 +595,42 @@ func deployDashboard(client *sshClient, network string, conf *config, config *da
statsLogin = "" statsLogin = ""
} }
indexfile := new(bytes.Buffer) indexfile := new(bytes.Buffer)
bootCpp := make([]string, len(conf.bootFull)) bootCpp := make([]string, len(conf.bootnodes))
for i, boot := range conf.bootFull { for i, boot := range conf.bootnodes {
bootCpp[i] = "required:" + strings.TrimPrefix(boot, "enode://") bootCpp[i] = "required:" + strings.TrimPrefix(boot, "enode://")
} }
bootHarmony := make([]string, len(conf.bootFull)) bootHarmony := make([]string, len(conf.bootnodes))
for i, boot := range conf.bootFull { for i, boot := range conf.bootnodes {
bootHarmony[i] = fmt.Sprintf("-Dpeer.active.%d.url=%s", i, boot) bootHarmony[i] = fmt.Sprintf("-Dpeer.active.%d.url=%s", i, boot)
} }
bootPython := make([]string, len(conf.bootFull)) bootPython := make([]string, len(conf.bootnodes))
for i, boot := range conf.bootFull { for i, boot := range conf.bootnodes {
bootPython[i] = "'" + boot + "'" bootPython[i] = "'" + boot + "'"
} }
template.Must(template.New("").Parse(dashboardContent)).Execute(indexfile, map[string]interface{}{ template.Must(template.New("").Parse(dashboardContent)).Execute(indexfile, map[string]interface{}{
"Network": network, "Network": network,
"NetworkID": conf.Genesis.Config.ChainId, "NetworkID": conf.Genesis.Config.ChainId,
"NetworkTitle": strings.Title(network), "NetworkTitle": strings.Title(network),
"EthstatsPage": config.ethstats, "EthstatsPage": config.ethstats,
"ExplorerPage": config.explorer, "ExplorerPage": config.explorer,
"WalletPage": config.wallet, "WalletPage": config.wallet,
"FaucetPage": config.faucet, "FaucetPage": config.faucet,
"GethGenesis": network + ".json", "GethGenesis": network + ".json",
"BootnodesFull": conf.bootFull, "Bootnodes": conf.bootnodes,
"BootnodesLight": conf.bootLight, "BootnodesFlat": strings.Join(conf.bootnodes, ","),
"BootnodesFullFlat": strings.Join(conf.bootFull, ","), "Ethstats": statsLogin,
"BootnodesLightFlat": strings.Join(conf.bootLight, ","), "Ethash": conf.Genesis.Config.Ethash != nil,
"Ethstats": statsLogin, "CppGenesis": network + "-cpp.json",
"Ethash": conf.Genesis.Config.Ethash != nil, "CppBootnodes": strings.Join(bootCpp, " "),
"CppGenesis": network + "-cpp.json", "HarmonyGenesis": network + "-harmony.json",
"CppBootnodes": strings.Join(bootCpp, " "), "HarmonyBootnodes": strings.Join(bootHarmony, " "),
"HarmonyGenesis": network + "-harmony.json", "ParityGenesis": network + "-parity.json",
"HarmonyBootnodes": strings.Join(bootHarmony, " "), "PythonGenesis": network + "-python.json",
"ParityGenesis": network + "-parity.json", "PythonBootnodes": strings.Join(bootPython, ","),
"PythonGenesis": network + "-python.json", "Homestead": conf.Genesis.Config.HomesteadBlock,
"PythonBootnodes": strings.Join(bootPython, ","), "Tangerine": conf.Genesis.Config.EIP150Block,
"Homestead": conf.Genesis.Config.HomesteadBlock, "Spurious": conf.Genesis.Config.EIP155Block,
"Tangerine": conf.Genesis.Config.EIP150Block, "Byzantium": conf.Genesis.Config.ByzantiumBlock,
"Spurious": conf.Genesis.Config.EIP155Block,
"Byzantium": conf.Genesis.Config.ByzantiumBlock,
}) })
files[filepath.Join(workdir, "index.html")] = indexfile.Bytes() files[filepath.Join(workdir, "index.html")] = indexfile.Bytes()
@ -651,7 +649,7 @@ func deployDashboard(client *sshClient, network string, conf *config, config *da
harmonySpecJSON, _ := conf.Genesis.MarshalJSON() harmonySpecJSON, _ := conf.Genesis.MarshalJSON()
files[filepath.Join(workdir, network+"-harmony.json")] = harmonySpecJSON files[filepath.Join(workdir, network+"-harmony.json")] = harmonySpecJSON
paritySpec, err := newParityChainSpec(network, conf.Genesis, conf.bootFull) paritySpec, err := newParityChainSpec(network, conf.Genesis, conf.bootnodes)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -93,7 +93,7 @@ func deployFaucet(client *sshClient, network string, bootnodes []string, config
"NetworkID": config.node.network, "NetworkID": config.node.network,
"Bootnodes": strings.Join(bootnodes, ","), "Bootnodes": strings.Join(bootnodes, ","),
"Ethstats": config.node.ethstats, "Ethstats": config.node.ethstats,
"EthPort": config.node.portFull, "EthPort": config.node.port,
"CaptchaToken": config.captchaToken, "CaptchaToken": config.captchaToken,
"CaptchaSecret": config.captchaSecret, "CaptchaSecret": config.captchaSecret,
"FaucetName": strings.Title(network), "FaucetName": strings.Title(network),
@ -110,7 +110,7 @@ func deployFaucet(client *sshClient, network string, bootnodes []string, config
"Datadir": config.node.datadir, "Datadir": config.node.datadir,
"VHost": config.host, "VHost": config.host,
"ApiPort": config.port, "ApiPort": config.port,
"EthPort": config.node.portFull, "EthPort": config.node.port,
"EthName": config.node.ethstats[:strings.Index(config.node.ethstats, ":")], "EthName": config.node.ethstats[:strings.Index(config.node.ethstats, ":")],
"CaptchaToken": config.captchaToken, "CaptchaToken": config.captchaToken,
"CaptchaSecret": config.captchaSecret, "CaptchaSecret": config.captchaSecret,
@ -158,7 +158,7 @@ func (info *faucetInfos) Report() map[string]string {
report := map[string]string{ report := map[string]string{
"Website address": info.host, "Website address": info.host,
"Website listener port": strconv.Itoa(info.port), "Website listener port": strconv.Itoa(info.port),
"Ethereum listener port": strconv.Itoa(info.node.portFull), "Ethereum listener port": strconv.Itoa(info.node.port),
"Funding amount (base tier)": fmt.Sprintf("%d Ethers", info.amount), "Funding amount (base tier)": fmt.Sprintf("%d Ethers", info.amount),
"Funding cooldown (base tier)": fmt.Sprintf("%d mins", info.minutes), "Funding cooldown (base tier)": fmt.Sprintf("%d mins", info.minutes),
"Funding tiers": strconv.Itoa(info.tiers), "Funding tiers": strconv.Itoa(info.tiers),
@ -228,7 +228,7 @@ func checkFaucet(client *sshClient, network string) (*faucetInfos, error) {
return &faucetInfos{ return &faucetInfos{
node: &nodeInfos{ node: &nodeInfos{
datadir: infos.volumes["/root/.faucet"], datadir: infos.volumes["/root/.faucet"],
portFull: infos.portmap[infos.envvars["ETH_PORT"]+"/tcp"], port: infos.portmap[infos.envvars["ETH_PORT"]+"/tcp"],
ethstats: infos.envvars["ETH_NAME"], ethstats: infos.envvars["ETH_NAME"],
keyJSON: keyJSON, keyJSON: keyJSON,
keyPass: keyPass, keyPass: keyPass,

View file

@ -42,7 +42,7 @@ ADD genesis.json /genesis.json
RUN \ RUN \
echo 'geth --cache 512 init /genesis.json' > geth.sh && \{{if .Unlock}} echo 'geth --cache 512 init /genesis.json' > geth.sh && \{{if .Unlock}}
echo 'mkdir -p /root/.ethereum/keystore/ && cp /signer.json /root/.ethereum/keystore/' >> geth.sh && \{{end}} echo 'mkdir -p /root/.ethereum/keystore/ && cp /signer.json /root/.ethereum/keystore/' >> geth.sh && \{{end}}
echo $'geth --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .BootV4}}--bootnodesv4 {{.BootV4}}{{end}} {{if .BootV5}}--bootnodesv5 {{.BootV5}}{{end}} {{if .Etherbase}}--etherbase {{.Etherbase}} --mine --minerthreads 1{{end}} {{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --targetgaslimit {{.GasTarget}} --gasprice {{.GasPrice}}' >> geth.sh echo $'geth --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .Bootnodes}}--bootnodes {{.Bootnodes}}{{end}} {{if .Etherbase}}--etherbase {{.Etherbase}} --mine --minerthreads 1{{end}} {{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --targetgaslimit {{.GasTarget}} --gasprice {{.GasPrice}}' >> geth.sh
ENTRYPOINT ["/bin/sh", "geth.sh"] ENTRYPOINT ["/bin/sh", "geth.sh"]
` `
@ -56,15 +56,13 @@ services:
build: . build: .
image: {{.Network}}/{{.Type}} image: {{.Network}}/{{.Type}}
ports: ports:
- "{{.FullPort}}:{{.FullPort}}" - "{{.Port}}:{{.Port}}"
- "{{.FullPort}}:{{.FullPort}}/udp"{{if .Light}} - "{{.Port}}:{{.Port}}/udp"
- "{{.LightPort}}:{{.LightPort}}/udp"{{end}}
volumes: volumes:
- {{.Datadir}}:/root/.ethereum{{if .Ethashdir}} - {{.Datadir}}:/root/.ethereum{{if .Ethashdir}}
- {{.Ethashdir}}:/root/.ethash{{end}} - {{.Ethashdir}}:/root/.ethash{{end}}
environment: environment:
- FULL_PORT={{.FullPort}}/tcp - PORT={{.Port}}/tcp
- LIGHT_PORT={{.LightPort}}/udp
- TOTAL_PEERS={{.TotalPeers}} - TOTAL_PEERS={{.TotalPeers}}
- LIGHT_PEERS={{.LightPeers}} - LIGHT_PEERS={{.LightPeers}}
- STATS_NAME={{.Ethstats}} - STATS_NAME={{.Ethstats}}
@ -82,12 +80,11 @@ services:
// deployNode deploys a new Ethereum node container to a remote machine via SSH, // deployNode deploys a new Ethereum node container to a remote machine via SSH,
// docker and docker-compose. If an instance with the specified network name // docker and docker-compose. If an instance with the specified network name
// already exists there, it will be overwritten! // already exists there, it will be overwritten!
func deployNode(client *sshClient, network string, bootv4, bootv5 []string, config *nodeInfos, nocache bool) ([]byte, error) { func deployNode(client *sshClient, network string, bootnodes []string, config *nodeInfos, nocache bool) ([]byte, error) {
kind := "sealnode" kind := "sealnode"
if config.keyJSON == "" && config.etherbase == "" { if config.keyJSON == "" && config.etherbase == "" {
kind = "bootnode" kind = "bootnode"
bootv4 = make([]string, 0) bootnodes = make([]string, 0)
bootv5 = make([]string, 0)
} }
// Generate the content to upload to the server // Generate the content to upload to the server
workdir := fmt.Sprintf("%d", rand.Int63()) workdir := fmt.Sprintf("%d", rand.Int63())
@ -100,11 +97,10 @@ func deployNode(client *sshClient, network string, bootv4, bootv5 []string, conf
dockerfile := new(bytes.Buffer) dockerfile := new(bytes.Buffer)
template.Must(template.New("").Parse(nodeDockerfile)).Execute(dockerfile, map[string]interface{}{ template.Must(template.New("").Parse(nodeDockerfile)).Execute(dockerfile, map[string]interface{}{
"NetworkID": config.network, "NetworkID": config.network,
"Port": config.portFull, "Port": config.port,
"Peers": config.peersTotal, "Peers": config.peersTotal,
"LightFlag": lightFlag, "LightFlag": lightFlag,
"BootV4": strings.Join(bootv4, ","), "Bootnodes": strings.Join(bootnodes, ","),
"BootV5": strings.Join(bootv5, ","),
"Ethstats": config.ethstats, "Ethstats": config.ethstats,
"Etherbase": config.etherbase, "Etherbase": config.etherbase,
"GasTarget": uint64(1000000 * config.gasTarget), "GasTarget": uint64(1000000 * config.gasTarget),
@ -119,10 +115,9 @@ func deployNode(client *sshClient, network string, bootv4, bootv5 []string, conf
"Datadir": config.datadir, "Datadir": config.datadir,
"Ethashdir": config.ethashdir, "Ethashdir": config.ethashdir,
"Network": network, "Network": network,
"FullPort": config.portFull, "Port": config.port,
"TotalPeers": config.peersTotal, "TotalPeers": config.peersTotal,
"Light": config.peersLight > 0, "Light": config.peersLight > 0,
"LightPort": config.portFull + 1,
"LightPeers": config.peersLight, "LightPeers": config.peersLight,
"Ethstats": config.ethstats[:strings.Index(config.ethstats, ":")], "Ethstats": config.ethstats[:strings.Index(config.ethstats, ":")],
"Etherbase": config.etherbase, "Etherbase": config.etherbase,
@ -157,10 +152,8 @@ type nodeInfos struct {
datadir string datadir string
ethashdir string ethashdir string
ethstats string ethstats string
portFull int port int
portLight int enode string
enodeFull string
enodeLight string
peersTotal int peersTotal int
peersLight int peersLight int
etherbase string etherbase string
@ -174,15 +167,11 @@ type nodeInfos struct {
// most - but not all - fields for reporting to the user. // most - but not all - fields for reporting to the user.
func (info *nodeInfos) Report() map[string]string { func (info *nodeInfos) Report() map[string]string {
report := map[string]string{ report := map[string]string{
"Data directory": info.datadir, "Data directory": info.datadir,
"Listener port (full nodes)": strconv.Itoa(info.portFull), "Listener port": strconv.Itoa(info.port),
"Peer count (all total)": strconv.Itoa(info.peersTotal), "Peer count (all total)": strconv.Itoa(info.peersTotal),
"Peer count (light nodes)": strconv.Itoa(info.peersLight), "Peer count (light nodes)": strconv.Itoa(info.peersLight),
"Ethstats username": info.ethstats, "Ethstats username": info.ethstats,
}
if info.peersLight > 0 {
// Light server enabled
report["Listener port (light nodes)"] = strconv.Itoa(info.portLight)
} }
if info.gasTarget > 0 { if info.gasTarget > 0 {
// Miner or signer node // Miner or signer node
@ -250,7 +239,7 @@ func checkNode(client *sshClient, network string, boot bool) (*nodeInfos, error)
keyPass = string(bytes.TrimSpace(out)) keyPass = string(bytes.TrimSpace(out))
} }
// Run a sanity check to see if the devp2p is reachable // Run a sanity check to see if the devp2p is reachable
port := infos.portmap[infos.envvars["FULL_PORT"]] port := infos.portmap[infos.envvars["PORT"]]
if err = checkPort(client.server, port); err != nil { if err = checkPort(client.server, port); err != nil {
log.Warn(fmt.Sprintf("%s devp2p port seems unreachable", strings.Title(kind)), "server", client.server, "port", port, "err", err) log.Warn(fmt.Sprintf("%s devp2p port seems unreachable", strings.Title(kind)), "server", client.server, "port", port, "err", err)
} }
@ -259,8 +248,7 @@ func checkNode(client *sshClient, network string, boot bool) (*nodeInfos, error)
genesis: genesis, genesis: genesis,
datadir: infos.volumes["/root/.ethereum"], datadir: infos.volumes["/root/.ethereum"],
ethashdir: infos.volumes["/root/.ethash"], ethashdir: infos.volumes["/root/.ethash"],
portFull: infos.portmap[infos.envvars["FULL_PORT"]], port: port,
portLight: infos.portmap[infos.envvars["LIGHT_PORT"]],
peersTotal: totalPeers, peersTotal: totalPeers,
peersLight: lightPeers, peersLight: lightPeers,
ethstats: infos.envvars["STATS_NAME"], ethstats: infos.envvars["STATS_NAME"],
@ -270,9 +258,7 @@ func checkNode(client *sshClient, network string, boot bool) (*nodeInfos, error)
gasTarget: gasTarget, gasTarget: gasTarget,
gasPrice: gasPrice, gasPrice: gasPrice,
} }
stats.enodeFull = fmt.Sprintf("enode://%s@%s:%d", id, client.address, stats.portFull) stats.enode = fmt.Sprintf("enode://%s@%s:%d", id, client.address, stats.port)
if stats.portLight != 0 {
stats.enodeLight = fmt.Sprintf("enode://%s@%s:%d?discport=%d", id, client.address, stats.portFull, stats.portLight)
}
return stats, nil return stats, nil
} }

View file

@ -40,8 +40,7 @@ import (
// between sessions. // between sessions.
type config struct { type config struct {
path string // File containing the configuration values path string // File containing the configuration values
bootFull []string // Bootnodes to always connect to by full nodes bootnodes []string // Bootnodes to always connect to by all nodes
bootLight []string // Bootnodes to always connect to by light nodes
ethstats string // Ethstats settings to cache for node deploys ethstats string // Ethstats settings to cache for node deploys
Genesis *core.Genesis `json:"genesis,omitempty"` // Genesis block to cache for node deploys Genesis *core.Genesis `json:"genesis,omitempty"` // Genesis block to cache for node deploys

View file

@ -55,7 +55,7 @@ func (w *wizard) deployExplorer() {
} }
existed := err == nil existed := err == nil
chainspec, err := newParityChainSpec(w.network, w.conf.Genesis, w.conf.bootFull) chainspec, err := newParityChainSpec(w.network, w.conf.Genesis, w.conf.bootnodes)
if err != nil { if err != nil {
log.Error("Failed to create chain spec for explorer", "err", err) log.Error("Failed to create chain spec for explorer", "err", err)
return return

View file

@ -38,7 +38,7 @@ func (w *wizard) deployFaucet() {
infos, err := checkFaucet(client, w.network) infos, err := checkFaucet(client, w.network)
if err != nil { if err != nil {
infos = &faucetInfos{ infos = &faucetInfos{
node: &nodeInfos{portFull: 30303, peersTotal: 25}, node: &nodeInfos{port: 30303, peersTotal: 25},
port: 80, port: 80,
host: client.server, host: client.server,
amount: 1, amount: 1,
@ -113,8 +113,8 @@ func (w *wizard) deployFaucet() {
} }
// Figure out which port to listen on // Figure out which port to listen on
fmt.Println() fmt.Println()
fmt.Printf("Which TCP/UDP port should the light client listen on? (default = %d)\n", infos.node.portFull) fmt.Printf("Which TCP/UDP port should the light client listen on? (default = %d)\n", infos.node.port)
infos.node.portFull = w.readDefaultInt(infos.node.portFull) infos.node.port = w.readDefaultInt(infos.node.port)
// Set a proper name to report on the stats page // Set a proper name to report on the stats page
fmt.Println() fmt.Println()
@ -168,7 +168,7 @@ func (w *wizard) deployFaucet() {
fmt.Printf("Should the faucet be built from scratch (y/n)? (default = no)\n") fmt.Printf("Should the faucet be built from scratch (y/n)? (default = no)\n")
nocache = w.readDefaultString("n") != "n" nocache = w.readDefaultString("n") != "n"
} }
if out, err := deployFaucet(client, w.network, w.conf.bootLight, infos, nocache); err != nil { if out, err := deployFaucet(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
log.Error("Failed to deploy faucet container", "err", err) log.Error("Failed to deploy faucet container", "err", err)
if len(out) > 0 { if len(out) > 0 {
fmt.Printf("%s\n", out) fmt.Printf("%s\n", out)

View file

@ -37,8 +37,7 @@ func (w *wizard) networkStats() {
} }
// Clear out some previous configs to refill from current scan // Clear out some previous configs to refill from current scan
w.conf.ethstats = "" w.conf.ethstats = ""
w.conf.bootFull = w.conf.bootFull[:0] w.conf.bootnodes = w.conf.bootnodes[:0]
w.conf.bootLight = w.conf.bootLight[:0]
// Iterate over all the specified hosts and check their status // Iterate over all the specified hosts and check their status
var pend sync.WaitGroup var pend sync.WaitGroup
@ -76,8 +75,7 @@ func (w *wizard) gatherStats(server string, pubkey []byte, client *sshClient) *s
var ( var (
genesis string genesis string
ethstats string ethstats string
bootFull []string bootnodes []string
bootLight []string
) )
// Ensure a valid SSH connection to the remote server // Ensure a valid SSH connection to the remote server
logger := log.New("server", server) logger := log.New("server", server)
@ -123,10 +121,7 @@ func (w *wizard) gatherStats(server string, pubkey []byte, client *sshClient) *s
stat.services["bootnode"] = infos.Report() stat.services["bootnode"] = infos.Report()
genesis = string(infos.genesis) genesis = string(infos.genesis)
bootFull = append(bootFull, infos.enodeFull) bootnodes = append(bootnodes, infos.enode)
if infos.enodeLight != "" {
bootLight = append(bootLight, infos.enodeLight)
}
} }
logger.Debug("Checking for sealnode availability") logger.Debug("Checking for sealnode availability")
if infos, err := checkNode(client, w.network, false); err != nil { if infos, err := checkNode(client, w.network, false); err != nil {
@ -184,8 +179,7 @@ func (w *wizard) gatherStats(server string, pubkey []byte, client *sshClient) *s
if ethstats != "" { if ethstats != "" {
w.conf.ethstats = ethstats w.conf.ethstats = ethstats
} }
w.conf.bootFull = append(w.conf.bootFull, bootFull...) w.conf.bootnodes = append(w.conf.bootnodes, bootnodes...)
w.conf.bootLight = append(w.conf.bootLight, bootLight...)
return stat return stat
} }

View file

@ -48,9 +48,9 @@ func (w *wizard) deployNode(boot bool) {
infos, err := checkNode(client, w.network, boot) infos, err := checkNode(client, w.network, boot)
if err != nil { if err != nil {
if boot { if boot {
infos = &nodeInfos{portFull: 30303, peersTotal: 512, peersLight: 256} infos = &nodeInfos{port: 30303, peersTotal: 512, peersLight: 256}
} else { } else {
infos = &nodeInfos{portFull: 30303, peersTotal: 50, peersLight: 0, gasTarget: 4.7, gasPrice: 18} infos = &nodeInfos{port: 30303, peersTotal: 50, peersLight: 0, gasTarget: 4.7, gasPrice: 18}
} }
} }
existed := err == nil existed := err == nil
@ -79,8 +79,8 @@ func (w *wizard) deployNode(boot bool) {
} }
// Figure out which port to listen on // Figure out which port to listen on
fmt.Println() fmt.Println()
fmt.Printf("Which TCP/UDP port to listen on? (default = %d)\n", infos.portFull) fmt.Printf("Which TCP/UDP port to listen on? (default = %d)\n", infos.port)
infos.portFull = w.readDefaultInt(infos.portFull) infos.port = w.readDefaultInt(infos.port)
// Figure out how many peers to allow (different based on node type) // Figure out how many peers to allow (different based on node type)
fmt.Println() fmt.Println()
@ -163,7 +163,7 @@ func (w *wizard) deployNode(boot bool) {
fmt.Printf("Should the node be built from scratch (y/n)? (default = no)\n") fmt.Printf("Should the node be built from scratch (y/n)? (default = no)\n")
nocache = w.readDefaultString("n") != "n" nocache = w.readDefaultString("n") != "n"
} }
if out, err := deployNode(client, w.network, w.conf.bootFull, w.conf.bootLight, infos, nocache); err != nil { if out, err := deployNode(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
log.Error("Failed to deploy Ethereum node container", "err", err) log.Error("Failed to deploy Ethereum node container", "err", err)
if len(out) > 0 { if len(out) > 0 {
fmt.Printf("%s\n", out) fmt.Printf("%s\n", out)

View file

@ -98,7 +98,7 @@ func (w *wizard) deployWallet() {
fmt.Printf("Should the wallet be built from scratch (y/n)? (default = no)\n") fmt.Printf("Should the wallet be built from scratch (y/n)? (default = no)\n")
nocache = w.readDefaultString("n") != "n" nocache = w.readDefaultString("n") != "n"
} }
if out, err := deployWallet(client, w.network, w.conf.bootFull, infos, nocache); err != nil { if out, err := deployWallet(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
log.Error("Failed to deploy wallet container", "err", err) log.Error("Failed to deploy wallet container", "err", err)
if len(out) > 0 { if len(out) > 0 {
fmt.Printf("%s\n", out) fmt.Printf("%s\n", out)

View file

@ -23,6 +23,7 @@ import (
"path/filepath" "path/filepath"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/swarm/storage" "github.com/ethereum/go-ethereum/swarm/storage"
"gopkg.in/urfave/cli.v1" "gopkg.in/urfave/cli.v1"
@ -30,11 +31,11 @@ import (
func dbExport(ctx *cli.Context) { func dbExport(ctx *cli.Context) {
args := ctx.Args() args := ctx.Args()
if len(args) != 2 { if len(args) != 3 {
utils.Fatalf("invalid arguments, please specify both <chunkdb> (path to a local chunk database) and <file> (path to write the tar archive to, - for stdout)") utils.Fatalf("invalid arguments, please specify both <chunkdb> (path to a local chunk database), <file> (path to write the tar archive to, - for stdout) and the base key")
} }
store, err := openDbStore(args[0]) store, err := openDbStore(args[0], common.Hex2Bytes(args[2]))
if err != nil { if err != nil {
utils.Fatalf("error opening local chunk database: %s", err) utils.Fatalf("error opening local chunk database: %s", err)
} }
@ -62,11 +63,11 @@ func dbExport(ctx *cli.Context) {
func dbImport(ctx *cli.Context) { func dbImport(ctx *cli.Context) {
args := ctx.Args() args := ctx.Args()
if len(args) != 2 { if len(args) != 3 {
utils.Fatalf("invalid arguments, please specify both <chunkdb> (path to a local chunk database) and <file> (path to read the tar archive from, - for stdin)") utils.Fatalf("invalid arguments, please specify both <chunkdb> (path to a local chunk database), <file> (path to read the tar archive from, - for stdin) and the base key")
} }
store, err := openDbStore(args[0]) store, err := openDbStore(args[0], common.Hex2Bytes(args[2]))
if err != nil { if err != nil {
utils.Fatalf("error opening local chunk database: %s", err) utils.Fatalf("error opening local chunk database: %s", err)
} }
@ -94,11 +95,11 @@ func dbImport(ctx *cli.Context) {
func dbClean(ctx *cli.Context) { func dbClean(ctx *cli.Context) {
args := ctx.Args() args := ctx.Args()
if len(args) != 1 { if len(args) != 2 {
utils.Fatalf("invalid arguments, please specify <chunkdb> (path to a local chunk database)") utils.Fatalf("invalid arguments, please specify <chunkdb> (path to a local chunk database) and the base key")
} }
store, err := openDbStore(args[0]) store, err := openDbStore(args[0], common.Hex2Bytes(args[1]))
if err != nil { if err != nil {
utils.Fatalf("error opening local chunk database: %s", err) utils.Fatalf("error opening local chunk database: %s", err)
} }
@ -107,10 +108,10 @@ func dbClean(ctx *cli.Context) {
store.Cleanup() store.Cleanup()
} }
func openDbStore(path string) (*storage.DbStore, error) { func openDbStore(path string, basekey []byte) (*storage.DbStore, error) {
if _, err := os.Stat(filepath.Join(path, "CURRENT")); err != nil { if _, err := os.Stat(filepath.Join(path, "CURRENT")); err != nil {
return nil, fmt.Errorf("invalid chunkdb path: %s", err) return nil, fmt.Errorf("invalid chunkdb path: %s", err)
} }
hash := storage.MakeHashFunc("SHA3") hash := storage.MakeHashFunc("SHA3")
return storage.NewDbStore(path, hash, 10000000, 0) return storage.NewDbStore(path, hash, 10000000, func(k storage.Key) (ret uint8) { return uint8(storage.Proximity(basekey[:], k[:])) })
} }

View file

@ -39,7 +39,7 @@ func hash(ctx *cli.Context) {
stat, _ := f.Stat() stat, _ := f.Stat()
chunker := storage.NewTreeChunker(storage.NewChunkerParams()) chunker := storage.NewTreeChunker(storage.NewChunkerParams())
key, err := chunker.Split(f, stat.Size(), nil, nil, nil) key, _, err := chunker.Split(f, stat.Size(), nil)
if err != nil { if err != nil {
utils.Fatalf("%v\n", err) utils.Fatalf("%v\n", err)
} else { } else {

View file

@ -1,4 +1,4 @@
// Copyright 2016 The go-ethereum Authors // Copyright 2017 The go-ethereum Authors
// This file is part of go-ethereum. // This file is part of go-ethereum.
// //
// go-ethereum is free software: you can redistribute it and/or modify // go-ethereum is free software: you can redistribute it and/or modify

View file

@ -1,4 +1,4 @@
// Copyright 2016 The go-ethereum Authors // Copyright 2017 The go-ethereum Authors
// This file is part of go-ethereum. // This file is part of go-ethereum.
// //
// go-ethereum is free software: you can redistribute it and/or modify // go-ethereum is free software: you can redistribute it and/or modify

View file

@ -116,7 +116,6 @@ func ImportChain(chain *core.BlockChain, fn string) error {
return err return err
} }
} }
stream := rlp.NewStream(reader, 0) stream := rlp.NewStream(reader, 0)
// Run actual the import. // Run actual the import.
@ -150,25 +149,34 @@ func ImportChain(chain *core.BlockChain, fn string) error {
if checkInterrupt() { if checkInterrupt() {
return fmt.Errorf("interrupted") return fmt.Errorf("interrupted")
} }
if hasAllBlocks(chain, blocks[:i]) { missing := missingBlocks(chain, blocks[:i])
if len(missing) == 0 {
log.Info("Skipping batch as all blocks present", "batch", batch, "first", blocks[0].Hash(), "last", blocks[i-1].Hash()) log.Info("Skipping batch as all blocks present", "batch", batch, "first", blocks[0].Hash(), "last", blocks[i-1].Hash())
continue continue
} }
if _, err := chain.InsertChain(missing); err != nil {
if _, err := chain.InsertChain(blocks[:i]); err != nil {
return fmt.Errorf("invalid block %d: %v", n, err) return fmt.Errorf("invalid block %d: %v", n, err)
} }
} }
return nil return nil
} }
func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool { func missingBlocks(chain *core.BlockChain, blocks []*types.Block) []*types.Block {
for _, b := range bs { head := chain.CurrentBlock()
if !chain.HasBlock(b.Hash(), b.NumberU64()) { for i, block := range blocks {
return false // If we're behind the chain head, only check block, state is available at head
if head.NumberU64() > block.NumberU64() {
if !chain.HasBlock(block.Hash(), block.NumberU64()) {
return blocks[i:]
}
continue
}
// If we're above the chain head, state availability is a must
if !chain.HasBlockAndState(block.Hash(), block.NumberU64()) {
return blocks[i:]
} }
} }
return true return nil
} }
func ExportChain(blockchain *core.BlockChain, fn string) error { func ExportChain(blockchain *core.BlockChain, fn string) error {

View file

@ -170,7 +170,11 @@ var (
Usage: `Blockchain sync mode ("fast", "full", or "light")`, Usage: `Blockchain sync mode ("fast", "full", or "light")`,
Value: &defaultSyncMode, Value: &defaultSyncMode,
} }
GCModeFlag = cli.StringFlag{
Name: "gcmode",
Usage: `Blockchain garbage collection mode ("full", "archive")`,
Value: "full",
}
LightServFlag = cli.IntFlag{ LightServFlag = cli.IntFlag{
Name: "lightserv", Name: "lightserv",
Usage: "Maximum percentage of time allowed for serving LES requests (0-90)", Usage: "Maximum percentage of time allowed for serving LES requests (0-90)",
@ -179,7 +183,7 @@ var (
LightPeersFlag = cli.IntFlag{ LightPeersFlag = cli.IntFlag{
Name: "lightpeers", Name: "lightpeers",
Usage: "Maximum number of LES client peers", Usage: "Maximum number of LES client peers",
Value: 20, Value: eth.DefaultConfig.LightPeers,
} }
LightKDFFlag = cli.BoolFlag{ LightKDFFlag = cli.BoolFlag{
Name: "lightkdf", Name: "lightkdf",
@ -293,8 +297,18 @@ var (
// Performance tuning settings // Performance tuning settings
CacheFlag = cli.IntFlag{ CacheFlag = cli.IntFlag{
Name: "cache", Name: "cache",
Usage: "Megabytes of memory allocated to internal caching (min 16MB / database forced)", Usage: "Megabytes of memory allocated to internal caching",
Value: 128, Value: 1024,
}
CacheDatabaseFlag = cli.IntFlag{
Name: "cache.database",
Usage: "Percentage of cache memory allowance to use for database io",
Value: 75,
}
CacheGCFlag = cli.IntFlag{
Name: "cache.gc",
Usage: "Percentage of cache memory allowance to use for trie pruning",
Value: 25,
} }
TrieCacheGenFlag = cli.IntFlag{ TrieCacheGenFlag = cli.IntFlag{
Name: "trie-cache-gens", Name: "trie-cache-gens",
@ -383,6 +397,11 @@ var (
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
Value: "", Value: "",
} }
RPCVirtualHostsFlag = cli.StringFlag{
Name: "rpcvhosts",
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
Value: "localhost",
}
RPCApiFlag = cli.StringFlag{ RPCApiFlag = cli.StringFlag{
Name: "rpcapi", Name: "rpcapi",
Usage: "API's offered over the HTTP-RPC interface", Usage: "API's offered over the HTTP-RPC interface",
@ -612,7 +631,7 @@ func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) {
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",") urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
} }
case ctx.GlobalBool(RinkebyFlag.Name): case ctx.GlobalBool(RinkebyFlag.Name):
urls = params.RinkebyV5Bootnodes urls = params.RinkebyBootnodes
case cfg.BootstrapNodesV5 != nil: case cfg.BootstrapNodesV5 != nil:
return // already set, don't apply defaults. return // already set, don't apply defaults.
} }
@ -636,14 +655,6 @@ func setListenAddress(ctx *cli.Context, cfg *p2p.Config) {
} }
} }
// setDiscoveryV5Address creates a UDP listening address string from set command
// line flags for the V5 discovery protocol.
func setDiscoveryV5Address(ctx *cli.Context, cfg *p2p.Config) {
if ctx.GlobalIsSet(ListenPortFlag.Name) {
cfg.DiscoveryV5Addr = fmt.Sprintf(":%d", ctx.GlobalInt(ListenPortFlag.Name)+1)
}
}
// setNAT creates a port mapper from command line flags. // setNAT creates a port mapper from command line flags.
func setNAT(ctx *cli.Context, cfg *p2p.Config) { func setNAT(ctx *cli.Context, cfg *p2p.Config) {
if ctx.GlobalIsSet(NATFlag.Name) { if ctx.GlobalIsSet(NATFlag.Name) {
@ -684,6 +695,8 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) {
if ctx.GlobalIsSet(RPCApiFlag.Name) { if ctx.GlobalIsSet(RPCApiFlag.Name) {
cfg.HTTPModules = splitAndTrim(ctx.GlobalString(RPCApiFlag.Name)) cfg.HTTPModules = splitAndTrim(ctx.GlobalString(RPCApiFlag.Name))
} }
cfg.HTTPVirtualHosts = splitAndTrim(ctx.GlobalString(RPCVirtualHostsFlag.Name))
} }
// setWS creates the WebSocket RPC listener interface string from the set // setWS creates the WebSocket RPC listener interface string from the set
@ -722,13 +735,15 @@ func setIPC(ctx *cli.Context, cfg *node.Config) {
// makeDatabaseHandles raises out the number of allowed file handles per process // makeDatabaseHandles raises out the number of allowed file handles per process
// for Geth and returns half of the allowance to assign to the database. // for Geth and returns half of the allowance to assign to the database.
func makeDatabaseHandles() int { func makeDatabaseHandles() int {
if err := fdlimit.Raise(2048); err != nil {
Fatalf("Failed to raise file descriptor allowance: %v", err)
}
limit, err := fdlimit.Current() limit, err := fdlimit.Current()
if err != nil { if err != nil {
Fatalf("Failed to retrieve file descriptor allowance: %v", err) Fatalf("Failed to retrieve file descriptor allowance: %v", err)
} }
if limit < 2048 {
if err := fdlimit.Raise(2048); err != nil {
Fatalf("Failed to raise file descriptor allowance: %v", err)
}
}
if limit > 2048 { // cap database file descriptors even if more is available if limit > 2048 { // cap database file descriptors even if more is available
limit = 2048 limit = 2048
} }
@ -794,24 +809,43 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
setNodeKey(ctx, cfg) setNodeKey(ctx, cfg)
setNAT(ctx, cfg) setNAT(ctx, cfg)
setListenAddress(ctx, cfg) setListenAddress(ctx, cfg)
setDiscoveryV5Address(ctx, cfg)
setBootstrapNodes(ctx, cfg) setBootstrapNodes(ctx, cfg)
setBootstrapNodesV5(ctx, cfg) setBootstrapNodesV5(ctx, cfg)
lightClient := ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalString(SyncModeFlag.Name) == "light"
lightServer := ctx.GlobalInt(LightServFlag.Name) != 0
lightPeers := ctx.GlobalInt(LightPeersFlag.Name)
if ctx.GlobalIsSet(MaxPeersFlag.Name) { if ctx.GlobalIsSet(MaxPeersFlag.Name) {
cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name) cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name)
} else {
if lightServer {
cfg.MaxPeers += lightPeers
}
if lightClient && ctx.GlobalIsSet(LightPeersFlag.Name) && cfg.MaxPeers < lightPeers {
cfg.MaxPeers = lightPeers
}
} }
if !(lightClient || lightServer) {
lightPeers = 0
}
ethPeers := cfg.MaxPeers - lightPeers
if lightClient {
ethPeers = 0
}
log.Info("Maximum peer count", "ETH", ethPeers, "LES", lightPeers, "total", cfg.MaxPeers)
if ctx.GlobalIsSet(MaxPendingPeersFlag.Name) { if ctx.GlobalIsSet(MaxPendingPeersFlag.Name) {
cfg.MaxPendingPeers = ctx.GlobalInt(MaxPendingPeersFlag.Name) cfg.MaxPendingPeers = ctx.GlobalInt(MaxPendingPeersFlag.Name)
} }
if ctx.GlobalIsSet(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name) { if ctx.GlobalIsSet(NoDiscoverFlag.Name) || lightClient {
cfg.NoDiscovery = true cfg.NoDiscovery = true
} }
// if we're running a light client or server, force enable the v5 peer discovery // if we're running a light client or server, force enable the v5 peer discovery
// unless it is explicitly disabled with --nodiscover note that explicitly specifying // unless it is explicitly disabled with --nodiscover note that explicitly specifying
// --v5disc overrides --nodiscover, in which case the later only disables v4 discovery // --v5disc overrides --nodiscover, in which case the later only disables v4 discovery
forceV5Discovery := (ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalInt(LightServFlag.Name) > 0) && !ctx.GlobalBool(NoDiscoverFlag.Name) forceV5Discovery := (lightClient || lightServer) && !ctx.GlobalBool(NoDiscoverFlag.Name)
if ctx.GlobalIsSet(DiscoveryV5Flag.Name) { if ctx.GlobalIsSet(DiscoveryV5Flag.Name) {
cfg.DiscoveryV5 = ctx.GlobalBool(DiscoveryV5Flag.Name) cfg.DiscoveryV5 = ctx.GlobalBool(DiscoveryV5Flag.Name)
} else if forceV5Discovery { } else if forceV5Discovery {
@ -830,7 +864,6 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
// --dev mode can't use p2p networking. // --dev mode can't use p2p networking.
cfg.MaxPeers = 0 cfg.MaxPeers = 0
cfg.ListenAddr = ":0" cfg.ListenAddr = ":0"
cfg.DiscoveryV5Addr = ":0"
cfg.NoDiscovery = true cfg.NoDiscovery = true
cfg.DiscoveryV5 = false cfg.DiscoveryV5 = false
} }
@ -1009,11 +1042,19 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name) cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name)
} }
if ctx.GlobalIsSet(CacheFlag.Name) { if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheDatabaseFlag.Name) {
cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
} }
cfg.DatabaseHandles = makeDatabaseHandles() cfg.DatabaseHandles = makeDatabaseHandles()
if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
}
cfg.NoPruning = ctx.GlobalString(GCModeFlag.Name) == "archive"
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) {
cfg.TrieCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
}
if ctx.GlobalIsSet(MinerThreadsFlag.Name) { if ctx.GlobalIsSet(MinerThreadsFlag.Name) {
cfg.MinerThreads = ctx.GlobalInt(MinerThreadsFlag.Name) cfg.MinerThreads = ctx.GlobalInt(MinerThreadsFlag.Name)
} }
@ -1145,7 +1186,7 @@ func SetupNetwork(ctx *cli.Context) {
// MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails. // MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails.
func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database { func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
var ( var (
cache = ctx.GlobalInt(CacheFlag.Name) cache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
handles = makeDatabaseHandles() handles = makeDatabaseHandles()
) )
name := "chaindata" name := "chaindata"
@ -1197,8 +1238,19 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
}) })
} }
} }
if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
}
cache := &core.CacheConfig{
Disabled: ctx.GlobalString(GCModeFlag.Name) == "archive",
TrieNodeLimit: eth.DefaultConfig.TrieCache,
TrieTimeLimit: eth.DefaultConfig.TrieTimeout,
}
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) {
cache.TrieNodeLimit = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
}
vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)} vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)}
chain, err = core.NewBlockChain(chainDb, config, engine, vmcfg) chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg)
if err != nil { if err != nil {
Fatalf("Can't create BlockChain: %v", err) Fatalf("Can't create BlockChain: %v", err)
} }

View file

@ -43,7 +43,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/nat" "github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/whisper/mailserver" "github.com/ethereum/go-ethereum/whisper/mailserver"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5" whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
) )
@ -61,15 +61,17 @@ var (
// encryption // encryption
var ( var (
symKey []byte symKey []byte
pub *ecdsa.PublicKey pub *ecdsa.PublicKey
asymKey *ecdsa.PrivateKey asymKey *ecdsa.PrivateKey
nodeid *ecdsa.PrivateKey nodeid *ecdsa.PrivateKey
topic whisper.TopicType topic whisper.TopicType
asymKeyID string
filterID string asymKeyID string
symPass string asymFilterID string
msPassword string symFilterID string
symPass string
msPassword string
) )
// cmd arguments // cmd arguments
@ -263,7 +265,7 @@ func initialize() {
Config: p2p.Config{ Config: p2p.Config{
PrivateKey: nodeid, PrivateKey: nodeid,
MaxPeers: maxPeers, MaxPeers: maxPeers,
Name: common.MakeName("wnode", "5.0"), Name: common.MakeName("wnode", "6.0"),
Protocols: shh.Protocols(), Protocols: shh.Protocols(),
ListenAddr: *argIP, ListenAddr: *argIP,
NAT: nat.Any(), NAT: nat.Any(),
@ -363,13 +365,22 @@ func configureNode() {
} }
} }
filter := whisper.Filter{ symFilter := whisper.Filter{
KeySym: symKey, KeySym: symKey,
Topics: [][]byte{topic[:]},
AllowP2P: p2pAccept,
}
symFilterID, err = shh.Subscribe(&symFilter)
if err != nil {
utils.Fatalf("Failed to install filter: %s", err)
}
asymFilter := whisper.Filter{
KeyAsym: asymKey, KeyAsym: asymKey,
Topics: [][]byte{topic[:]}, Topics: [][]byte{topic[:]},
AllowP2P: p2pAccept, AllowP2P: p2pAccept,
} }
filterID, err = shh.Subscribe(&filter) asymFilterID, err = shh.Subscribe(&asymFilter)
if err != nil { if err != nil {
utils.Fatalf("Failed to install filter: %s", err) utils.Fatalf("Failed to install filter: %s", err)
} }
@ -522,9 +533,14 @@ func sendMsg(payload []byte) common.Hash {
} }
func messageLoop() { func messageLoop() {
f := shh.GetFilter(filterID) sf := shh.GetFilter(symFilterID)
if f == nil { if sf == nil {
utils.Fatalf("filter is not installed") utils.Fatalf("symmetric filter is not installed")
}
af := shh.GetFilter(asymFilterID)
if af == nil {
utils.Fatalf("asymmetric filter is not installed")
} }
ticker := time.NewTicker(time.Millisecond * 50) ticker := time.NewTicker(time.Millisecond * 50)
@ -532,7 +548,16 @@ func messageLoop() {
for { for {
select { select {
case <-ticker.C: case <-ticker.C:
messages := f.Retrieve() messages := sf.Retrieve()
for _, msg := range messages {
if *fileExMode || len(msg.Payload) > 2048 {
writeMessageToFile(*argSaveDir, msg)
} else {
printMessageInfo(msg)
}
}
messages = af.Retrieve()
for _, msg := range messages { for _, msg := range messages {
if *fileExMode || len(msg.Payload) > 2048 { if *fileExMode || len(msg.Payload) > 2048 {
writeMessageToFile(*argSaveDir, msg) writeMessageToFile(*argSaveDir, msg)
@ -601,7 +626,7 @@ func requestExpiredMessagesLoop() {
if err != nil { if err != nil {
utils.Fatalf("Failed to save symmetric key for mail request: %s", err) utils.Fatalf("Failed to save symmetric key for mail request: %s", err)
} }
peerID = extractIdFromEnode(*argEnode) peerID = extractIDFromEnode(*argEnode)
shh.AllowP2PMessagesFromPeer(peerID) shh.AllowP2PMessagesFromPeer(peerID)
for { for {
@ -631,7 +656,7 @@ func requestExpiredMessagesLoop() {
params.PoW = *argServerPoW params.PoW = *argServerPoW
params.Payload = data params.Payload = data
params.KeySym = key params.KeySym = key
params.Src = nodeid params.Src = asymKey
params.WorkTime = 5 params.WorkTime = 5
msg, err := whisper.NewSentMessage(&params) msg, err := whisper.NewSentMessage(&params)
@ -652,7 +677,7 @@ func requestExpiredMessagesLoop() {
} }
} }
func extractIdFromEnode(s string) []byte { func extractIDFromEnode(s string) []byte {
n, err := discover.ParseNode(s) n, err := discover.ParseNode(s)
if err != nil { if err != nil {
utils.Fatalf("Failed to parse enode: %s", err) utils.Fatalf("Failed to parse enode: %s", err)

View file

@ -1,18 +1,18 @@
// Copyright 2016 The go-ethereum Authors // Copyright 2016 The go-ethereum Authors
// This file is part of go-ethereum. // This file is part of the go-ethereum library.
// //
// go-ethereum is free software: you can redistribute it and/or modify // The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // 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 // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// go-ethereum is distributed in the hope that it will be useful, // The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU Lesser General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU Lesser General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +build freebsd // +build freebsd

View file

@ -1,18 +1,18 @@
// Copyright 2016 The go-ethereum Authors // Copyright 2016 The go-ethereum Authors
// This file is part of go-ethereum. // This file is part of the go-ethereum library.
// //
// go-ethereum is free software: you can redistribute it and/or modify // The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // 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 // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// go-ethereum is distributed in the hope that it will be useful, // The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU Lesser General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU Lesser General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package fdlimit package fdlimit

View file

@ -1,18 +1,18 @@
// Copyright 2016 The go-ethereum Authors // Copyright 2016 The go-ethereum Authors
// This file is part of go-ethereum. // This file is part of the go-ethereum library.
// //
// go-ethereum is free software: you can redistribute it and/or modify // The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // 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 // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// go-ethereum is distributed in the hope that it will be useful, // The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU Lesser General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU Lesser General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +build linux darwin netbsd openbsd solaris // +build linux darwin netbsd openbsd solaris

View file

@ -1,18 +1,18 @@
// Copyright 2016 The go-ethereum Authors // Copyright 2018 The go-ethereum Authors
// This file is part of go-ethereum. // This file is part of the go-ethereum library.
// //
// go-ethereum is free software: you can redistribute it and/or modify // The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // 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 // the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version. // (at your option) any later version.
// //
// go-ethereum is distributed in the hope that it will be useful, // The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of // but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details. // GNU Lesser General Public License for more details.
// //
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU Lesser General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package fdlimit package fdlimit

View file

@ -20,18 +20,29 @@ import (
"fmt" "fmt"
) )
// StorageSize is a wrapper around a float value that supports user friendly
// formatting.
type StorageSize float64 type StorageSize float64
func (self StorageSize) String() string { // String implements the stringer interface.
if self > 1000000 { func (s StorageSize) String() string {
return fmt.Sprintf("%.2f mB", self/1000000) if s > 1000000 {
} else if self > 1000 { return fmt.Sprintf("%.2f mB", s/1000000)
return fmt.Sprintf("%.2f kB", self/1000) } else if s > 1000 {
return fmt.Sprintf("%.2f kB", s/1000)
} else { } else {
return fmt.Sprintf("%.2f B", self) return fmt.Sprintf("%.2f B", s)
} }
} }
func (self StorageSize) Int64() int64 { // TerminalString implements log.TerminalStringer, formatting a string for console
return int64(self) // output during logging.
func (s StorageSize) TerminalString() string {
if s > 1000000 {
return fmt.Sprintf("%.2fmB", s/1000000)
} else if s > 1000 {
return fmt.Sprintf("%.2fkB", s/1000)
} else {
return fmt.Sprintf("%.2fB", s)
}
} }

View file

@ -23,6 +23,10 @@ var (
// that is unknown. // that is unknown.
ErrUnknownAncestor = errors.New("unknown ancestor") ErrUnknownAncestor = errors.New("unknown ancestor")
// ErrPrunedAncestor is returned when validating a block requires an ancestor
// that is known, but the state of which is not available.
ErrPrunedAncestor = errors.New("pruned ancestor")
// ErrFutureBlock is returned when a block's timestamp is in the future according // ErrFutureBlock is returned when a block's timestamp is in the future according
// to the current node. // to the current node.
ErrFutureBlock = errors.New("block in the future") ErrFutureBlock = errors.New("block in the future")

View file

@ -355,9 +355,11 @@ func hashimotoFull(dataset []uint32, hash []byte, nonce uint64) ([]byte, []byte)
return hashimoto(hash, nonce, uint64(len(dataset))*4, lookup) return hashimoto(hash, nonce, uint64(len(dataset))*4, lookup)
} }
const maxEpoch = 2048
// datasetSizes is a lookup table for the ethash dataset size for the first 2048 // datasetSizes is a lookup table for the ethash dataset size for the first 2048
// epochs (i.e. 61440000 blocks). // epochs (i.e. 61440000 blocks).
var datasetSizes = []uint64{ var datasetSizes = [maxEpoch]uint64{
1073739904, 1082130304, 1090514816, 1098906752, 1107293056, 1073739904, 1082130304, 1090514816, 1098906752, 1107293056,
1115684224, 1124070016, 1132461952, 1140849536, 1149232768, 1115684224, 1124070016, 1132461952, 1140849536, 1149232768,
1157627776, 1166013824, 1174404736, 1182786944, 1191180416, 1157627776, 1166013824, 1174404736, 1182786944, 1191180416,
@ -771,7 +773,7 @@ var datasetSizes = []uint64{
// cacheSizes is a lookup table for the ethash verification cache size for the // cacheSizes is a lookup table for the ethash verification cache size for the
// first 2048 epochs (i.e. 61440000 blocks). // first 2048 epochs (i.e. 61440000 blocks).
var cacheSizes = []uint64{ var cacheSizes = [maxEpoch]uint64{
16776896, 16907456, 17039296, 17170112, 17301056, 17432512, 17563072, 16776896, 16907456, 17039296, 17170112, 17301056, 17432512, 17563072,
17693888, 17824192, 17955904, 18087488, 18218176, 18349504, 18481088, 17693888, 17824192, 17955904, 18087488, 18218176, 18349504, 18481088,
18611392, 18742336, 18874304, 19004224, 19135936, 19267264, 19398208, 18611392, 18742336, 18874304, 19004224, 19135936, 19267264, 19398208,

View file

@ -25,7 +25,7 @@ package ethash
func cacheSize(block uint64) uint64 { func cacheSize(block uint64) uint64 {
// If we have a pre-generated value, use that // If we have a pre-generated value, use that
epoch := int(block / epochLength) epoch := int(block / epochLength)
if epoch < len(cacheSizes) { if epoch < maxEpoch {
return cacheSizes[epoch] return cacheSizes[epoch]
} }
// We don't have a way to verify primes fast before Go 1.8 // We don't have a way to verify primes fast before Go 1.8
@ -39,7 +39,7 @@ func cacheSize(block uint64) uint64 {
func datasetSize(block uint64) uint64 { func datasetSize(block uint64) uint64 {
// If we have a pre-generated value, use that // If we have a pre-generated value, use that
epoch := int(block / epochLength) epoch := int(block / epochLength)
if epoch < len(datasetSizes) { if epoch < maxEpoch {
return datasetSizes[epoch] return datasetSizes[epoch]
} }
// We don't have a way to verify primes fast before Go 1.8 // We don't have a way to verify primes fast before Go 1.8

View file

@ -20,17 +20,20 @@ package ethash
import "math/big" import "math/big"
// cacheSize calculates and returns the size of the ethash verification cache that // cacheSize returns the size of the ethash verification cache that belongs to a certain
// belongs to a certain block number. The cache size grows linearly, however, we // block number.
// always take the highest prime below the linearly growing threshold in order to
// reduce the risk of accidental regularities leading to cyclic behavior.
func cacheSize(block uint64) uint64 { func cacheSize(block uint64) uint64 {
// If we have a pre-generated value, use that
epoch := int(block / epochLength) epoch := int(block / epochLength)
if epoch < len(cacheSizes) { if epoch < maxEpoch {
return cacheSizes[epoch] return cacheSizes[epoch]
} }
// No known cache size, calculate manually (sanity branch only) return calcCacheSize(epoch)
}
// calcCacheSize calculates the cache size for epoch. The cache size grows linearly,
// however, we always take the highest prime below the linearly growing threshold in order
// to reduce the risk of accidental regularities leading to cyclic behavior.
func calcCacheSize(epoch int) uint64 {
size := cacheInitBytes + cacheGrowthBytes*uint64(epoch) - hashBytes size := cacheInitBytes + cacheGrowthBytes*uint64(epoch) - hashBytes
for !new(big.Int).SetUint64(size / hashBytes).ProbablyPrime(1) { // Always accurate for n < 2^64 for !new(big.Int).SetUint64(size / hashBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
size -= 2 * hashBytes size -= 2 * hashBytes
@ -38,17 +41,20 @@ func cacheSize(block uint64) uint64 {
return size return size
} }
// datasetSize calculates and returns the size of the ethash mining dataset that // datasetSize returns the size of the ethash mining dataset that belongs to a certain
// belongs to a certain block number. The dataset size grows linearly, however, we // block number.
// always take the highest prime below the linearly growing threshold in order to
// reduce the risk of accidental regularities leading to cyclic behavior.
func datasetSize(block uint64) uint64 { func datasetSize(block uint64) uint64 {
// If we have a pre-generated value, use that
epoch := int(block / epochLength) epoch := int(block / epochLength)
if epoch < len(datasetSizes) { if epoch < maxEpoch {
return datasetSizes[epoch] return datasetSizes[epoch]
} }
// No known dataset size, calculate manually (sanity branch only) return calcDatasetSize(epoch)
}
// calcDatasetSize calculates the dataset size for epoch. The dataset size grows linearly,
// however, we always take the highest prime below the linearly growing threshold in order
// to reduce the risk of accidental regularities leading to cyclic behavior.
func calcDatasetSize(epoch int) uint64 {
size := datasetInitBytes + datasetGrowthBytes*uint64(epoch) - mixBytes size := datasetInitBytes + datasetGrowthBytes*uint64(epoch) - mixBytes
for !new(big.Int).SetUint64(size / mixBytes).ProbablyPrime(1) { // Always accurate for n < 2^64 for !new(big.Int).SetUint64(size / mixBytes).ProbablyPrime(1) { // Always accurate for n < 2^64
size -= 2 * mixBytes size -= 2 * mixBytes

View file

@ -23,24 +23,15 @@ import "testing"
// Tests whether the dataset size calculator works correctly by cross checking the // Tests whether the dataset size calculator works correctly by cross checking the
// hard coded lookup table with the value generated by it. // hard coded lookup table with the value generated by it.
func TestSizeCalculations(t *testing.T) { func TestSizeCalculations(t *testing.T) {
var tests []uint64 // Verify all the cache and dataset sizes from the lookup table.
for epoch, want := range cacheSizes {
// Verify all the cache sizes from the lookup table if size := calcCacheSize(epoch); size != want {
defer func(sizes []uint64) { cacheSizes = sizes }(cacheSizes) t.Errorf("cache %d: cache size mismatch: have %d, want %d", epoch, size, want)
tests, cacheSizes = cacheSizes, []uint64{}
for i, test := range tests {
if size := cacheSize(uint64(i*epochLength) + 1); size != test {
t.Errorf("cache %d: cache size mismatch: have %d, want %d", i, size, test)
} }
} }
// Verify all the dataset sizes from the lookup table for epoch, want := range datasetSizes {
defer func(sizes []uint64) { datasetSizes = sizes }(datasetSizes) if size := calcDatasetSize(epoch); size != want {
tests, datasetSizes = datasetSizes, []uint64{} t.Errorf("dataset %d: dataset size mismatch: have %d, want %d", epoch, size, want)
for i, test := range tests {
if size := datasetSize(uint64(i*epochLength) + 1); size != test {
t.Errorf("dataset %d: dataset size mismatch: have %d, want %d", i, size, test)
} }
} }
} }

View file

@ -476,7 +476,7 @@ func (ethash *Ethash) VerifySeal(chain consensus.ChainReader, header *types.Head
} }
// Sanity check that the block number is below the lookup table size (60M blocks) // Sanity check that the block number is below the lookup table size (60M blocks)
number := header.Number.Uint64() number := header.Number.Uint64()
if number/epochLength >= uint64(len(cacheSizes)) { if number/epochLength >= maxEpoch {
// Go < 1.7 cannot calculate new cache/dataset sizes (no fast prime check) // Go < 1.7 cannot calculate new cache/dataset sizes (no fast prime check)
return errNonceOutOfRange return errNonceOutOfRange
} }
@ -484,14 +484,18 @@ func (ethash *Ethash) VerifySeal(chain consensus.ChainReader, header *types.Head
if header.Difficulty.Sign() <= 0 { if header.Difficulty.Sign() <= 0 {
return errInvalidDifficulty return errInvalidDifficulty
} }
// Recompute the digest and PoW value and verify against the header // Recompute the digest and PoW value and verify against the header
cache := ethash.cache(number) cache := ethash.cache(number)
size := datasetSize(number) size := datasetSize(number)
if ethash.config.PowMode == ModeTest { if ethash.config.PowMode == ModeTest {
size = 32 * 1024 size = 32 * 1024
} }
digest, result := hashimotoLight(size, cache, header.HashNoNonce().Bytes(), header.Nonce.Uint64()) digest, result := hashimotoLight(size, cache.cache, header.HashNoNonce().Bytes(), header.Nonce.Uint64())
// Caches are unmapped in a finalizer. Ensure that the cache stays live
// until after the call to hashimotoLight so it's not unmapped while being used.
runtime.KeepAlive(cache)
if !bytes.Equal(header.MixDigest[:], digest) { if !bytes.Equal(header.MixDigest[:], digest) {
return errInvalidMixDigest return errInvalidMixDigest
} }

View file

@ -26,6 +26,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"reflect" "reflect"
"runtime"
"strconv" "strconv"
"sync" "sync"
"time" "time"
@ -35,6 +36,7 @@ import (
"github.com/ethereum/go-ethereum/consensus" "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/hashicorp/golang-lru/simplelru"
metrics "github.com/rcrowley/go-metrics" metrics "github.com/rcrowley/go-metrics"
) )
@ -142,32 +144,82 @@ func memoryMapAndGenerate(path string, size uint64, generator func(buffer []uint
return memoryMap(path) return memoryMap(path)
} }
// lru tracks caches or datasets by their last use time, keeping at most N of them.
type lru struct {
what string
new func(epoch uint64) interface{}
mu sync.Mutex
// Items are kept in a LRU cache, but there is a special case:
// We always keep an item for (highest seen epoch) + 1 as the 'future item'.
cache *simplelru.LRU
future uint64
futureItem interface{}
}
// newlru create a new least-recently-used cache for ither the verification caches
// or the mining datasets.
func newlru(what string, maxItems int, new func(epoch uint64) interface{}) *lru {
if maxItems <= 0 {
maxItems = 1
}
cache, _ := simplelru.NewLRU(maxItems, func(key, value interface{}) {
log.Trace("Evicted ethash "+what, "epoch", key)
})
return &lru{what: what, new: new, cache: cache}
}
// get retrieves or creates an item for the given epoch. The first return value is always
// non-nil. The second return value is non-nil if lru thinks that an item will be useful in
// the near future.
func (lru *lru) get(epoch uint64) (item, future interface{}) {
lru.mu.Lock()
defer lru.mu.Unlock()
// Get or create the item for the requested epoch.
item, ok := lru.cache.Get(epoch)
if !ok {
if lru.future > 0 && lru.future == epoch {
item = lru.futureItem
} else {
log.Trace("Requiring new ethash "+lru.what, "epoch", epoch)
item = lru.new(epoch)
}
lru.cache.Add(epoch, item)
}
// Update the 'future item' if epoch is larger than previously seen.
if epoch < maxEpoch-1 && lru.future < epoch+1 {
log.Trace("Requiring new future ethash "+lru.what, "epoch", epoch+1)
future = lru.new(epoch + 1)
lru.future = epoch + 1
lru.futureItem = future
}
return item, future
}
// cache wraps an ethash cache with some metadata to allow easier concurrent use. // cache wraps an ethash cache with some metadata to allow easier concurrent use.
type cache struct { type cache struct {
epoch uint64 // Epoch for which this cache is relevant epoch uint64 // Epoch for which this cache is relevant
dump *os.File // File descriptor of the memory mapped cache
mmap mmap.MMap // Memory map itself to unmap before releasing
cache []uint32 // The actual cache data content (may be memory mapped)
once sync.Once // Ensures the cache is generated only once
}
dump *os.File // File descriptor of the memory mapped cache // newCache creates a new ethash verification cache and returns it as a plain Go
mmap mmap.MMap // Memory map itself to unmap before releasing // interface to be usable in an LRU cache.
func newCache(epoch uint64) interface{} {
cache []uint32 // The actual cache data content (may be memory mapped) return &cache{epoch: epoch}
used time.Time // Timestamp of the last use for smarter eviction
once sync.Once // Ensures the cache is generated only once
lock sync.Mutex // Ensures thread safety for updating the usage time
} }
// generate ensures that the cache content is generated before use. // generate ensures that the cache content is generated before use.
func (c *cache) generate(dir string, limit int, test bool) { func (c *cache) generate(dir string, limit int, test bool) {
c.once.Do(func() { c.once.Do(func() {
// If we have a testing cache, generate and return
if test {
c.cache = make([]uint32, 1024/4)
generateCache(c.cache, c.epoch, seedHash(c.epoch*epochLength+1))
return
}
// If we don't store anything on disk, generate and return
size := cacheSize(c.epoch*epochLength + 1) size := cacheSize(c.epoch*epochLength + 1)
seed := seedHash(c.epoch*epochLength + 1) seed := seedHash(c.epoch*epochLength + 1)
if test {
size = 1024
}
// If we don't store anything on disk, generate and return.
if dir == "" { if dir == "" {
c.cache = make([]uint32, size/4) c.cache = make([]uint32, size/4)
generateCache(c.cache, c.epoch, seed) generateCache(c.cache, c.epoch, seed)
@ -181,6 +233,10 @@ func (c *cache) generate(dir string, limit int, test bool) {
path := filepath.Join(dir, fmt.Sprintf("cache-R%d-%x%s", algorithmRevision, seed[:8], endian)) path := filepath.Join(dir, fmt.Sprintf("cache-R%d-%x%s", algorithmRevision, seed[:8], endian))
logger := log.New("epoch", c.epoch) logger := log.New("epoch", c.epoch)
// We're about to mmap the file, ensure that the mapping is cleaned up when the
// cache becomes unused.
runtime.SetFinalizer(c, (*cache).finalizer)
// Try to load the file from disk and memory map it // Try to load the file from disk and memory map it
var err error var err error
c.dump, c.mmap, c.cache, err = memoryMap(path) c.dump, c.mmap, c.cache, err = memoryMap(path)
@ -207,49 +263,41 @@ func (c *cache) generate(dir string, limit int, test bool) {
}) })
} }
// release closes any file handlers and memory maps open. // finalizer unmaps the memory and closes the file.
func (c *cache) release() { func (c *cache) finalizer() {
if c.mmap != nil { if c.mmap != nil {
c.mmap.Unmap() c.mmap.Unmap()
c.mmap = nil
}
if c.dump != nil {
c.dump.Close() c.dump.Close()
c.dump = nil c.mmap, c.dump = nil, nil
} }
} }
// dataset wraps an ethash dataset with some metadata to allow easier concurrent use. // dataset wraps an ethash dataset with some metadata to allow easier concurrent use.
type dataset struct { type dataset struct {
epoch uint64 // Epoch for which this cache is relevant epoch uint64 // Epoch for which this cache is relevant
dump *os.File // File descriptor of the memory mapped cache
mmap mmap.MMap // Memory map itself to unmap before releasing
dataset []uint32 // The actual cache data content
once sync.Once // Ensures the cache is generated only once
}
dump *os.File // File descriptor of the memory mapped cache // newDataset creates a new ethash mining dataset and returns it as a plain Go
mmap mmap.MMap // Memory map itself to unmap before releasing // interface to be usable in an LRU cache.
func newDataset(epoch uint64) interface{} {
dataset []uint32 // The actual cache data content return &dataset{epoch: epoch}
used time.Time // Timestamp of the last use for smarter eviction
once sync.Once // Ensures the cache is generated only once
lock sync.Mutex // Ensures thread safety for updating the usage time
} }
// generate ensures that the dataset content is generated before use. // generate ensures that the dataset content is generated before use.
func (d *dataset) generate(dir string, limit int, test bool) { func (d *dataset) generate(dir string, limit int, test bool) {
d.once.Do(func() { d.once.Do(func() {
// If we have a testing dataset, generate and return
if test {
cache := make([]uint32, 1024/4)
generateCache(cache, d.epoch, seedHash(d.epoch*epochLength+1))
d.dataset = make([]uint32, 32*1024/4)
generateDataset(d.dataset, d.epoch, cache)
return
}
// If we don't store anything on disk, generate and return
csize := cacheSize(d.epoch*epochLength + 1) csize := cacheSize(d.epoch*epochLength + 1)
dsize := datasetSize(d.epoch*epochLength + 1) dsize := datasetSize(d.epoch*epochLength + 1)
seed := seedHash(d.epoch*epochLength + 1) seed := seedHash(d.epoch*epochLength + 1)
if test {
csize = 1024
dsize = 32 * 1024
}
// If we don't store anything on disk, generate and return
if dir == "" { if dir == "" {
cache := make([]uint32, csize/4) cache := make([]uint32, csize/4)
generateCache(cache, d.epoch, seed) generateCache(cache, d.epoch, seed)
@ -265,6 +313,10 @@ func (d *dataset) generate(dir string, limit int, test bool) {
path := filepath.Join(dir, fmt.Sprintf("full-R%d-%x%s", algorithmRevision, seed[:8], endian)) path := filepath.Join(dir, fmt.Sprintf("full-R%d-%x%s", algorithmRevision, seed[:8], endian))
logger := log.New("epoch", d.epoch) logger := log.New("epoch", d.epoch)
// We're about to mmap the file, ensure that the mapping is cleaned up when the
// cache becomes unused.
runtime.SetFinalizer(d, (*dataset).finalizer)
// Try to load the file from disk and memory map it // Try to load the file from disk and memory map it
var err error var err error
d.dump, d.mmap, d.dataset, err = memoryMap(path) d.dump, d.mmap, d.dataset, err = memoryMap(path)
@ -294,15 +346,12 @@ func (d *dataset) generate(dir string, limit int, test bool) {
}) })
} }
// release closes any file handlers and memory maps open. // finalizer closes any file handlers and memory maps open.
func (d *dataset) release() { func (d *dataset) finalizer() {
if d.mmap != nil { if d.mmap != nil {
d.mmap.Unmap() d.mmap.Unmap()
d.mmap = nil
}
if d.dump != nil {
d.dump.Close() d.dump.Close()
d.dump = nil d.mmap, d.dump = nil, nil
} }
} }
@ -310,14 +359,12 @@ func (d *dataset) release() {
func MakeCache(block uint64, dir string) { func MakeCache(block uint64, dir string) {
c := cache{epoch: block / epochLength} c := cache{epoch: block / epochLength}
c.generate(dir, math.MaxInt32, false) c.generate(dir, math.MaxInt32, false)
c.release()
} }
// MakeDataset generates a new ethash dataset and optionally stores it to disk. // MakeDataset generates a new ethash dataset and optionally stores it to disk.
func MakeDataset(block uint64, dir string) { func MakeDataset(block uint64, dir string) {
d := dataset{epoch: block / epochLength} d := dataset{epoch: block / epochLength}
d.generate(dir, math.MaxInt32, false) d.generate(dir, math.MaxInt32, false)
d.release()
} }
// Mode defines the type and amount of PoW verification an ethash engine makes. // Mode defines the type and amount of PoW verification an ethash engine makes.
@ -347,10 +394,8 @@ type Config struct {
type Ethash struct { type Ethash struct {
config Config config Config
caches map[uint64]*cache // In memory caches to avoid regenerating too often caches *lru // In memory caches to avoid regenerating too often
fcache *cache // Pre-generated cache for the estimated future epoch datasets *lru // In memory datasets to avoid regenerating too often
datasets map[uint64]*dataset // In memory datasets to avoid regenerating too often
fdataset *dataset // Pre-generated dataset for the estimated future epoch
// Mining related fields // Mining related fields
rand *rand.Rand // Properly seeded random source for nonces rand *rand.Rand // Properly seeded random source for nonces
@ -380,8 +425,8 @@ func New(config Config) *Ethash {
} }
return &Ethash{ return &Ethash{
config: config, config: config,
caches: make(map[uint64]*cache), caches: newlru("cache", config.CachesInMem, newCache),
datasets: make(map[uint64]*dataset), datasets: newlru("dataset", config.DatasetsInMem, newDataset),
update: make(chan struct{}), update: make(chan struct{}),
hashrate: metrics.NewMeter(), hashrate: metrics.NewMeter(),
} }
@ -390,16 +435,7 @@ func New(config Config) *Ethash {
// NewTester creates a small sized ethash PoW scheme useful only for testing // NewTester creates a small sized ethash PoW scheme useful only for testing
// purposes. // purposes.
func NewTester() *Ethash { func NewTester() *Ethash {
return &Ethash{ return New(Config{CachesInMem: 1, PowMode: ModeTest})
config: Config{
CachesInMem: 1,
PowMode: ModeTest,
},
caches: make(map[uint64]*cache),
datasets: make(map[uint64]*dataset),
update: make(chan struct{}),
hashrate: metrics.NewMeter(),
}
} }
// NewFaker creates a ethash consensus engine with a fake PoW scheme that accepts // NewFaker creates a ethash consensus engine with a fake PoW scheme that accepts
@ -456,126 +492,40 @@ func NewShared() *Ethash {
// cache tries to retrieve a verification cache for the specified block number // cache tries to retrieve a verification cache for the specified block number
// by first checking against a list of in-memory caches, then against caches // by first checking against a list of in-memory caches, then against caches
// stored on disk, and finally generating one if none can be found. // stored on disk, and finally generating one if none can be found.
func (ethash *Ethash) cache(block uint64) []uint32 { func (ethash *Ethash) cache(block uint64) *cache {
epoch := block / epochLength epoch := block / epochLength
currentI, futureI := ethash.caches.get(epoch)
current := currentI.(*cache)
// If we have a PoW for that epoch, use that // Wait for generation finish.
ethash.lock.Lock()
current, future := ethash.caches[epoch], (*cache)(nil)
if current == nil {
// No in-memory cache, evict the oldest if the cache limit was reached
for len(ethash.caches) > 0 && len(ethash.caches) >= ethash.config.CachesInMem {
var evict *cache
for _, cache := range ethash.caches {
if evict == nil || evict.used.After(cache.used) {
evict = cache
}
}
delete(ethash.caches, evict.epoch)
evict.release()
log.Trace("Evicted ethash cache", "epoch", evict.epoch, "used", evict.used)
}
// If we have the new cache pre-generated, use that, otherwise create a new one
if ethash.fcache != nil && ethash.fcache.epoch == epoch {
log.Trace("Using pre-generated cache", "epoch", epoch)
current, ethash.fcache = ethash.fcache, nil
} else {
log.Trace("Requiring new ethash cache", "epoch", epoch)
current = &cache{epoch: epoch}
}
ethash.caches[epoch] = current
// If we just used up the future cache, or need a refresh, regenerate
if ethash.fcache == nil || ethash.fcache.epoch <= epoch {
if ethash.fcache != nil {
ethash.fcache.release()
}
log.Trace("Requiring new future ethash cache", "epoch", epoch+1)
future = &cache{epoch: epoch + 1}
ethash.fcache = future
}
// New current cache, set its initial timestamp
current.used = time.Now()
}
ethash.lock.Unlock()
// Wait for generation finish, bump the timestamp and finalize the cache
current.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest) current.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest)
current.lock.Lock() // If we need a new future cache, now's a good time to regenerate it.
current.used = time.Now() if futureI != nil {
current.lock.Unlock() future := futureI.(*cache)
// If we exhausted the future cache, now's a good time to regenerate it
if future != nil {
go future.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest) go future.generate(ethash.config.CacheDir, ethash.config.CachesOnDisk, ethash.config.PowMode == ModeTest)
} }
return current.cache return current
} }
// dataset tries to retrieve a mining dataset for the specified block number // dataset tries to retrieve a mining dataset for the specified block number
// by first checking against a list of in-memory datasets, then against DAGs // by first checking against a list of in-memory datasets, then against DAGs
// stored on disk, and finally generating one if none can be found. // stored on disk, and finally generating one if none can be found.
func (ethash *Ethash) dataset(block uint64) []uint32 { func (ethash *Ethash) dataset(block uint64) *dataset {
epoch := block / epochLength epoch := block / epochLength
currentI, futureI := ethash.datasets.get(epoch)
current := currentI.(*dataset)
// If we have a PoW for that epoch, use that // Wait for generation finish.
ethash.lock.Lock()
current, future := ethash.datasets[epoch], (*dataset)(nil)
if current == nil {
// No in-memory dataset, evict the oldest if the dataset limit was reached
for len(ethash.datasets) > 0 && len(ethash.datasets) >= ethash.config.DatasetsInMem {
var evict *dataset
for _, dataset := range ethash.datasets {
if evict == nil || evict.used.After(dataset.used) {
evict = dataset
}
}
delete(ethash.datasets, evict.epoch)
evict.release()
log.Trace("Evicted ethash dataset", "epoch", evict.epoch, "used", evict.used)
}
// If we have the new cache pre-generated, use that, otherwise create a new one
if ethash.fdataset != nil && ethash.fdataset.epoch == epoch {
log.Trace("Using pre-generated dataset", "epoch", epoch)
current = &dataset{epoch: ethash.fdataset.epoch} // Reload from disk
ethash.fdataset = nil
} else {
log.Trace("Requiring new ethash dataset", "epoch", epoch)
current = &dataset{epoch: epoch}
}
ethash.datasets[epoch] = current
// If we just used up the future dataset, or need a refresh, regenerate
if ethash.fdataset == nil || ethash.fdataset.epoch <= epoch {
if ethash.fdataset != nil {
ethash.fdataset.release()
}
log.Trace("Requiring new future ethash dataset", "epoch", epoch+1)
future = &dataset{epoch: epoch + 1}
ethash.fdataset = future
}
// New current dataset, set its initial timestamp
current.used = time.Now()
}
ethash.lock.Unlock()
// Wait for generation finish, bump the timestamp and finalize the cache
current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest) current.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
current.lock.Lock() // If we need a new future dataset, now's a good time to regenerate it.
current.used = time.Now() if futureI != nil {
current.lock.Unlock() future := futureI.(*dataset)
// If we exhausted the future dataset, now's a good time to regenerate it
if future != nil {
go future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest) go future.generate(ethash.config.DatasetDir, ethash.config.DatasetsOnDisk, ethash.config.PowMode == ModeTest)
} }
return current.dataset
return current
} }
// Threads returns the number of mining threads currently enabled. This doesn't // Threads returns the number of mining threads currently enabled. This doesn't

View file

@ -17,7 +17,11 @@
package ethash package ethash
import ( import (
"io/ioutil"
"math/big" "math/big"
"math/rand"
"os"
"sync"
"testing" "testing"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
@ -38,3 +42,38 @@ func TestTestMode(t *testing.T) {
t.Fatalf("unexpected verification error: %v", err) t.Fatalf("unexpected verification error: %v", err)
} }
} }
// This test checks that cache lru logic doesn't crash under load.
// It reproduces https://github.com/ethereum/go-ethereum/issues/14943
func TestCacheFileEvict(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "ethash-test")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpdir)
e := New(Config{CachesInMem: 3, CachesOnDisk: 10, CacheDir: tmpdir, PowMode: ModeTest})
workers := 8
epochs := 100
var wg sync.WaitGroup
wg.Add(workers)
for i := 0; i < workers; i++ {
go verifyTest(&wg, e, i, epochs)
}
wg.Wait()
}
func verifyTest(wg *sync.WaitGroup, e *Ethash, workerIndex, epochs int) {
defer wg.Done()
const wiggle = 4 * epochLength
r := rand.New(rand.NewSource(int64(workerIndex)))
for epoch := 0; epoch < epochs; epoch++ {
block := int64(epoch)*epochLength - wiggle/2 + r.Int63n(wiggle)
if block < 0 {
block = 0
}
head := &types.Header{Number: big.NewInt(block), Difficulty: big.NewInt(100)}
e.VerifySeal(nil, head)
}
}

View file

@ -97,10 +97,9 @@ func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, stop
func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan struct{}, found chan *types.Block) { func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan struct{}, found chan *types.Block) {
// Extract some data from the header // Extract some data from the header
var ( var (
header = block.Header() header = block.Header()
hash = header.HashNoNonce().Bytes() hash = header.HashNoNonce().Bytes()
target = new(big.Int).Div(maxUint256, header.Difficulty) target = new(big.Int).Div(maxUint256, header.Difficulty)
number = header.Number.Uint64() number = header.Number.Uint64()
dataset = ethash.dataset(number) dataset = ethash.dataset(number)
) )
@ -111,13 +110,14 @@ func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan s
) )
logger := log.New("miner", id) logger := log.New("miner", id)
logger.Trace("Started ethash search for new nonces", "seed", seed) logger.Trace("Started ethash search for new nonces", "seed", seed)
search:
for { for {
select { select {
case <-abort: case <-abort:
// Mining terminated, update stats and abort // Mining terminated, update stats and abort
logger.Trace("Ethash nonce search aborted", "attempts", nonce-seed) logger.Trace("Ethash nonce search aborted", "attempts", nonce-seed)
ethash.hashrate.Mark(attempts) ethash.hashrate.Mark(attempts)
return break search
default: default:
// We don't have to update hash rate on every nonce, so update after after 2^X nonces // We don't have to update hash rate on every nonce, so update after after 2^X nonces
@ -127,7 +127,7 @@ func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan s
attempts = 0 attempts = 0
} }
// Compute the PoW value of this nonce // Compute the PoW value of this nonce
digest, result := hashimotoFull(dataset, hash, nonce) digest, result := hashimotoFull(dataset.dataset, hash, nonce)
if new(big.Int).SetBytes(result).Cmp(target) <= 0 { if new(big.Int).SetBytes(result).Cmp(target) <= 0 {
// Correct nonce found, create a new header with it // Correct nonce found, create a new header with it
header = types.CopyHeader(header) header = types.CopyHeader(header)
@ -141,9 +141,12 @@ func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan s
case <-abort: case <-abort:
logger.Trace("Ethash nonce found but discarded", "attempts", nonce-seed, "nonce", nonce) logger.Trace("Ethash nonce found but discarded", "attempts", nonce-seed, "nonce", nonce)
} }
return break search
} }
nonce++ nonce++
} }
} }
// Datasets are unmapped in a finalizer. Ensure that the dataset stays live
// during sealing so it's not unmapped while being read.
runtime.KeepAlive(dataset)
} }

View file

@ -2,7 +2,7 @@ FROM alpine:3.7
RUN \ RUN \
apk add --update go git make gcc musl-dev linux-headers ca-certificates && \ apk add --update go git make gcc musl-dev linux-headers ca-certificates && \
git clone --depth 1 --branch release/1.7 https://github.com/ethereum/go-ethereum && \ git clone --depth 1 --branch release/1.8 https://github.com/ethereum/go-ethereum && \
(cd go-ethereum && make geth) && \ (cd go-ethereum && make geth) && \
cp go-ethereum/build/bin/geth /geth && \ cp go-ethereum/build/bin/geth /geth && \
apk del go git make gcc musl-dev linux-headers && \ apk del go git make gcc musl-dev linux-headers && \

View file

@ -5,7 +5,7 @@ ENV PATH=/usr/lib/go-1.9/bin:$PATH
RUN \ RUN \
apt-get update && apt-get upgrade -q -y && \ apt-get update && apt-get upgrade -q -y && \
apt-get install -y --no-install-recommends golang-1.9 git make gcc libc-dev ca-certificates && \ apt-get install -y --no-install-recommends golang-1.9 git make gcc libc-dev ca-certificates && \
git clone --depth 1 --branch release/1.7 https://github.com/ethereum/go-ethereum && \ git clone --depth 1 --branch release/1.8 https://github.com/ethereum/go-ethereum && \
(cd go-ethereum && make geth) && \ (cd go-ethereum && make geth) && \
cp go-ethereum/build/bin/geth /geth && \ cp go-ethereum/build/bin/geth /geth && \
apt-get remove -y golang-1.9 git make gcc libc-dev && apt autoremove -y && apt-get clean && \ apt-get remove -y golang-1.9 git make gcc libc-dev && apt autoremove -y && apt-get clean && \

View file

@ -281,8 +281,8 @@ func TestDeposit(t *testing.T) {
t.Fatalf("expected balance %v, got %v", exp, chbook.Balance()) t.Fatalf("expected balance %v, got %v", exp, chbook.Balance())
} }
// autodeposit every 30ms if new cheque issued // autodeposit every 200ms if new cheque issued
interval := 30 * time.Millisecond interval := 200 * time.Millisecond
chbook.AutoDeposit(interval, common.Big1, balance) chbook.AutoDeposit(interval, common.Big1, balance)
_, err = chbook.Issue(addr1, amount) _, err = chbook.Issue(addr1, amount)
if err != nil { if err != nil {

View file

@ -7,17 +7,19 @@ import (
"math/big" "math/big"
"strings" "strings"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
) )
// ChequebookABI is the input ABI used to generate the binding from. // ChequebookABI is the input ABI used to generate the binding from.
const ChequebookABI = "[{\"constant\":false,\"inputs\":[],\"name\":\"kill\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"sent\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"beneficiary\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"},{\"name\":\"sig_v\",\"type\":\"uint8\"},{\"name\":\"sig_r\",\"type\":\"bytes32\"},{\"name\":\"sig_s\",\"type\":\"bytes32\"}],\"name\":\"cash\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"deadbeat\",\"type\":\"address\"}],\"name\":\"Overdraft\",\"type\":\"event\"}]" const ChequebookABI = "[{\"constant\":false,\"inputs\":[],\"name\":\"kill\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"address\"}],\"name\":\"sent\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"beneficiary\",\"type\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\"},{\"name\":\"sig_v\",\"type\":\"uint8\"},{\"name\":\"sig_r\",\"type\":\"bytes32\"},{\"name\":\"sig_s\",\"type\":\"bytes32\"}],\"name\":\"cash\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"deadbeat\",\"type\":\"address\"}],\"name\":\"Overdraft\",\"type\":\"event\"}]"
// ChequebookBin is the compiled bytecode used for deploying new contracts. // ChequebookBin is the compiled bytecode used for deploying new contracts.
const ChequebookBin = `0x606060405260008054600160a060020a033316600160a060020a03199091161790556102ec806100306000396000f3006060604052600436106100565763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166341c0e1b581146100585780637bf786f81461006b578063fbf788d61461009c575b005b341561006357600080fd5b6100566100ca565b341561007657600080fd5b61008a600160a060020a03600435166100f1565b60405190815260200160405180910390f35b34156100a757600080fd5b610056600160a060020a036004351660243560ff60443516606435608435610103565b60005433600160a060020a03908116911614156100ef57600054600160a060020a0316ff5b565b60016020526000908152604090205481565b600160a060020a0385166000908152600160205260408120548190861161012957600080fd5b3087876040516c01000000000000000000000000600160a060020a03948516810282529290931690910260148301526028820152604801604051809103902091506001828686866040516000815260200160405260006040516020015260405193845260ff90921660208085019190915260408085019290925260608401929092526080909201915160208103908084039060008661646e5a03f115156101cf57600080fd5b505060206040510351600054600160a060020a039081169116146101f257600080fd5b50600160a060020a03808716600090815260016020526040902054860390301631811161026257600160a060020a0387166000818152600160205260409081902088905582156108fc0290839051600060405180830381858888f19350505050151561025d57600080fd5b6102b7565b6000547f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f97890600160a060020a0316604051600160a060020a03909116815260200160405180910390a186600160a060020a0316ff5b505050505050505600a165627a7a7230582014e927522ca5cd8f68529ac4d3b9cdf36d40e09d8a33b70008248d1abebf79680029` const ChequebookBin = `0x606060405260008054600160a060020a033316600160a060020a03199091161790556102ec806100306000396000f3006060604052600436106100565763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166341c0e1b581146100585780637bf786f81461006b578063fbf788d61461009c575b005b341561006357600080fd5b6100566100ca565b341561007657600080fd5b61008a600160a060020a03600435166100f1565b60405190815260200160405180910390f35b34156100a757600080fd5b610056600160a060020a036004351660243560ff60443516606435608435610103565b60005433600160a060020a03908116911614156100ef57600054600160a060020a0316ff5b565b60016020526000908152604090205481565b600160a060020a0385166000908152600160205260408120548190861161012957600080fd5b3087876040516c01000000000000000000000000600160a060020a03948516810282529290931690910260148301526028820152604801604051809103902091506001828686866040516000815260200160405260006040516020015260405193845260ff90921660208085019190915260408085019290925260608401929092526080909201915160208103908084039060008661646e5a03f115156101cf57600080fd5b505060206040510351600054600160a060020a039081169116146101f257600080fd5b50600160a060020a03808716600090815260016020526040902054860390301631811161026257600160a060020a0387166000818152600160205260409081902088905582156108fc0290839051600060405180830381858888f19350505050151561025d57600080fd5b6102b7565b6000547f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f97890600160a060020a0316604051600160a060020a03909116815260200160405180910390a186600160a060020a0316ff5b505050505050505600a165627a7a72305820533e856fc37e3d64d1706bcc7dfb6b1d490c8d566ea498d9d01ec08965a896ca0029`
// DeployChequebook deploys a new Ethereum contract, binding an instance of Chequebook to it. // DeployChequebook deploys a new Ethereum contract, binding an instance of Chequebook to it.
func DeployChequebook(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Chequebook, error) { func DeployChequebook(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Chequebook, error) {
@ -29,13 +31,14 @@ func DeployChequebook(auth *bind.TransactOpts, backend bind.ContractBackend) (co
if err != nil { if err != nil {
return common.Address{}, nil, nil, err return common.Address{}, nil, nil, err
} }
return address, tx, &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}}, nil return address, tx, &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}, ChequebookFilterer: ChequebookFilterer{contract: contract}}, nil
} }
// Chequebook is an auto generated Go binding around an Ethereum contract. // Chequebook is an auto generated Go binding around an Ethereum contract.
type Chequebook struct { type Chequebook struct {
ChequebookCaller // Read-only binding to the contract ChequebookCaller // Read-only binding to the contract
ChequebookTransactor // Write-only binding to the contract ChequebookTransactor // Write-only binding to the contract
ChequebookFilterer // Log filterer for contract events
} }
// ChequebookCaller is an auto generated read-only Go binding around an Ethereum contract. // ChequebookCaller is an auto generated read-only Go binding around an Ethereum contract.
@ -48,6 +51,11 @@ type ChequebookTransactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls contract *bind.BoundContract // Generic contract wrapper for the low level calls
} }
// ChequebookFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
type ChequebookFilterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// ChequebookSession is an auto generated Go binding around an Ethereum contract, // ChequebookSession is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options. // with pre-set call and transact options.
type ChequebookSession struct { type ChequebookSession struct {
@ -87,16 +95,16 @@ type ChequebookTransactorRaw struct {
// NewChequebook creates a new instance of Chequebook, bound to a specific deployed contract. // NewChequebook creates a new instance of Chequebook, bound to a specific deployed contract.
func NewChequebook(address common.Address, backend bind.ContractBackend) (*Chequebook, error) { func NewChequebook(address common.Address, backend bind.ContractBackend) (*Chequebook, error) {
contract, err := bindChequebook(address, backend, backend) contract, err := bindChequebook(address, backend, backend, backend)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}}, nil return &Chequebook{ChequebookCaller: ChequebookCaller{contract: contract}, ChequebookTransactor: ChequebookTransactor{contract: contract}, ChequebookFilterer: ChequebookFilterer{contract: contract}}, nil
} }
// NewChequebookCaller creates a new read-only instance of Chequebook, bound to a specific deployed contract. // NewChequebookCaller creates a new read-only instance of Chequebook, bound to a specific deployed contract.
func NewChequebookCaller(address common.Address, caller bind.ContractCaller) (*ChequebookCaller, error) { func NewChequebookCaller(address common.Address, caller bind.ContractCaller) (*ChequebookCaller, error) {
contract, err := bindChequebook(address, caller, nil) contract, err := bindChequebook(address, caller, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -105,20 +113,29 @@ func NewChequebookCaller(address common.Address, caller bind.ContractCaller) (*C
// NewChequebookTransactor creates a new write-only instance of Chequebook, bound to a specific deployed contract. // NewChequebookTransactor creates a new write-only instance of Chequebook, bound to a specific deployed contract.
func NewChequebookTransactor(address common.Address, transactor bind.ContractTransactor) (*ChequebookTransactor, error) { func NewChequebookTransactor(address common.Address, transactor bind.ContractTransactor) (*ChequebookTransactor, error) {
contract, err := bindChequebook(address, nil, transactor) contract, err := bindChequebook(address, nil, transactor, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &ChequebookTransactor{contract: contract}, nil return &ChequebookTransactor{contract: contract}, nil
} }
// NewChequebookFilterer creates a new log filterer instance of Chequebook, bound to a specific deployed contract.
func NewChequebookFilterer(address common.Address, filterer bind.ContractFilterer) (*ChequebookFilterer, error) {
contract, err := bindChequebook(address, nil, nil, filterer)
if err != nil {
return nil, err
}
return &ChequebookFilterer{contract: contract}, nil
}
// bindChequebook binds a generic wrapper to an already deployed contract. // bindChequebook binds a generic wrapper to an already deployed contract.
func bindChequebook(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { func bindChequebook(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := abi.JSON(strings.NewReader(ChequebookABI)) parsed, err := abi.JSON(strings.NewReader(ChequebookABI))
if err != nil { if err != nil {
return nil, err return nil, err
} }
return bind.NewBoundContract(address, parsed, caller, transactor), nil return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
} }
// Call invokes the (constant) contract method with params as input values and // Call invokes the (constant) contract method with params as input values and
@ -226,3 +243,125 @@ func (_Chequebook *ChequebookSession) Kill() (*types.Transaction, error) {
func (_Chequebook *ChequebookTransactorSession) Kill() (*types.Transaction, error) { func (_Chequebook *ChequebookTransactorSession) Kill() (*types.Transaction, error) {
return _Chequebook.Contract.Kill(&_Chequebook.TransactOpts) return _Chequebook.Contract.Kill(&_Chequebook.TransactOpts)
} }
// ChequebookOverdraftIterator is returned from FilterOverdraft and is used to iterate over the raw logs and unpacked data for Overdraft events raised by the Chequebook contract.
type ChequebookOverdraftIterator struct {
Event *ChequebookOverdraft // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *ChequebookOverdraftIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(ChequebookOverdraft)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(ChequebookOverdraft)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error retruned any retrieval or parsing error occurred during filtering.
func (it *ChequebookOverdraftIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *ChequebookOverdraftIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// ChequebookOverdraft represents a Overdraft event raised by the Chequebook contract.
type ChequebookOverdraft struct {
Deadbeat common.Address
Raw types.Log // Blockchain specific contextual infos
}
// FilterOverdraft is a free log retrieval operation binding the contract event 0x2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f978.
//
// Solidity: event Overdraft(deadbeat address)
func (_Chequebook *ChequebookFilterer) FilterOverdraft(opts *bind.FilterOpts) (*ChequebookOverdraftIterator, error) {
logs, sub, err := _Chequebook.contract.FilterLogs(opts, "Overdraft")
if err != nil {
return nil, err
}
return &ChequebookOverdraftIterator{contract: _Chequebook.contract, event: "Overdraft", logs: logs, sub: sub}, nil
}
// WatchOverdraft is a free log subscription operation binding the contract event 0x2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f978.
//
// Solidity: event Overdraft(deadbeat address)
func (_Chequebook *ChequebookFilterer) WatchOverdraft(opts *bind.WatchOpts, sink chan<- *ChequebookOverdraft) (event.Subscription, error) {
logs, sub, err := _Chequebook.contract.WatchLogs(opts, "Overdraft")
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(ChequebookOverdraft)
if err := _Chequebook.contract.UnpackLog(event, "Overdraft", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}

View file

@ -2,4 +2,4 @@ package contract
// ContractDeployedCode is used to detect suicides. This constant needs to be // ContractDeployedCode is used to detect suicides. This constant needs to be
// updated when the contract code is changed. // updated when the contract code is changed.
const ContractDeployedCode = "0x6060604052600436106100565763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166341c0e1b581146100585780637bf786f81461006b578063fbf788d61461009c575b005b341561006357600080fd5b6100566100ca565b341561007657600080fd5b61008a600160a060020a03600435166100f1565b60405190815260200160405180910390f35b34156100a757600080fd5b610056600160a060020a036004351660243560ff60443516606435608435610103565b60005433600160a060020a03908116911614156100ef57600054600160a060020a0316ff5b565b60016020526000908152604090205481565b600160a060020a0385166000908152600160205260408120548190861161012957600080fd5b3087876040516c01000000000000000000000000600160a060020a03948516810282529290931690910260148301526028820152604801604051809103902091506001828686866040516000815260200160405260006040516020015260405193845260ff90921660208085019190915260408085019290925260608401929092526080909201915160208103908084039060008661646e5a03f115156101cf57600080fd5b505060206040510351600054600160a060020a039081169116146101f257600080fd5b50600160a060020a03808716600090815260016020526040902054860390301631811161026257600160a060020a0387166000818152600160205260409081902088905582156108fc0290839051600060405180830381858888f19350505050151561025d57600080fd5b6102b7565b6000547f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f97890600160a060020a0316604051600160a060020a03909116815260200160405180910390a186600160a060020a0316ff5b505050505050505600a165627a7a7230582014e927522ca5cd8f68529ac4d3b9cdf36d40e09d8a33b70008248d1abebf79680029" const ContractDeployedCode = "0x6060604052600436106100565763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166341c0e1b581146100585780637bf786f81461006b578063fbf788d61461009c575b005b341561006357600080fd5b6100566100ca565b341561007657600080fd5b61008a600160a060020a03600435166100f1565b60405190815260200160405180910390f35b34156100a757600080fd5b610056600160a060020a036004351660243560ff60443516606435608435610103565b60005433600160a060020a03908116911614156100ef57600054600160a060020a0316ff5b565b60016020526000908152604090205481565b600160a060020a0385166000908152600160205260408120548190861161012957600080fd5b3087876040516c01000000000000000000000000600160a060020a03948516810282529290931690910260148301526028820152604801604051809103902091506001828686866040516000815260200160405260006040516020015260405193845260ff90921660208085019190915260408085019290925260608401929092526080909201915160208103908084039060008661646e5a03f115156101cf57600080fd5b505060206040510351600054600160a060020a039081169116146101f257600080fd5b50600160a060020a03808716600090815260016020526040902054860390301631811161026257600160a060020a0387166000818152600160205260409081902088905582156108fc0290839051600060405180830381858888f19350505050151561025d57600080fd5b6102b7565b6000547f2250e2993c15843b32621c89447cc589ee7a9f049c026986e545d3c2c0c6f97890600160a060020a0316604051600160a060020a03909116815260200160405180910390a186600160a060020a0316ff5b505050505050505600a165627a7a72305820533e856fc37e3d64d1706bcc7dfb6b1d490c8d566ea498d9d01ec08965a896ca0029"

View file

@ -6,17 +6,19 @@ package contract
import ( import (
"strings" "strings"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
) )
// ENSABI is the input ABI used to generate the binding from. // ENSABI is the input ABI used to generate the binding from.
const ENSABI = "[{\"constant\":true,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"}],\"name\":\"resolver\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"}],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"label\",\"type\":\"bytes32\"},{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"setSubnodeOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"ttl\",\"type\":\"uint64\"}],\"name\":\"setTTL\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"}],\"name\":\"ttl\",\"outputs\":[{\"name\":\"\",\"type\":\"uint64\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"resolver\",\"type\":\"address\"}],\"name\":\"setResolver\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"node\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"label\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"NewOwner\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"node\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"node\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"resolver\",\"type\":\"address\"}],\"name\":\"NewResolver\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"node\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"ttl\",\"type\":\"uint64\"}],\"name\":\"NewTTL\",\"type\":\"event\"}]" const ENSABI = "[{\"constant\":true,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"}],\"name\":\"resolver\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"}],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"label\",\"type\":\"bytes32\"},{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"setSubnodeOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"ttl\",\"type\":\"uint64\"}],\"name\":\"setTTL\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"}],\"name\":\"ttl\",\"outputs\":[{\"name\":\"\",\"type\":\"uint64\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"resolver\",\"type\":\"address\"}],\"name\":\"setResolver\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"node\",\"type\":\"bytes32\"},{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"setOwner\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"node\",\"type\":\"bytes32\"},{\"indexed\":true,\"name\":\"label\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"NewOwner\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"node\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"node\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"resolver\",\"type\":\"address\"}],\"name\":\"NewResolver\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"node\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"ttl\",\"type\":\"uint64\"}],\"name\":\"NewTTL\",\"type\":\"event\"}]"
// ENSBin is the compiled bytecode used for deploying new contracts. // ENSBin is the compiled bytecode used for deploying new contracts.
const ENSBin = `0x6060604052341561000f57600080fd5b60008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb58054600160a060020a033316600160a060020a0319909116179055610503806100626000396000f3006060604052600436106100825763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416630178b8bf811461008757806302571be3146100b957806306ab5923146100cf57806314ab9038146100f657806316a25cbd146101195780631896f70a1461014c5780635b0fc9c31461016e575b600080fd5b341561009257600080fd5b61009d600435610190565b604051600160a060020a03909116815260200160405180910390f35b34156100c457600080fd5b61009d6004356101ae565b34156100da57600080fd5b6100f4600435602435600160a060020a03604435166101c9565b005b341561010157600080fd5b6100f460043567ffffffffffffffff6024351661028b565b341561012457600080fd5b61012f600435610357565b60405167ffffffffffffffff909116815260200160405180910390f35b341561015757600080fd5b6100f4600435600160a060020a036024351661038e565b341561017957600080fd5b6100f4600435600160a060020a0360243516610434565b600090815260208190526040902060010154600160a060020a031690565b600090815260208190526040902054600160a060020a031690565b600083815260208190526040812054849033600160a060020a039081169116146101f257600080fd5b8484604051918252602082015260409081019051908190039020915083857fce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e8285604051600160a060020a03909116815260200160405180910390a3506000908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03929092169190911790555050565b600082815260208190526040902054829033600160a060020a039081169116146102b457600080fd5b827f1d4f9bbfc9cab89d66e1a1562f2233ccbf1308cb4f63de2ead5787adddb8fa688360405167ffffffffffffffff909116815260200160405180910390a250600091825260208290526040909120600101805467ffffffffffffffff90921674010000000000000000000000000000000000000000027fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055565b60009081526020819052604090206001015474010000000000000000000000000000000000000000900467ffffffffffffffff1690565b600082815260208190526040902054829033600160a060020a039081169116146103b757600080fd5b827f335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a083604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120600101805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03909216919091179055565b600082815260208190526040902054829033600160a060020a0390811691161461045d57600080fd5b827fd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d26683604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a039092169190911790555600a165627a7a7230582087c335a130f7bd19015451f7e1dc0e44cdeb5b64393f51a105ee00160711fcff0029` const ENSBin = `0x6060604052341561000f57600080fd5b60008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb58054600160a060020a033316600160a060020a0319909116179055610503806100626000396000f3006060604052600436106100825763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416630178b8bf811461008757806302571be3146100b957806306ab5923146100cf57806314ab9038146100f657806316a25cbd146101195780631896f70a1461014c5780635b0fc9c31461016e575b600080fd5b341561009257600080fd5b61009d600435610190565b604051600160a060020a03909116815260200160405180910390f35b34156100c457600080fd5b61009d6004356101ae565b34156100da57600080fd5b6100f4600435602435600160a060020a03604435166101c9565b005b341561010157600080fd5b6100f460043567ffffffffffffffff6024351661028b565b341561012457600080fd5b61012f600435610357565b60405167ffffffffffffffff909116815260200160405180910390f35b341561015757600080fd5b6100f4600435600160a060020a036024351661038e565b341561017957600080fd5b6100f4600435600160a060020a0360243516610434565b600090815260208190526040902060010154600160a060020a031690565b600090815260208190526040902054600160a060020a031690565b600083815260208190526040812054849033600160a060020a039081169116146101f257600080fd5b8484604051918252602082015260409081019051908190039020915083857fce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e8285604051600160a060020a03909116815260200160405180910390a3506000908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03929092169190911790555050565b600082815260208190526040902054829033600160a060020a039081169116146102b457600080fd5b827f1d4f9bbfc9cab89d66e1a1562f2233ccbf1308cb4f63de2ead5787adddb8fa688360405167ffffffffffffffff909116815260200160405180910390a250600091825260208290526040909120600101805467ffffffffffffffff90921674010000000000000000000000000000000000000000027fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055565b60009081526020819052604090206001015474010000000000000000000000000000000000000000900467ffffffffffffffff1690565b600082815260208190526040902054829033600160a060020a039081169116146103b757600080fd5b827f335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a083604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120600101805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03909216919091179055565b600082815260208190526040902054829033600160a060020a0390811691161461045d57600080fd5b827fd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d26683604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a039092169190911790555600a165627a7a72305820f4c798d4c84c9912f389f64631e85e8d16c3e6644f8c2e1579936015c7d5f6660029`
// DeployENS deploys a new Ethereum contract, binding an instance of ENS to it. // DeployENS deploys a new Ethereum contract, binding an instance of ENS to it.
func DeployENS(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *ENS, error) { func DeployENS(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *ENS, error) {
@ -28,13 +30,14 @@ func DeployENS(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Ad
if err != nil { if err != nil {
return common.Address{}, nil, nil, err return common.Address{}, nil, nil, err
} }
return address, tx, &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}}, nil return address, tx, &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}, ENSFilterer: ENSFilterer{contract: contract}}, nil
} }
// ENS is an auto generated Go binding around an Ethereum contract. // ENS is an auto generated Go binding around an Ethereum contract.
type ENS struct { type ENS struct {
ENSCaller // Read-only binding to the contract ENSCaller // Read-only binding to the contract
ENSTransactor // Write-only binding to the contract ENSTransactor // Write-only binding to the contract
ENSFilterer // Log filterer for contract events
} }
// ENSCaller is an auto generated read-only Go binding around an Ethereum contract. // ENSCaller is an auto generated read-only Go binding around an Ethereum contract.
@ -47,6 +50,11 @@ type ENSTransactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls contract *bind.BoundContract // Generic contract wrapper for the low level calls
} }
// ENSFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
type ENSFilterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// ENSSession is an auto generated Go binding around an Ethereum contract, // ENSSession is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options. // with pre-set call and transact options.
type ENSSession struct { type ENSSession struct {
@ -86,16 +94,16 @@ type ENSTransactorRaw struct {
// NewENS creates a new instance of ENS, bound to a specific deployed contract. // NewENS creates a new instance of ENS, bound to a specific deployed contract.
func NewENS(address common.Address, backend bind.ContractBackend) (*ENS, error) { func NewENS(address common.Address, backend bind.ContractBackend) (*ENS, error) {
contract, err := bindENS(address, backend, backend) contract, err := bindENS(address, backend, backend, backend)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}}, nil return &ENS{ENSCaller: ENSCaller{contract: contract}, ENSTransactor: ENSTransactor{contract: contract}, ENSFilterer: ENSFilterer{contract: contract}}, nil
} }
// NewENSCaller creates a new read-only instance of ENS, bound to a specific deployed contract. // NewENSCaller creates a new read-only instance of ENS, bound to a specific deployed contract.
func NewENSCaller(address common.Address, caller bind.ContractCaller) (*ENSCaller, error) { func NewENSCaller(address common.Address, caller bind.ContractCaller) (*ENSCaller, error) {
contract, err := bindENS(address, caller, nil) contract, err := bindENS(address, caller, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -104,20 +112,29 @@ func NewENSCaller(address common.Address, caller bind.ContractCaller) (*ENSCalle
// NewENSTransactor creates a new write-only instance of ENS, bound to a specific deployed contract. // NewENSTransactor creates a new write-only instance of ENS, bound to a specific deployed contract.
func NewENSTransactor(address common.Address, transactor bind.ContractTransactor) (*ENSTransactor, error) { func NewENSTransactor(address common.Address, transactor bind.ContractTransactor) (*ENSTransactor, error) {
contract, err := bindENS(address, nil, transactor) contract, err := bindENS(address, nil, transactor, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &ENSTransactor{contract: contract}, nil return &ENSTransactor{contract: contract}, nil
} }
// NewENSFilterer creates a new log filterer instance of ENS, bound to a specific deployed contract.
func NewENSFilterer(address common.Address, filterer bind.ContractFilterer) (*ENSFilterer, error) {
contract, err := bindENS(address, nil, nil, filterer)
if err != nil {
return nil, err
}
return &ENSFilterer{contract: contract}, nil
}
// bindENS binds a generic wrapper to an already deployed contract. // bindENS binds a generic wrapper to an already deployed contract.
func bindENS(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { func bindENS(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := abi.JSON(strings.NewReader(ENSABI)) parsed, err := abi.JSON(strings.NewReader(ENSABI))
if err != nil { if err != nil {
return nil, err return nil, err
} }
return bind.NewBoundContract(address, parsed, caller, transactor), nil return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
} }
// Call invokes the (constant) contract method with params as input values and // Call invokes the (constant) contract method with params as input values and
@ -319,3 +336,544 @@ func (_ENS *ENSSession) SetTTL(node [32]byte, ttl uint64) (*types.Transaction, e
func (_ENS *ENSTransactorSession) SetTTL(node [32]byte, ttl uint64) (*types.Transaction, error) { func (_ENS *ENSTransactorSession) SetTTL(node [32]byte, ttl uint64) (*types.Transaction, error) {
return _ENS.Contract.SetTTL(&_ENS.TransactOpts, node, ttl) return _ENS.Contract.SetTTL(&_ENS.TransactOpts, node, ttl)
} }
// ENSNewOwnerIterator is returned from FilterNewOwner and is used to iterate over the raw logs and unpacked data for NewOwner events raised by the ENS contract.
type ENSNewOwnerIterator struct {
Event *ENSNewOwner // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *ENSNewOwnerIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(ENSNewOwner)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(ENSNewOwner)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error retruned any retrieval or parsing error occurred during filtering.
func (it *ENSNewOwnerIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *ENSNewOwnerIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// ENSNewOwner represents a NewOwner event raised by the ENS contract.
type ENSNewOwner struct {
Node [32]byte
Label [32]byte
Owner common.Address
Raw types.Log // Blockchain specific contextual infos
}
// FilterNewOwner is a free log retrieval operation binding the contract event 0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82.
//
// Solidity: event NewOwner(node indexed bytes32, label indexed bytes32, owner address)
func (_ENS *ENSFilterer) FilterNewOwner(opts *bind.FilterOpts, node [][32]byte, label [][32]byte) (*ENSNewOwnerIterator, error) {
var nodeRule []interface{}
for _, nodeItem := range node {
nodeRule = append(nodeRule, nodeItem)
}
var labelRule []interface{}
for _, labelItem := range label {
labelRule = append(labelRule, labelItem)
}
logs, sub, err := _ENS.contract.FilterLogs(opts, "NewOwner", nodeRule, labelRule)
if err != nil {
return nil, err
}
return &ENSNewOwnerIterator{contract: _ENS.contract, event: "NewOwner", logs: logs, sub: sub}, nil
}
// WatchNewOwner is a free log subscription operation binding the contract event 0xce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e82.
//
// Solidity: event NewOwner(node indexed bytes32, label indexed bytes32, owner address)
func (_ENS *ENSFilterer) WatchNewOwner(opts *bind.WatchOpts, sink chan<- *ENSNewOwner, node [][32]byte, label [][32]byte) (event.Subscription, error) {
var nodeRule []interface{}
for _, nodeItem := range node {
nodeRule = append(nodeRule, nodeItem)
}
var labelRule []interface{}
for _, labelItem := range label {
labelRule = append(labelRule, labelItem)
}
logs, sub, err := _ENS.contract.WatchLogs(opts, "NewOwner", nodeRule, labelRule)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(ENSNewOwner)
if err := _ENS.contract.UnpackLog(event, "NewOwner", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ENSNewResolverIterator is returned from FilterNewResolver and is used to iterate over the raw logs and unpacked data for NewResolver events raised by the ENS contract.
type ENSNewResolverIterator struct {
Event *ENSNewResolver // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *ENSNewResolverIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(ENSNewResolver)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(ENSNewResolver)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error retruned any retrieval or parsing error occurred during filtering.
func (it *ENSNewResolverIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *ENSNewResolverIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// ENSNewResolver represents a NewResolver event raised by the ENS contract.
type ENSNewResolver struct {
Node [32]byte
Resolver common.Address
Raw types.Log // Blockchain specific contextual infos
}
// FilterNewResolver is a free log retrieval operation binding the contract event 0x335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0.
//
// Solidity: event NewResolver(node indexed bytes32, resolver address)
func (_ENS *ENSFilterer) FilterNewResolver(opts *bind.FilterOpts, node [][32]byte) (*ENSNewResolverIterator, error) {
var nodeRule []interface{}
for _, nodeItem := range node {
nodeRule = append(nodeRule, nodeItem)
}
logs, sub, err := _ENS.contract.FilterLogs(opts, "NewResolver", nodeRule)
if err != nil {
return nil, err
}
return &ENSNewResolverIterator{contract: _ENS.contract, event: "NewResolver", logs: logs, sub: sub}, nil
}
// WatchNewResolver is a free log subscription operation binding the contract event 0x335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a0.
//
// Solidity: event NewResolver(node indexed bytes32, resolver address)
func (_ENS *ENSFilterer) WatchNewResolver(opts *bind.WatchOpts, sink chan<- *ENSNewResolver, node [][32]byte) (event.Subscription, error) {
var nodeRule []interface{}
for _, nodeItem := range node {
nodeRule = append(nodeRule, nodeItem)
}
logs, sub, err := _ENS.contract.WatchLogs(opts, "NewResolver", nodeRule)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(ENSNewResolver)
if err := _ENS.contract.UnpackLog(event, "NewResolver", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ENSNewTTLIterator is returned from FilterNewTTL and is used to iterate over the raw logs and unpacked data for NewTTL events raised by the ENS contract.
type ENSNewTTLIterator struct {
Event *ENSNewTTL // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *ENSNewTTLIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(ENSNewTTL)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(ENSNewTTL)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error retruned any retrieval or parsing error occurred during filtering.
func (it *ENSNewTTLIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *ENSNewTTLIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// ENSNewTTL represents a NewTTL event raised by the ENS contract.
type ENSNewTTL struct {
Node [32]byte
Ttl uint64
Raw types.Log // Blockchain specific contextual infos
}
// FilterNewTTL is a free log retrieval operation binding the contract event 0x1d4f9bbfc9cab89d66e1a1562f2233ccbf1308cb4f63de2ead5787adddb8fa68.
//
// Solidity: event NewTTL(node indexed bytes32, ttl uint64)
func (_ENS *ENSFilterer) FilterNewTTL(opts *bind.FilterOpts, node [][32]byte) (*ENSNewTTLIterator, error) {
var nodeRule []interface{}
for _, nodeItem := range node {
nodeRule = append(nodeRule, nodeItem)
}
logs, sub, err := _ENS.contract.FilterLogs(opts, "NewTTL", nodeRule)
if err != nil {
return nil, err
}
return &ENSNewTTLIterator{contract: _ENS.contract, event: "NewTTL", logs: logs, sub: sub}, nil
}
// WatchNewTTL is a free log subscription operation binding the contract event 0x1d4f9bbfc9cab89d66e1a1562f2233ccbf1308cb4f63de2ead5787adddb8fa68.
//
// Solidity: event NewTTL(node indexed bytes32, ttl uint64)
func (_ENS *ENSFilterer) WatchNewTTL(opts *bind.WatchOpts, sink chan<- *ENSNewTTL, node [][32]byte) (event.Subscription, error) {
var nodeRule []interface{}
for _, nodeItem := range node {
nodeRule = append(nodeRule, nodeItem)
}
logs, sub, err := _ENS.contract.WatchLogs(opts, "NewTTL", nodeRule)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(ENSNewTTL)
if err := _ENS.contract.UnpackLog(event, "NewTTL", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// ENSTransferIterator is returned from FilterTransfer and is used to iterate over the raw logs and unpacked data for Transfer events raised by the ENS contract.
type ENSTransferIterator struct {
Event *ENSTransfer // Event containing the contract specifics and raw log
contract *bind.BoundContract // Generic contract to use for unpacking event data
event string // Event name to use for unpacking event data
logs chan types.Log // Log channel receiving the found contract events
sub ethereum.Subscription // Subscription for errors, completion and termination
done bool // Whether the subscription completed delivering logs
fail error // Occurred error to stop iteration
}
// Next advances the iterator to the subsequent event, returning whether there
// are any more events found. In case of a retrieval or parsing error, false is
// returned and Error() can be queried for the exact failure.
func (it *ENSTransferIterator) Next() bool {
// If the iterator failed, stop iterating
if it.fail != nil {
return false
}
// If the iterator completed, deliver directly whatever's available
if it.done {
select {
case log := <-it.logs:
it.Event = new(ENSTransfer)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
default:
return false
}
}
// Iterator still in progress, wait for either a data or an error event
select {
case log := <-it.logs:
it.Event = new(ENSTransfer)
if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil {
it.fail = err
return false
}
it.Event.Raw = log
return true
case err := <-it.sub.Err():
it.done = true
it.fail = err
return it.Next()
}
}
// Error retruned any retrieval or parsing error occurred during filtering.
func (it *ENSTransferIterator) Error() error {
return it.fail
}
// Close terminates the iteration process, releasing any pending underlying
// resources.
func (it *ENSTransferIterator) Close() error {
it.sub.Unsubscribe()
return nil
}
// ENSTransfer represents a Transfer event raised by the ENS contract.
type ENSTransfer struct {
Node [32]byte
Owner common.Address
Raw types.Log // Blockchain specific contextual infos
}
// FilterTransfer is a free log retrieval operation binding the contract event 0xd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266.
//
// Solidity: event Transfer(node indexed bytes32, owner address)
func (_ENS *ENSFilterer) FilterTransfer(opts *bind.FilterOpts, node [][32]byte) (*ENSTransferIterator, error) {
var nodeRule []interface{}
for _, nodeItem := range node {
nodeRule = append(nodeRule, nodeItem)
}
logs, sub, err := _ENS.contract.FilterLogs(opts, "Transfer", nodeRule)
if err != nil {
return nil, err
}
return &ENSTransferIterator{contract: _ENS.contract, event: "Transfer", logs: logs, sub: sub}, nil
}
// WatchTransfer is a free log subscription operation binding the contract event 0xd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d266.
//
// Solidity: event Transfer(node indexed bytes32, owner address)
func (_ENS *ENSFilterer) WatchTransfer(opts *bind.WatchOpts, sink chan<- *ENSTransfer, node [][32]byte) (event.Subscription, error) {
var nodeRule []interface{}
for _, nodeItem := range node {
nodeRule = append(nodeRule, nodeItem)
}
logs, sub, err := _ENS.contract.WatchLogs(opts, "Transfer", nodeRule)
if err != nil {
return nil, err
}
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case log := <-logs:
// New log arrived, parse the event and forward to the user
event := new(ENSTransfer)
if err := _ENS.contract.UnpackLog(event, "Transfer", log); err != nil {
return err
}
event.Raw = log
select {
case sink <- event:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}

View file

@ -16,7 +16,7 @@ import (
const FIFSRegistrarABI = "[{\"constant\":false,\"inputs\":[{\"name\":\"subnode\",\"type\":\"bytes32\"},{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"register\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"ensAddr\",\"type\":\"address\"},{\"name\":\"node\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]" const FIFSRegistrarABI = "[{\"constant\":false,\"inputs\":[{\"name\":\"subnode\",\"type\":\"bytes32\"},{\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"register\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"ensAddr\",\"type\":\"address\"},{\"name\":\"node\",\"type\":\"bytes32\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]"
// FIFSRegistrarBin is the compiled bytecode used for deploying new contracts. // FIFSRegistrarBin is the compiled bytecode used for deploying new contracts.
const FIFSRegistrarBin = `0x6060604052341561000f57600080fd5b604051604080610224833981016040528080519190602001805160008054600160a060020a03909516600160a060020a03199095169490941790935550506001556101c58061005f6000396000f3006060604052600436106100275763ffffffff60e060020a600035041663d22057a9811461002c575b600080fd5b341561003757600080fd5b61004e600435600160a060020a0360243516610050565b005b816000806001548360405191825260208201526040908101905190819003902060008054919350600160a060020a03909116906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b15156100c857600080fd5b6102c65a03f115156100d957600080fd5b5050506040518051915050600160a060020a0381161580159061010e575033600160a060020a031681600160a060020a031614155b1561011857600080fd5b600054600154600160a060020a03909116906306ab592390878760405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b151561017e57600080fd5b6102c65a03f1151561018f57600080fd5b50505050505050505600a165627a7a723058209b0c0f4ed76e4fe49a71d4b838ab3d00d6bad29021172db7ced9f36abcafbf510029` const FIFSRegistrarBin = `0x6060604052341561000f57600080fd5b604051604080610224833981016040528080519190602001805160008054600160a060020a03909516600160a060020a03199095169490941790935550506001556101c58061005f6000396000f3006060604052600436106100275763ffffffff60e060020a600035041663d22057a9811461002c575b600080fd5b341561003757600080fd5b61004e600435600160a060020a0360243516610050565b005b816000806001548360405191825260208201526040908101905190819003902060008054919350600160a060020a03909116906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b15156100c857600080fd5b6102c65a03f115156100d957600080fd5b5050506040518051915050600160a060020a0381161580159061010e575033600160a060020a031681600160a060020a031614155b1561011857600080fd5b600054600154600160a060020a03909116906306ab592390878760405160e060020a63ffffffff861602815260048101939093526024830191909152600160a060020a03166044820152606401600060405180830381600087803b151561017e57600080fd5b6102c65a03f1151561018f57600080fd5b50505050505050505600a165627a7a723058206fb963cb168d5e3a51af12cd6bb23e324dbd32dd4954f43653ba27e66b68ea650029`
// DeployFIFSRegistrar deploys a new Ethereum contract, binding an instance of FIFSRegistrar to it. // DeployFIFSRegistrar deploys a new Ethereum contract, binding an instance of FIFSRegistrar to it.
func DeployFIFSRegistrar(auth *bind.TransactOpts, backend bind.ContractBackend, ensAddr common.Address, node [32]byte) (common.Address, *types.Transaction, *FIFSRegistrar, error) { func DeployFIFSRegistrar(auth *bind.TransactOpts, backend bind.ContractBackend, ensAddr common.Address, node [32]byte) (common.Address, *types.Transaction, *FIFSRegistrar, error) {
@ -28,13 +28,14 @@ func DeployFIFSRegistrar(auth *bind.TransactOpts, backend bind.ContractBackend,
if err != nil { if err != nil {
return common.Address{}, nil, nil, err return common.Address{}, nil, nil, err
} }
return address, tx, &FIFSRegistrar{FIFSRegistrarCaller: FIFSRegistrarCaller{contract: contract}, FIFSRegistrarTransactor: FIFSRegistrarTransactor{contract: contract}}, nil return address, tx, &FIFSRegistrar{FIFSRegistrarCaller: FIFSRegistrarCaller{contract: contract}, FIFSRegistrarTransactor: FIFSRegistrarTransactor{contract: contract}, FIFSRegistrarFilterer: FIFSRegistrarFilterer{contract: contract}}, nil
} }
// FIFSRegistrar is an auto generated Go binding around an Ethereum contract. // FIFSRegistrar is an auto generated Go binding around an Ethereum contract.
type FIFSRegistrar struct { type FIFSRegistrar struct {
FIFSRegistrarCaller // Read-only binding to the contract FIFSRegistrarCaller // Read-only binding to the contract
FIFSRegistrarTransactor // Write-only binding to the contract FIFSRegistrarTransactor // Write-only binding to the contract
FIFSRegistrarFilterer // Log filterer for contract events
} }
// FIFSRegistrarCaller is an auto generated read-only Go binding around an Ethereum contract. // FIFSRegistrarCaller is an auto generated read-only Go binding around an Ethereum contract.
@ -47,6 +48,11 @@ type FIFSRegistrarTransactor struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls contract *bind.BoundContract // Generic contract wrapper for the low level calls
} }
// FIFSRegistrarFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
type FIFSRegistrarFilterer struct {
contract *bind.BoundContract // Generic contract wrapper for the low level calls
}
// FIFSRegistrarSession is an auto generated Go binding around an Ethereum contract, // FIFSRegistrarSession is an auto generated Go binding around an Ethereum contract,
// with pre-set call and transact options. // with pre-set call and transact options.
type FIFSRegistrarSession struct { type FIFSRegistrarSession struct {
@ -86,16 +92,16 @@ type FIFSRegistrarTransactorRaw struct {
// NewFIFSRegistrar creates a new instance of FIFSRegistrar, bound to a specific deployed contract. // NewFIFSRegistrar creates a new instance of FIFSRegistrar, bound to a specific deployed contract.
func NewFIFSRegistrar(address common.Address, backend bind.ContractBackend) (*FIFSRegistrar, error) { func NewFIFSRegistrar(address common.Address, backend bind.ContractBackend) (*FIFSRegistrar, error) {
contract, err := bindFIFSRegistrar(address, backend, backend) contract, err := bindFIFSRegistrar(address, backend, backend, backend)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &FIFSRegistrar{FIFSRegistrarCaller: FIFSRegistrarCaller{contract: contract}, FIFSRegistrarTransactor: FIFSRegistrarTransactor{contract: contract}}, nil return &FIFSRegistrar{FIFSRegistrarCaller: FIFSRegistrarCaller{contract: contract}, FIFSRegistrarTransactor: FIFSRegistrarTransactor{contract: contract}, FIFSRegistrarFilterer: FIFSRegistrarFilterer{contract: contract}}, nil
} }
// NewFIFSRegistrarCaller creates a new read-only instance of FIFSRegistrar, bound to a specific deployed contract. // NewFIFSRegistrarCaller creates a new read-only instance of FIFSRegistrar, bound to a specific deployed contract.
func NewFIFSRegistrarCaller(address common.Address, caller bind.ContractCaller) (*FIFSRegistrarCaller, error) { func NewFIFSRegistrarCaller(address common.Address, caller bind.ContractCaller) (*FIFSRegistrarCaller, error) {
contract, err := bindFIFSRegistrar(address, caller, nil) contract, err := bindFIFSRegistrar(address, caller, nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -104,20 +110,29 @@ func NewFIFSRegistrarCaller(address common.Address, caller bind.ContractCaller)
// NewFIFSRegistrarTransactor creates a new write-only instance of FIFSRegistrar, bound to a specific deployed contract. // NewFIFSRegistrarTransactor creates a new write-only instance of FIFSRegistrar, bound to a specific deployed contract.
func NewFIFSRegistrarTransactor(address common.Address, transactor bind.ContractTransactor) (*FIFSRegistrarTransactor, error) { func NewFIFSRegistrarTransactor(address common.Address, transactor bind.ContractTransactor) (*FIFSRegistrarTransactor, error) {
contract, err := bindFIFSRegistrar(address, nil, transactor) contract, err := bindFIFSRegistrar(address, nil, transactor, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &FIFSRegistrarTransactor{contract: contract}, nil return &FIFSRegistrarTransactor{contract: contract}, nil
} }
// NewFIFSRegistrarFilterer creates a new log filterer instance of FIFSRegistrar, bound to a specific deployed contract.
func NewFIFSRegistrarFilterer(address common.Address, filterer bind.ContractFilterer) (*FIFSRegistrarFilterer, error) {
contract, err := bindFIFSRegistrar(address, nil, nil, filterer)
if err != nil {
return nil, err
}
return &FIFSRegistrarFilterer{contract: contract}, nil
}
// bindFIFSRegistrar binds a generic wrapper to an already deployed contract. // bindFIFSRegistrar binds a generic wrapper to an already deployed contract.
func bindFIFSRegistrar(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor) (*bind.BoundContract, error) { func bindFIFSRegistrar(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
parsed, err := abi.JSON(strings.NewReader(FIFSRegistrarABI)) parsed, err := abi.JSON(strings.NewReader(FIFSRegistrarABI))
if err != nil { if err != nil {
return nil, err return nil, err
} }
return bind.NewBoundContract(address, parsed, caller, transactor), nil return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
} }
// Call invokes the (constant) contract method with params as input values and // Call invokes the (constant) contract method with params as input values and

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,251 +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/>.
pragma solidity ^0.4.18;
// ReleaseOracle is an Ethereum contract to store the current and previous
// versions of the go-ethereum implementation. Its goal is to allow Geth to
// check for new releases automatically without the need to consult a central
// repository.
//
// The contract takes a vote based approach on both assigning authorised signers
// as well as signing off on new Geth releases.
//
// Note, when a signer is demoted, the currently pending release is auto-nuked.
// The reason is to prevent suprises where a demotion actually tilts the votes
// in favor of one voter party and pushing out a new release as a consequence of
// a simple demotion.
contract ReleaseOracle {
// Votes is an internal data structure to count votes on a specific proposal
struct Votes {
address[] pass; // List of signers voting to pass a proposal
address[] fail; // List of signers voting to fail a proposal
}
// Version is the version details of a particular Geth release
struct Version {
uint32 major; // Major version component of the release
uint32 minor; // Minor version component of the release
uint32 patch; // Patch version component of the release
bytes20 commit; // Git SHA1 commit hash of the release
uint64 time; // Timestamp of the release approval
Votes votes; // Votes that passed this release
}
// Oracle authorization details
mapping(address => bool) authorised; // Set of accounts allowed to vote on updating the contract
address[] voters; // List of addresses currently accepted as signers
// Various proposals being voted on
mapping(address => Votes) authProps; // Currently running user authorization proposals
address[] authPend; // List of addresses being voted on (map indexes)
Version verProp; // Currently proposed release being voted on
Version[] releases; // All the positively voted releases
// isSigner is a modifier to authorize contract transactions.
modifier isSigner() {
if (authorised[msg.sender]) {
_;
}
}
// Constructor to assign the initial set of signers.
function ReleaseOracle(address[] signers) {
// If no signers were specified, assign the creator as the sole signer
if (signers.length == 0) {
authorised[msg.sender] = true;
voters.push(msg.sender);
return;
}
// Otherwise assign the individual signers one by one
for (uint i = 0; i < signers.length; i++) {
authorised[signers[i]] = true;
voters.push(signers[i]);
}
}
// signers is an accessor method to retrieve all the signers (public accessor
// generates an indexed one, not a retrieve-all version).
function signers() constant returns(address[]) {
return voters;
}
// authProposals retrieves the list of addresses that authorization proposals
// are currently being voted on.
function authProposals() constant returns(address[]) {
return authPend;
}
// authVotes retrieves the current authorization votes for a particular user
// to promote him into the list of signers, or demote him from there.
function authVotes(address user) constant returns(address[] promote, address[] demote) {
return (authProps[user].pass, authProps[user].fail);
}
// currentVersion retrieves the semantic version, commit hash and release time
// of the currently votec active release.
function currentVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, uint time) {
if (releases.length == 0) {
return (0, 0, 0, 0, 0);
}
var release = releases[releases.length - 1];
return (release.major, release.minor, release.patch, release.commit, release.time);
}
// proposedVersion retrieves the semantic version, commit hash and the current
// votes for the next proposed release.
function proposedVersion() constant returns (uint32 major, uint32 minor, uint32 patch, bytes20 commit, address[] pass, address[] fail) {
return (verProp.major, verProp.minor, verProp.patch, verProp.commit, verProp.votes.pass, verProp.votes.fail);
}
// promote pitches in on a voting campaign to promote a new user to a signer
// position.
function promote(address user) {
updateSigner(user, true);
}
// demote pitches in on a voting campaign to demote an authorised user from
// its signer position.
function demote(address user) {
updateSigner(user, false);
}
// release votes for a particular version to be included as the next release.
function release(uint32 major, uint32 minor, uint32 patch, bytes20 commit) {
updateRelease(major, minor, patch, commit, true);
}
// nuke votes for the currently proposed version to not be included as the next
// release. Nuking doesn't require a specific version number for simplicity.
function nuke() {
updateRelease(0, 0, 0, 0, false);
}
// updateSigner marks a vote for changing the status of an Ethereum user, either
// for or against the user being an authorised signer.
function updateSigner(address user, bool authorize) internal isSigner {
// Gather the current votes and ensure we don't double vote
Votes votes = authProps[user];
for (uint i = 0; i < votes.pass.length; i++) {
if (votes.pass[i] == msg.sender) {
return;
}
}
for (i = 0; i < votes.fail.length; i++) {
if (votes.fail[i] == msg.sender) {
return;
}
}
// If no authorization proposal is open, add the user to the index for later lookups
if (votes.pass.length == 0 && votes.fail.length == 0) {
authPend.push(user);
}
// Cast the vote and return if the proposal cannot be resolved yet
if (authorize) {
votes.pass.push(msg.sender);
if (votes.pass.length <= voters.length / 2) {
return;
}
} else {
votes.fail.push(msg.sender);
if (votes.fail.length <= voters.length / 2) {
return;
}
}
// Proposal resolved in our favor, execute whatever we voted on
if (authorize && !authorised[user]) {
authorised[user] = true;
voters.push(user);
} else if (!authorize && authorised[user]) {
authorised[user] = false;
for (i = 0; i < voters.length; i++) {
if (voters[i] == user) {
voters[i] = voters[voters.length - 1];
voters.length--;
delete verProp; // Nuke any version proposal (no surprise releases!)
break;
}
}
}
// Finally delete the resolved proposal, index and garbage collect
delete authProps[user];
for (i = 0; i < authPend.length; i++) {
if (authPend[i] == user) {
authPend[i] = authPend[authPend.length - 1];
authPend.length--;
break;
}
}
}
// updateRelease votes for a particular version to be included as the next release,
// or for the currently proposed release to be nuked out.
function updateRelease(uint32 major, uint32 minor, uint32 patch, bytes20 commit, bool release) internal isSigner {
// Skip nuke votes if no proposal is pending
if (!release && verProp.votes.pass.length == 0) {
return;
}
// Mark a new release if no proposal is pending
if (verProp.votes.pass.length == 0) {
verProp.major = major;
verProp.minor = minor;
verProp.patch = patch;
verProp.commit = commit;
}
// Make sure positive votes match the current proposal
if (release && (verProp.major != major || verProp.minor != minor || verProp.patch != patch || verProp.commit != commit)) {
return;
}
// Gather the current votes and ensure we don't double vote
Votes votes = verProp.votes;
for (uint i = 0; i < votes.pass.length; i++) {
if (votes.pass[i] == msg.sender) {
return;
}
}
for (i = 0; i < votes.fail.length; i++) {
if (votes.fail[i] == msg.sender) {
return;
}
}
// Cast the vote and return if the proposal cannot be resolved yet
if (release) {
votes.pass.push(msg.sender);
if (votes.pass.length <= voters.length / 2) {
return;
}
} else {
votes.fail.push(msg.sender);
if (votes.fail.length <= voters.length / 2) {
return;
}
}
// Proposal resolved in our favor, execute whatever we voted on
if (release) {
verProp.time = uint64(now);
releases.push(verProp);
delete verProp;
} else {
delete verProp;
}
}
}

View file

@ -1,374 +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 release
import (
"crypto/ecdsa"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
)
// setupReleaseTest creates a blockchain simulator and deploys a version oracle
// contract for testing.
func setupReleaseTest(t *testing.T, prefund ...*ecdsa.PrivateKey) (*ecdsa.PrivateKey, *ReleaseOracle, *backends.SimulatedBackend) {
// Generate a new random account and a funded simulator
key, _ := crypto.GenerateKey()
auth := bind.NewKeyedTransactor(key)
alloc := core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}
for _, key := range prefund {
alloc[crypto.PubkeyToAddress(key.PublicKey)] = core.GenesisAccount{Balance: big.NewInt(10000000000)}
}
sim := backends.NewSimulatedBackend(alloc)
// Deploy a version oracle contract, commit and return
_, _, oracle, err := DeployReleaseOracle(auth, sim, []common.Address{auth.From})
if err != nil {
t.Fatalf("Failed to deploy version contract: %v", err)
}
sim.Commit()
return key, oracle, sim
}
// Tests that the version contract can be deployed and the creator is assigned
// the sole authorized signer.
func TestContractCreation(t *testing.T) {
key, oracle, _ := setupReleaseTest(t)
owner := crypto.PubkeyToAddress(key.PublicKey)
signers, err := oracle.Signers(nil)
if err != nil {
t.Fatalf("Failed to retrieve list of signers: %v", err)
}
if len(signers) != 1 || signers[0] != owner {
t.Fatalf("Initial signer mismatch: have %v, want %v", signers, owner)
}
}
// Tests that subsequent signers can be promoted, each requiring half plus one
// votes for it to pass through.
func TestSignerPromotion(t *testing.T) {
// Prefund a few accounts to authorize with and create the oracle
keys := make([]*ecdsa.PrivateKey, 5)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
}
key, oracle, sim := setupReleaseTest(t, keys...)
// Gradually promote the keys, until all are authorized
keys = append([]*ecdsa.PrivateKey{key}, keys...)
for i := 1; i < len(keys); i++ {
// Check that no votes are accepted from the not yet authorized user
if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[i]), common.Address{}); err != nil {
t.Fatalf("Iter #%d: failed invalid promotion attempt: %v", i, err)
}
sim.Commit()
pend, err := oracle.AuthProposals(nil)
if err != nil {
t.Fatalf("Iter #%d: failed to retrieve active proposals: %v", i, err)
}
if len(pend) != 0 {
t.Fatalf("Iter #%d: proposal count mismatch: have %d, want 0", i, len(pend))
}
// Promote with half - 1 voters and check that the user's not yet authorized
for j := 0; j < i/2; j++ {
if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil {
t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err)
}
}
sim.Commit()
signers, err := oracle.Signers(nil)
if err != nil {
t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", i, err)
}
if len(signers) != i {
t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", i, len(signers), i)
}
// Promote with the last one needed to pass the promotion
if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[i/2]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil {
t.Fatalf("Iter #%d: failed valid promotion completion attempt: %v", i, err)
}
sim.Commit()
signers, err = oracle.Signers(nil)
if err != nil {
t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", i, err)
}
if len(signers) != i+1 {
t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", i, len(signers), i+1)
}
}
}
// Tests that subsequent signers can be demoted, each requiring half plus one
// votes for it to pass through.
func TestSignerDemotion(t *testing.T) {
// Prefund a few accounts to authorize with and create the oracle
keys := make([]*ecdsa.PrivateKey, 5)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
}
key, oracle, sim := setupReleaseTest(t, keys...)
// Authorize all the keys as valid signers and verify cardinality
keys = append([]*ecdsa.PrivateKey{key}, keys...)
for i := 1; i < len(keys); i++ {
for j := 0; j <= i/2; j++ {
if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil {
t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err)
}
}
sim.Commit()
}
signers, err := oracle.Signers(nil)
if err != nil {
t.Fatalf("Failed to retrieve list of signers: %v", err)
}
if len(signers) != len(keys) {
t.Fatalf("Signer count mismatch: have %v, want %v", len(signers), len(keys))
}
// Gradually demote users until we run out of signers
for i := len(keys) - 1; i >= 0; i-- {
// Demote with half - 1 voters and check that the user's not yet dropped
for j := 0; j < (i+1)/2; j++ {
if _, err = oracle.Demote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil {
t.Fatalf("Iter #%d: failed valid demotion attempt: %v", len(keys)-i, err)
}
}
sim.Commit()
signers, err := oracle.Signers(nil)
if err != nil {
t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", len(keys)-i, err)
}
if len(signers) != i+1 {
t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", len(keys)-i, len(signers), i+1)
}
// Demote with the last one needed to pass the demotion
if _, err = oracle.Demote(bind.NewKeyedTransactor(keys[(i+1)/2]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil {
t.Fatalf("Iter #%d: failed valid demotion completion attempt: %v", i, err)
}
sim.Commit()
signers, err = oracle.Signers(nil)
if err != nil {
t.Fatalf("Iter #%d: failed to retrieve list of signers: %v", len(keys)-i, err)
}
if len(signers) != i {
t.Fatalf("Iter #%d: signer count mismatch: have %v, want %v", len(keys)-i, len(signers), i)
}
// Check that no votes are accepted from the already demoted users
if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[i]), common.Address{}); err != nil {
t.Fatalf("Iter #%d: failed invalid promotion attempt: %v", i, err)
}
sim.Commit()
pend, err := oracle.AuthProposals(nil)
if err != nil {
t.Fatalf("Iter #%d: failed to retrieve active proposals: %v", i, err)
}
if len(pend) != 0 {
t.Fatalf("Iter #%d: proposal count mismatch: have %d, want 0", i, len(pend))
}
}
}
// Tests that new versions can be released, honouring both voting rights as well
// as the minimum required vote count.
func TestVersionRelease(t *testing.T) {
// Prefund a few accounts to authorize with and create the oracle
keys := make([]*ecdsa.PrivateKey, 5)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
}
key, oracle, sim := setupReleaseTest(t, keys...)
// Track the "current release"
var (
verMajor = uint32(0)
verMinor = uint32(0)
verPatch = uint32(0)
verCommit = [20]byte{}
)
// Gradually push releases, always requiring more signers than previously
keys = append([]*ecdsa.PrivateKey{key}, keys...)
for i := 1; i < len(keys); i++ {
// Check that no votes are accepted from the not yet authorized user
if _, err := oracle.Release(bind.NewKeyedTransactor(keys[i]), 0, 0, 0, [20]byte{0}); err != nil {
t.Fatalf("Iter #%d: failed invalid release attempt: %v", i, err)
}
sim.Commit()
prop, err := oracle.ProposedVersion(nil)
if err != nil {
t.Fatalf("Iter #%d: failed to retrieve active proposal: %v", i, err)
}
if len(prop.Pass) != 0 {
t.Fatalf("Iter #%d: proposal vote count mismatch: have %d, want 0", i, len(prop.Pass))
}
// Authorize the user to make releases
for j := 0; j <= i/2; j++ {
if _, err = oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil {
t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err)
}
}
sim.Commit()
// Propose release with half voters and check that the release does not yet go through
for j := 0; j < (i+1)/2; j++ {
if _, err = oracle.Release(bind.NewKeyedTransactor(keys[j]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)}); err != nil {
t.Fatalf("Iter #%d: failed valid release attempt: %v", i, err)
}
}
sim.Commit()
ver, err := oracle.CurrentVersion(nil)
if err != nil {
t.Fatalf("Iter #%d: failed to retrieve current version: %v", i, err)
}
if ver.Major != verMajor || ver.Minor != verMinor || ver.Patch != verPatch || ver.Commit != verCommit {
t.Fatalf("Iter #%d: version mismatch: have %d.%d.%d-%x, want %d.%d.%d-%x", i, ver.Major, ver.Minor, ver.Patch, ver.Commit, verMajor, verMinor, verPatch, verCommit)
}
// Pass the release and check that it became the next version
verMajor, verMinor, verPatch, verCommit = uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)}
if _, err = oracle.Release(bind.NewKeyedTransactor(keys[(i+1)/2]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)}); err != nil {
t.Fatalf("Iter #%d: failed valid release completion attempt: %v", i, err)
}
sim.Commit()
ver, err = oracle.CurrentVersion(nil)
if err != nil {
t.Fatalf("Iter #%d: failed to retrieve current version: %v", i, err)
}
if ver.Major != verMajor || ver.Minor != verMinor || ver.Patch != verPatch || ver.Commit != verCommit {
t.Fatalf("Iter #%d: version mismatch: have %d.%d.%d-%x, want %d.%d.%d-%x", i, ver.Major, ver.Minor, ver.Patch, ver.Commit, verMajor, verMinor, verPatch, verCommit)
}
}
}
// Tests that proposed versions can be nuked out of existence.
func TestVersionNuking(t *testing.T) {
// Prefund a few accounts to authorize with and create the oracle
keys := make([]*ecdsa.PrivateKey, 9)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
}
key, oracle, sim := setupReleaseTest(t, keys...)
// Authorize all the keys as valid signers
keys = append([]*ecdsa.PrivateKey{key}, keys...)
for i := 1; i < len(keys); i++ {
for j := 0; j <= i/2; j++ {
if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil {
t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err)
}
}
sim.Commit()
}
// Propose releases with more and more keys, always retaining enough users to nuke the proposals
for i := 1; i < (len(keys)+1)/2; i++ {
// Propose release with an initial set of signers
for j := 0; j < i; j++ {
if _, err := oracle.Release(bind.NewKeyedTransactor(keys[j]), uint32(i), uint32(i+1), uint32(i+2), [20]byte{byte(i + 3)}); err != nil {
t.Fatalf("Iter #%d: failed valid proposal attempt: %v", i, err)
}
}
sim.Commit()
prop, err := oracle.ProposedVersion(nil)
if err != nil {
t.Fatalf("Iter #%d: failed to retrieve active proposal: %v", i, err)
}
if len(prop.Pass) != i {
t.Fatalf("Iter #%d: proposal vote count mismatch: have %d, want %d", i, len(prop.Pass), i)
}
// Nuke the release with half+1 voters
for j := i; j <= i+(len(keys)+1)/2; j++ {
if _, err := oracle.Nuke(bind.NewKeyedTransactor(keys[j])); err != nil {
t.Fatalf("Iter #%d: failed valid nuke attempt: %v", i, err)
}
}
sim.Commit()
prop, err = oracle.ProposedVersion(nil)
if err != nil {
t.Fatalf("Iter #%d: failed to retrieve active proposal: %v", i, err)
}
if len(prop.Pass) != 0 || len(prop.Fail) != 0 {
t.Fatalf("Iter #%d: proposal vote count mismatch: have %d/%d pass/fail, want 0/0", i, len(prop.Pass), len(prop.Fail))
}
}
}
// Tests that demoting a signer will auto-nuke the currently pending release.
func TestVersionAutoNuke(t *testing.T) {
// Prefund a few accounts to authorize with and create the oracle
keys := make([]*ecdsa.PrivateKey, 5)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
}
key, oracle, sim := setupReleaseTest(t, keys...)
// Authorize all the keys as valid signers
keys = append([]*ecdsa.PrivateKey{key}, keys...)
for i := 1; i < len(keys); i++ {
for j := 0; j <= i/2; j++ {
if _, err := oracle.Promote(bind.NewKeyedTransactor(keys[j]), crypto.PubkeyToAddress(keys[i].PublicKey)); err != nil {
t.Fatalf("Iter #%d: failed valid promotion attempt: %v", i, err)
}
}
sim.Commit()
}
// Make a release proposal and check it's existence
if _, err := oracle.Release(bind.NewKeyedTransactor(keys[0]), 1, 2, 3, [20]byte{4}); err != nil {
t.Fatalf("Failed valid proposal attempt: %v", err)
}
sim.Commit()
prop, err := oracle.ProposedVersion(nil)
if err != nil {
t.Fatalf("Failed to retrieve active proposal: %v", err)
}
if len(prop.Pass) != 1 {
t.Fatalf("Proposal vote count mismatch: have %d, want 1", len(prop.Pass))
}
// Demote a signer and check release proposal deletion
for i := 0; i <= len(keys)/2; i++ {
if _, err := oracle.Demote(bind.NewKeyedTransactor(keys[i]), crypto.PubkeyToAddress(keys[len(keys)-1].PublicKey)); err != nil {
t.Fatalf("Iter #%d: failed valid demotion attempt: %v", i, err)
}
}
sim.Commit()
prop, err = oracle.ProposedVersion(nil)
if err != nil {
t.Fatalf("Failed to retrieve active proposal: %v", err)
}
if len(prop.Pass) != 0 {
t.Fatalf("Proposal vote count mismatch: have %d, want 0", len(prop.Pass))
}
}

View file

@ -1,164 +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 release contains the node service that tracks client releases.
package release
//go:generate abigen --sol ./contract.sol --pkg release --out ./contract.go
import (
"context"
"fmt"
"strings"
"time"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rpc"
)
// Interval to check for new releases
const releaseRecheckInterval = time.Hour
// Config contains the configurations of the release service.
type Config struct {
Oracle common.Address // Ethereum address of the release oracle
Major uint32 // Major version component of the release
Minor uint32 // Minor version component of the release
Patch uint32 // Patch version component of the release
Commit [20]byte // Git SHA1 commit hash of the release
}
// ReleaseService is a node service that periodically checks the blockchain for
// newly released versions of the client being run and issues a warning to the
// user about it.
type ReleaseService struct {
config Config // Current version to check releases against
oracle *ReleaseOracle // Native binding to the release oracle contract
quit chan chan error // Quit channel to terminate the version checker
}
// NewReleaseService creates a new service to periodically check for new client
// releases and notify the user of such.
func NewReleaseService(ctx *node.ServiceContext, config Config) (node.Service, error) {
// Retrieve the Ethereum service dependency to access the blockchain
var apiBackend ethapi.Backend
var ethereum *eth.Ethereum
if err := ctx.Service(&ethereum); err == nil {
apiBackend = ethereum.ApiBackend
} else {
var ethereum *les.LightEthereum
if err := ctx.Service(&ethereum); err == nil {
apiBackend = ethereum.ApiBackend
} else {
return nil, err
}
}
// Construct the release service
contract, err := NewReleaseOracle(config.Oracle, eth.NewContractBackend(apiBackend))
if err != nil {
return nil, err
}
return &ReleaseService{
config: config,
oracle: contract,
quit: make(chan chan error),
}, nil
}
// Protocols returns an empty list of P2P protocols as the release service does
// not have a networking component.
func (r *ReleaseService) Protocols() []p2p.Protocol { return nil }
// APIs returns an empty list of RPC descriptors as the release service does not
// expose any functioanlity to the outside world.
func (r *ReleaseService) APIs() []rpc.API { return nil }
// Start spawns the periodic version checker goroutine
func (r *ReleaseService) Start(server *p2p.Server) error {
go r.checker()
return nil
}
// Stop terminates all goroutines belonging to the service, blocking until they
// are all terminated.
func (r *ReleaseService) Stop() error {
errc := make(chan error)
r.quit <- errc
return <-errc
}
// checker runs indefinitely in the background, periodically checking for new
// client releases.
func (r *ReleaseService) checker() {
// Set up the timers to periodically check for releases
timer := time.NewTimer(0) // Immediately fire a version check
defer timer.Stop()
for {
select {
case <-timer.C:
// Rechedule the timer before continuing
timer.Reset(releaseRecheckInterval)
r.checkVersion()
case errc := <-r.quit:
errc <- nil
return
}
}
}
func (r *ReleaseService) checkVersion() {
// Retrieve the current version, and handle missing contracts gracefully
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
opts := &bind.CallOpts{Context: ctx}
defer cancel()
version, err := r.oracle.CurrentVersion(opts)
if err != nil {
if err == bind.ErrNoCode {
log.Debug("Release oracle not found", "contract", r.config.Oracle)
} else if err != les.ErrNoPeers {
log.Error("Failed to retrieve current release", "err", err)
}
return
}
// Version was successfully retrieved, notify if newer than ours
if version.Major > r.config.Major ||
(version.Major == r.config.Major && version.Minor > r.config.Minor) ||
(version.Major == r.config.Major && version.Minor == r.config.Minor && version.Patch > r.config.Patch) {
warning := fmt.Sprintf("Client v%d.%d.%d-%x seems older than the latest upstream release v%d.%d.%d-%x",
r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4], version.Major, version.Minor, version.Patch, version.Commit[:4])
howtofix := fmt.Sprintf("Please check https://github.com/ethereum/go-ethereum/releases for new releases")
separator := strings.Repeat("-", len(warning))
log.Warn(separator)
log.Warn(warning)
log.Warn(howtofix)
log.Warn(separator)
} else {
log.Debug("Client seems up to date with upstream",
"local", fmt.Sprintf("v%d.%d.%d-%x", r.config.Major, r.config.Minor, r.config.Patch, r.config.Commit[:4]),
"upstream", fmt.Sprintf("v%d.%d.%d-%x", version.Major, version.Minor, version.Patch, version.Commit[:4]))
}
}

View file

@ -173,7 +173,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// Time the insertion of the new chain. // Time the insertion of the new chain.
// State and blocks are stored in the same DB. // State and blocks are stored in the same DB.
chainman, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{}) chainman, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer chainman.Stop() defer chainman.Stop()
b.ReportAllocs() b.ReportAllocs()
b.ResetTimer() b.ResetTimer()
@ -283,7 +283,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
if err != nil { if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err) b.Fatalf("error opening database at %v: %v", dir, err)
} }
chain, err := NewBlockChain(db, params.TestChainConfig, ethash.NewFaker(), vm.Config{}) chain, err := NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
if err != nil { if err != nil {
b.Fatalf("error creating chain: %v", err) b.Fatalf("error creating chain: %v", err)
} }

View file

@ -50,11 +50,14 @@ func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engin
// validated at this point. // validated at this point.
func (v *BlockValidator) ValidateBody(block *types.Block) error { func (v *BlockValidator) ValidateBody(block *types.Block) error {
// Check whether the block's known, and if not, that it's linkable // Check whether the block's known, and if not, that it's linkable
if v.bc.HasBlockAndState(block.Hash()) { if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) {
return ErrKnownBlock return ErrKnownBlock
} }
if !v.bc.HasBlockAndState(block.ParentHash()) { if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
return consensus.ErrUnknownAncestor if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) {
return consensus.ErrUnknownAncestor
}
return consensus.ErrPrunedAncestor
} }
// Header validity is known at this point, check the uncles and transactions // Header validity is known at this point, check the uncles and transactions
header := block.Header() header := block.Header()

View file

@ -42,7 +42,7 @@ func TestHeaderVerification(t *testing.T) {
headers[i] = block.Header() headers[i] = block.Header()
} }
// Run the header checker for blocks one-by-one, checking for both valid and invalid nonces // Run the header checker for blocks one-by-one, checking for both valid and invalid nonces
chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFaker(), vm.Config{}) chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
defer chain.Stop() defer chain.Stop()
for i := 0; i < len(blocks); i++ { for i := 0; i < len(blocks); i++ {
@ -106,11 +106,11 @@ func testHeaderConcurrentVerification(t *testing.T, threads int) {
var results <-chan error var results <-chan error
if valid { if valid {
chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFaker(), vm.Config{}) chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{})
_, results = chain.engine.VerifyHeaders(chain, headers, seals) _, results = chain.engine.VerifyHeaders(chain, headers, seals)
chain.Stop() chain.Stop()
} else { } else {
chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{}) chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeFailer(uint64(len(headers)-1)), vm.Config{})
_, results = chain.engine.VerifyHeaders(chain, headers, seals) _, results = chain.engine.VerifyHeaders(chain, headers, seals)
chain.Stop() chain.Stop()
} }
@ -173,7 +173,7 @@ func testHeaderConcurrentAbortion(t *testing.T, threads int) {
defer runtime.GOMAXPROCS(old) defer runtime.GOMAXPROCS(old)
// Start the verifications and immediately abort // Start the verifications and immediately abort
chain, _ := NewBlockChain(testdb, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{}) chain, _ := NewBlockChain(testdb, nil, params.TestChainConfig, ethash.NewFakeDelayer(time.Millisecond), vm.Config{})
defer chain.Stop() defer chain.Stop()
abort, results := chain.engine.VerifyHeaders(chain, headers, seals) abort, results := chain.engine.VerifyHeaders(chain, headers, seals)

View file

@ -42,6 +42,7 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
"github.com/hashicorp/golang-lru" "github.com/hashicorp/golang-lru"
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
) )
var ( var (
@ -56,11 +57,20 @@ const (
maxFutureBlocks = 256 maxFutureBlocks = 256
maxTimeFutureBlocks = 30 maxTimeFutureBlocks = 30
badBlockLimit = 10 badBlockLimit = 10
triesInMemory = 128
// BlockChainVersion ensures that an incompatible database forces a resync from scratch. // BlockChainVersion ensures that an incompatible database forces a resync from scratch.
BlockChainVersion = 3 BlockChainVersion = 3
) )
// CacheConfig contains the configuration values for the trie caching/pruning
// that's resident in a blockchain.
type CacheConfig struct {
Disabled bool // Whether to disable trie write caching (archive node)
TrieNodeLimit int // Memory limit (MB) at which to flush the current in-memory trie to disk
TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
}
// BlockChain represents the canonical chain given a database with a genesis // BlockChain represents the canonical chain given a database with a genesis
// block. The Blockchain manages chain imports, reverts, chain reorganisations. // block. The Blockchain manages chain imports, reverts, chain reorganisations.
// //
@ -76,10 +86,14 @@ const (
// included in the canonical one where as GetBlockByNumber always represents the // included in the canonical one where as GetBlockByNumber always represents the
// canonical chain. // canonical chain.
type BlockChain struct { type BlockChain struct {
config *params.ChainConfig // chain & network configuration chainConfig *params.ChainConfig // Chain & network configuration
cacheConfig *CacheConfig // Cache configuration for pruning
db ethdb.Database // Low level persistent database to store final content in
triegc *prque.Prque // Priority queue mapping block numbers to tries to gc
gcproc time.Duration // Accumulates canonical block processing for trie dumping
hc *HeaderChain hc *HeaderChain
chainDb ethdb.Database
rmLogsFeed event.Feed rmLogsFeed event.Feed
chainFeed event.Feed chainFeed event.Feed
chainSideFeed event.Feed chainSideFeed event.Feed
@ -119,7 +133,13 @@ type BlockChain struct {
// NewBlockChain returns a fully initialised block chain using information // NewBlockChain returns a fully initialised block chain using information
// available in the database. It initialises the default Ethereum Validator and // available in the database. It initialises the default Ethereum Validator and
// Processor. // Processor.
func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config) (*BlockChain, error) { func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config) (*BlockChain, error) {
if cacheConfig == nil {
cacheConfig = &CacheConfig{
TrieNodeLimit: 256 * 1024 * 1024,
TrieTimeLimit: 5 * time.Minute,
}
}
bodyCache, _ := lru.New(bodyCacheLimit) bodyCache, _ := lru.New(bodyCacheLimit)
bodyRLPCache, _ := lru.New(bodyCacheLimit) bodyRLPCache, _ := lru.New(bodyCacheLimit)
blockCache, _ := lru.New(blockCacheLimit) blockCache, _ := lru.New(blockCacheLimit)
@ -127,9 +147,11 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine co
badBlocks, _ := lru.New(badBlockLimit) badBlocks, _ := lru.New(badBlockLimit)
bc := &BlockChain{ bc := &BlockChain{
config: config, chainConfig: chainConfig,
chainDb: chainDb, cacheConfig: cacheConfig,
stateCache: state.NewDatabase(chainDb), db: db,
triegc: prque.New(),
stateCache: state.NewDatabase(db),
quit: make(chan struct{}), quit: make(chan struct{}),
bodyCache: bodyCache, bodyCache: bodyCache,
bodyRLPCache: bodyRLPCache, bodyRLPCache: bodyRLPCache,
@ -139,11 +161,11 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, engine co
vmConfig: vmConfig, vmConfig: vmConfig,
badBlocks: badBlocks, badBlocks: badBlocks,
} }
bc.SetValidator(NewBlockValidator(config, bc, engine)) bc.SetValidator(NewBlockValidator(chainConfig, bc, engine))
bc.SetProcessor(NewStateProcessor(config, bc, engine)) bc.SetProcessor(NewStateProcessor(chainConfig, bc, engine))
var err error var err error
bc.hc, err = NewHeaderChain(chainDb, config, engine, bc.getProcInterrupt) bc.hc, err = NewHeaderChain(db, chainConfig, engine, bc.getProcInterrupt)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -180,7 +202,7 @@ func (bc *BlockChain) getProcInterrupt() bool {
// assumes that the chain manager mutex is held. // assumes that the chain manager mutex is held.
func (bc *BlockChain) loadLastState() error { func (bc *BlockChain) loadLastState() error {
// Restore the last known head block // Restore the last known head block
head := GetHeadBlockHash(bc.chainDb) head := GetHeadBlockHash(bc.db)
if head == (common.Hash{}) { if head == (common.Hash{}) {
// Corrupt or empty database, init from scratch // Corrupt or empty database, init from scratch
log.Warn("Empty database, resetting chain") log.Warn("Empty database, resetting chain")
@ -196,15 +218,17 @@ func (bc *BlockChain) loadLastState() error {
// Make sure the state associated with the block is available // Make sure the state associated with the block is available
if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil { if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil {
// Dangling block without a state associated, init from scratch // Dangling block without a state associated, init from scratch
log.Warn("Head state missing, resetting chain", "number", currentBlock.Number(), "hash", currentBlock.Hash()) log.Warn("Head state missing, repairing chain", "number", currentBlock.Number(), "hash", currentBlock.Hash())
return bc.Reset() if err := bc.repair(&currentBlock); err != nil {
return err
}
} }
// Everything seems to be fine, set as the head block // Everything seems to be fine, set as the head block
bc.currentBlock = currentBlock bc.currentBlock = currentBlock
// Restore the last known head header // Restore the last known head header
currentHeader := bc.currentBlock.Header() currentHeader := bc.currentBlock.Header()
if head := GetHeadHeaderHash(bc.chainDb); head != (common.Hash{}) { if head := GetHeadHeaderHash(bc.db); head != (common.Hash{}) {
if header := bc.GetHeaderByHash(head); header != nil { if header := bc.GetHeaderByHash(head); header != nil {
currentHeader = header currentHeader = header
} }
@ -213,7 +237,7 @@ func (bc *BlockChain) loadLastState() error {
// Restore the last known head fast block // Restore the last known head fast block
bc.currentFastBlock = bc.currentBlock bc.currentFastBlock = bc.currentBlock
if head := GetHeadFastBlockHash(bc.chainDb); head != (common.Hash{}) { if head := GetHeadFastBlockHash(bc.db); head != (common.Hash{}) {
if block := bc.GetBlockByHash(head); block != nil { if block := bc.GetBlockByHash(head); block != nil {
bc.currentFastBlock = block bc.currentFastBlock = block
} }
@ -243,7 +267,7 @@ func (bc *BlockChain) SetHead(head uint64) error {
// Rewind the header chain, deleting all block bodies until then // Rewind the header chain, deleting all block bodies until then
delFn := func(hash common.Hash, num uint64) { delFn := func(hash common.Hash, num uint64) {
DeleteBody(bc.chainDb, hash, num) DeleteBody(bc.db, hash, num)
} }
bc.hc.SetHead(head, delFn) bc.hc.SetHead(head, delFn)
currentHeader := bc.hc.CurrentHeader() currentHeader := bc.hc.CurrentHeader()
@ -275,10 +299,10 @@ func (bc *BlockChain) SetHead(head uint64) error {
if bc.currentFastBlock == nil { if bc.currentFastBlock == nil {
bc.currentFastBlock = bc.genesisBlock bc.currentFastBlock = bc.genesisBlock
} }
if err := WriteHeadBlockHash(bc.chainDb, bc.currentBlock.Hash()); err != nil { if err := WriteHeadBlockHash(bc.db, bc.currentBlock.Hash()); err != nil {
log.Crit("Failed to reset head full block", "err", err) log.Crit("Failed to reset head full block", "err", err)
} }
if err := WriteHeadFastBlockHash(bc.chainDb, bc.currentFastBlock.Hash()); err != nil { if err := WriteHeadFastBlockHash(bc.db, bc.currentFastBlock.Hash()); err != nil {
log.Crit("Failed to reset head fast block", "err", err) log.Crit("Failed to reset head fast block", "err", err)
} }
return bc.loadLastState() return bc.loadLastState()
@ -292,7 +316,7 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
if block == nil { if block == nil {
return fmt.Errorf("non existent block [%x…]", hash[:4]) return fmt.Errorf("non existent block [%x…]", hash[:4])
} }
if _, err := trie.NewSecure(block.Root(), bc.chainDb, 0); err != nil { if _, err := trie.NewSecure(block.Root(), bc.stateCache.TrieDB(), 0); err != nil {
return err return err
} }
// If all checks out, manually set the head block // If all checks out, manually set the head block
@ -312,14 +336,6 @@ func (bc *BlockChain) GasLimit() uint64 {
return bc.currentBlock.GasLimit() return bc.currentBlock.GasLimit()
} }
// LastBlockHash return the hash of the HEAD block.
func (bc *BlockChain) LastBlockHash() common.Hash {
bc.mu.RLock()
defer bc.mu.RUnlock()
return bc.currentBlock.Hash()
}
// CurrentBlock retrieves the current head block of the canonical chain. The // CurrentBlock retrieves the current head block of the canonical chain. The
// block is retrieved from the blockchain's internal cache. // block is retrieved from the blockchain's internal cache.
func (bc *BlockChain) CurrentBlock() *types.Block { func (bc *BlockChain) CurrentBlock() *types.Block {
@ -338,15 +354,6 @@ func (bc *BlockChain) CurrentFastBlock() *types.Block {
return bc.currentFastBlock return bc.currentFastBlock
} }
// Status returns status information about the current chain such as the HEAD Td,
// the HEAD hash and the hash of the genesis block.
func (bc *BlockChain) Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash) {
bc.mu.RLock()
defer bc.mu.RUnlock()
return bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64()), bc.currentBlock.Hash(), bc.genesisBlock.Hash()
}
// SetProcessor sets the processor required for making state modifications. // SetProcessor sets the processor required for making state modifications.
func (bc *BlockChain) SetProcessor(processor Processor) { func (bc *BlockChain) SetProcessor(processor Processor) {
bc.procmu.Lock() bc.procmu.Lock()
@ -404,7 +411,7 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil { if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
log.Crit("Failed to write genesis block TD", "err", err) log.Crit("Failed to write genesis block TD", "err", err)
} }
if err := WriteBlock(bc.chainDb, genesis); err != nil { if err := WriteBlock(bc.db, genesis); err != nil {
log.Crit("Failed to write genesis block", "err", err) log.Crit("Failed to write genesis block", "err", err)
} }
bc.genesisBlock = genesis bc.genesisBlock = genesis
@ -417,6 +424,24 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
return nil return nil
} }
// repair tries to repair the current blockchain by rolling back the current block
// until one with associated state is found. This is needed to fix incomplete db
// writes caused either by crashes/power outages, or simply non-committed tries.
//
// This method only rolls back the current block. The current header and current
// fast block are left intact.
func (bc *BlockChain) repair(head **types.Block) error {
for {
// Abort if we've rewound to a head block that does have associated state
if _, err := state.New((*head).Root(), bc.stateCache); err == nil {
log.Info("Rewound blockchain to past state", "number", (*head).Number(), "hash", (*head).Hash())
return nil
}
// Otherwise rewind one block and recheck state availability there
(*head) = bc.GetBlock((*head).ParentHash(), (*head).NumberU64()-1)
}
}
// Export writes the active chain to the given writer. // Export writes the active chain to the given writer.
func (bc *BlockChain) Export(w io.Writer) error { func (bc *BlockChain) Export(w io.Writer) error {
return bc.ExportN(w, uint64(0), bc.currentBlock.NumberU64()) return bc.ExportN(w, uint64(0), bc.currentBlock.NumberU64())
@ -454,22 +479,22 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
// Note, this function assumes that the `mu` mutex is held! // Note, this function assumes that the `mu` mutex is held!
func (bc *BlockChain) insert(block *types.Block) { func (bc *BlockChain) insert(block *types.Block) {
// If the block is on a side chain or an unknown one, force other heads onto it too // If the block is on a side chain or an unknown one, force other heads onto it too
updateHeads := GetCanonicalHash(bc.chainDb, block.NumberU64()) != block.Hash() updateHeads := GetCanonicalHash(bc.db, block.NumberU64()) != block.Hash()
// Add the block to the canonical chain number scheme and mark as the head // Add the block to the canonical chain number scheme and mark as the head
if err := WriteCanonicalHash(bc.chainDb, block.Hash(), block.NumberU64()); err != nil { if err := WriteCanonicalHash(bc.db, block.Hash(), block.NumberU64()); err != nil {
log.Crit("Failed to insert block number", "err", err) log.Crit("Failed to insert block number", "err", err)
} }
if err := WriteHeadBlockHash(bc.chainDb, block.Hash()); err != nil { if err := WriteHeadBlockHash(bc.db, block.Hash()); err != nil {
log.Crit("Failed to insert head block hash", "err", err) log.Crit("Failed to insert head block hash", "err", err)
} }
bc.currentBlock = block bc.currentBlock = block
// If the block is better than out head or is on a different chain, force update heads // If the block is better than our head or is on a different chain, force update heads
if updateHeads { if updateHeads {
bc.hc.SetCurrentHeader(block.Header()) bc.hc.SetCurrentHeader(block.Header())
if err := WriteHeadFastBlockHash(bc.chainDb, block.Hash()); err != nil { if err := WriteHeadFastBlockHash(bc.db, block.Hash()); err != nil {
log.Crit("Failed to insert head fast block hash", "err", err) log.Crit("Failed to insert head fast block hash", "err", err)
} }
bc.currentFastBlock = block bc.currentFastBlock = block
@ -489,7 +514,7 @@ func (bc *BlockChain) GetBody(hash common.Hash) *types.Body {
body := cached.(*types.Body) body := cached.(*types.Body)
return body return body
} }
body := GetBody(bc.chainDb, hash, bc.hc.GetBlockNumber(hash)) body := GetBody(bc.db, hash, bc.hc.GetBlockNumber(hash))
if body == nil { if body == nil {
return nil return nil
} }
@ -505,7 +530,7 @@ func (bc *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue {
if cached, ok := bc.bodyRLPCache.Get(hash); ok { if cached, ok := bc.bodyRLPCache.Get(hash); ok {
return cached.(rlp.RawValue) return cached.(rlp.RawValue)
} }
body := GetBodyRLP(bc.chainDb, hash, bc.hc.GetBlockNumber(hash)) body := GetBodyRLP(bc.db, hash, bc.hc.GetBlockNumber(hash))
if len(body) == 0 { if len(body) == 0 {
return nil return nil
} }
@ -519,21 +544,25 @@ func (bc *BlockChain) HasBlock(hash common.Hash, number uint64) bool {
if bc.blockCache.Contains(hash) { if bc.blockCache.Contains(hash) {
return true return true
} }
ok, _ := bc.chainDb.Has(blockBodyKey(hash, number)) ok, _ := bc.db.Has(blockBodyKey(hash, number))
return ok return ok
} }
// HasState checks if state trie is fully present in the database or not.
func (bc *BlockChain) HasState(hash common.Hash) bool {
_, err := bc.stateCache.OpenTrie(hash)
return err == nil
}
// HasBlockAndState checks if a block and associated state trie is fully present // HasBlockAndState checks if a block and associated state trie is fully present
// in the database or not, caching it if present. // in the database or not, caching it if present.
func (bc *BlockChain) HasBlockAndState(hash common.Hash) bool { func (bc *BlockChain) HasBlockAndState(hash common.Hash, number uint64) bool {
// Check first that the block itself is known // Check first that the block itself is known
block := bc.GetBlockByHash(hash) block := bc.GetBlock(hash, number)
if block == nil { if block == nil {
return false return false
} }
// Ensure the associated state is also present return bc.HasState(block.Root())
_, err := bc.stateCache.OpenTrie(block.Root())
return err == nil
} }
// GetBlock retrieves a block from the database by hash and number, // GetBlock retrieves a block from the database by hash and number,
@ -543,7 +572,7 @@ func (bc *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
if block, ok := bc.blockCache.Get(hash); ok { if block, ok := bc.blockCache.Get(hash); ok {
return block.(*types.Block) return block.(*types.Block)
} }
block := GetBlock(bc.chainDb, hash, number) block := GetBlock(bc.db, hash, number)
if block == nil { if block == nil {
return nil return nil
} }
@ -560,13 +589,18 @@ func (bc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block {
// GetBlockByNumber retrieves a block from the database by number, caching it // GetBlockByNumber retrieves a block from the database by number, caching it
// (associated with its hash) if found. // (associated with its hash) if found.
func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block { func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {
hash := GetCanonicalHash(bc.chainDb, number) hash := GetCanonicalHash(bc.db, number)
if hash == (common.Hash{}) { if hash == (common.Hash{}) {
return nil return nil
} }
return bc.GetBlock(hash, number) return bc.GetBlock(hash, number)
} }
// GetReceiptsByHash retrieves the receipts for all transactions in a given block.
func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
return GetBlockReceipts(bc.db, hash, GetBlockNumber(bc.db, hash))
}
// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors. // GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors.
// [deprecated by eth/62] // [deprecated by eth/62]
func (bc *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) { func (bc *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) {
@ -594,6 +628,12 @@ func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.
return uncles return uncles
} }
// TrieNode retrieves a blob of data associated with a trie node (or code hash)
// either from ephemeral in-memory cache, or from persistent storage.
func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) {
return bc.stateCache.TrieDB().Node(hash)
}
// Stop stops the blockchain service. If any imports are currently in progress // Stop stops the blockchain service. If any imports are currently in progress
// it will abort them using the procInterrupt. // it will abort them using the procInterrupt.
func (bc *BlockChain) Stop() { func (bc *BlockChain) Stop() {
@ -606,6 +646,33 @@ func (bc *BlockChain) Stop() {
atomic.StoreInt32(&bc.procInterrupt, 1) atomic.StoreInt32(&bc.procInterrupt, 1)
bc.wg.Wait() bc.wg.Wait()
// Ensure the state of a recent block is also stored to disk before exiting.
// It is fine if this state does not exist (fast start/stop cycle), but it is
// advisable to leave an N block gap from the head so 1) a restart loads up
// the last N blocks as sync assistance to remote nodes; 2) a restart during
// a (small) reorg doesn't require deep reprocesses; 3) chain "repair" from
// missing states are constantly tested.
//
// This may be tuned a bit on mainnet if its too annoying to reprocess the last
// N blocks.
if !bc.cacheConfig.Disabled {
triedb := bc.stateCache.TrieDB()
if number := bc.CurrentBlock().NumberU64(); number >= triesInMemory {
recent := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - triesInMemory + 1)
log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root())
if err := triedb.Commit(recent.Root(), true); err != nil {
log.Error("Failed to commit recent state trie", "err", err)
}
}
for !bc.triegc.Empty() {
triedb.Dereference(bc.triegc.PopItem().(common.Hash), common.Hash{})
}
if size := triedb.Size(); size != 0 {
log.Error("Dangling trie nodes after full cleanup")
}
}
log.Info("Blockchain manager stopped") log.Info("Blockchain manager stopped")
} }
@ -650,11 +717,11 @@ func (bc *BlockChain) Rollback(chain []common.Hash) {
} }
if bc.currentFastBlock.Hash() == hash { if bc.currentFastBlock.Hash() == hash {
bc.currentFastBlock = bc.GetBlock(bc.currentFastBlock.ParentHash(), bc.currentFastBlock.NumberU64()-1) bc.currentFastBlock = bc.GetBlock(bc.currentFastBlock.ParentHash(), bc.currentFastBlock.NumberU64()-1)
WriteHeadFastBlockHash(bc.chainDb, bc.currentFastBlock.Hash()) WriteHeadFastBlockHash(bc.db, bc.currentFastBlock.Hash())
} }
if bc.currentBlock.Hash() == hash { if bc.currentBlock.Hash() == hash {
bc.currentBlock = bc.GetBlock(bc.currentBlock.ParentHash(), bc.currentBlock.NumberU64()-1) bc.currentBlock = bc.GetBlock(bc.currentBlock.ParentHash(), bc.currentBlock.NumberU64()-1)
WriteHeadBlockHash(bc.chainDb, bc.currentBlock.Hash()) WriteHeadBlockHash(bc.db, bc.currentBlock.Hash())
} }
} }
} }
@ -713,7 +780,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
stats = struct{ processed, ignored int32 }{} stats = struct{ processed, ignored int32 }{}
start = time.Now() start = time.Now()
bytes = 0 bytes = 0
batch = bc.chainDb.NewBatch() batch = bc.db.NewBatch()
) )
for i, block := range blockChain { for i, block := range blockChain {
receipts := receiptChain[i] receipts := receiptChain[i]
@ -731,7 +798,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
continue continue
} }
// Compute all the non-consensus fields of the receipts // Compute all the non-consensus fields of the receipts
SetReceiptsData(bc.config, block, receipts) SetReceiptsData(bc.chainConfig, block, receipts)
// Write all the data out into the database // Write all the data out into the database
if err := WriteBody(batch, block.Hash(), block.NumberU64(), block.Body()); err != nil { if err := WriteBody(batch, block.Hash(), block.NumberU64(), block.Body()); err != nil {
return i, fmt.Errorf("failed to write block body: %v", err) return i, fmt.Errorf("failed to write block body: %v", err)
@ -749,7 +816,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
return 0, err return 0, err
} }
bytes += batch.ValueSize() bytes += batch.ValueSize()
batch = bc.chainDb.NewBatch() batch.Reset()
} }
} }
if batch.ValueSize() > 0 { if batch.ValueSize() > 0 {
@ -764,7 +831,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
head := blockChain[len(blockChain)-1] head := blockChain[len(blockChain)-1]
if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case
if bc.GetTd(bc.currentFastBlock.Hash(), bc.currentFastBlock.NumberU64()).Cmp(td) < 0 { if bc.GetTd(bc.currentFastBlock.Hash(), bc.currentFastBlock.NumberU64()).Cmp(td) < 0 {
if err := WriteHeadFastBlockHash(bc.chainDb, head.Hash()); err != nil { if err := WriteHeadFastBlockHash(bc.db, head.Hash()); err != nil {
log.Crit("Failed to update head fast block hash", "err", err) log.Crit("Failed to update head fast block hash", "err", err)
} }
bc.currentFastBlock = head bc.currentFastBlock = head
@ -775,15 +842,33 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
log.Info("Imported new block receipts", log.Info("Imported new block receipts",
"count", stats.processed, "count", stats.processed,
"elapsed", common.PrettyDuration(time.Since(start)), "elapsed", common.PrettyDuration(time.Since(start)),
"bytes", bytes,
"number", head.Number(), "number", head.Number(),
"hash", head.Hash(), "hash", head.Hash(),
"size", common.StorageSize(bytes),
"ignored", stats.ignored) "ignored", stats.ignored)
return 0, nil return 0, nil
} }
// WriteBlock writes the block to the chain. var lastWrite uint64
func (bc *BlockChain) WriteBlockAndState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
// WriteBlockWithoutState writes only the block and its metadata to the database,
// but does not write any state. This is used to construct competing side forks
// up to the point where they exceed the canonical total difficulty.
func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (err error) {
bc.wg.Add(1)
defer bc.wg.Done()
if err := bc.hc.WriteTd(block.Hash(), block.NumberU64(), td); err != nil {
return err
}
if err := WriteBlock(bc.db, block); err != nil {
return err
}
return nil
}
// WriteBlockWithState writes the block and all associated state to the database.
func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
bc.wg.Add(1) bc.wg.Add(1)
defer bc.wg.Done() defer bc.wg.Done()
@ -804,17 +889,69 @@ func (bc *BlockChain) WriteBlockAndState(block *types.Block, receipts []*types.R
return NonStatTy, err return NonStatTy, err
} }
// Write other block data using a batch. // Write other block data using a batch.
batch := bc.chainDb.NewBatch() batch := bc.db.NewBatch()
if err := WriteBlock(batch, block); err != nil { if err := WriteBlock(batch, block); err != nil {
return NonStatTy, err return NonStatTy, err
} }
if _, err := state.CommitTo(batch, bc.config.IsEIP158(block.Number())); err != nil { root, err := state.Commit(bc.chainConfig.IsEIP158(block.Number()))
if err != nil {
return NonStatTy, err return NonStatTy, err
} }
triedb := bc.stateCache.TrieDB()
// If we're running an archive node, always flush
if bc.cacheConfig.Disabled {
if err := triedb.Commit(root, false); err != nil {
return NonStatTy, err
}
} else {
// Full but not archive node, do proper garbage collection
triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
bc.triegc.Push(root, -float32(block.NumberU64()))
if current := block.NumberU64(); current > triesInMemory {
// Find the next state trie we need to commit
header := bc.GetHeaderByNumber(current - triesInMemory)
chosen := header.Number.Uint64()
// Only write to disk if we exceeded our memory allowance *and* also have at
// least a given number of tries gapped.
var (
size = triedb.Size()
limit = common.StorageSize(bc.cacheConfig.TrieNodeLimit) * 1024 * 1024
)
if size > limit || bc.gcproc > bc.cacheConfig.TrieTimeLimit {
// If we're exceeding limits but haven't reached a large enough memory gap,
// warn the user that the system is becoming unstable.
if chosen < lastWrite+triesInMemory {
switch {
case size >= 2*limit:
log.Warn("State memory usage too high, committing", "size", size, "limit", limit, "optimum", float64(chosen-lastWrite)/triesInMemory)
case bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit:
log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", bc.cacheConfig.TrieTimeLimit, "optimum", float64(chosen-lastWrite)/triesInMemory)
}
}
// If optimum or critical limits reached, write to disk
if chosen >= lastWrite+triesInMemory || size >= 2*limit || bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
triedb.Commit(header.Root, true)
lastWrite = chosen
bc.gcproc = 0
}
}
// Garbage collect anything below our required write retention
for !bc.triegc.Empty() {
root, number := bc.triegc.Pop()
if uint64(-number) > chosen {
bc.triegc.Push(root, number)
break
}
triedb.Dereference(root.(common.Hash), common.Hash{})
}
}
}
if err := WriteBlockReceipts(batch, block.Hash(), block.NumberU64(), receipts); err != nil { if err := WriteBlockReceipts(batch, block.Hash(), block.NumberU64(), receipts); err != nil {
return NonStatTy, err return NonStatTy, err
} }
// If the total difficulty is higher than our known, add it to the canonical chain // If the total difficulty is higher than our known, add it to the canonical chain
// Second clause in the if statement reduces the vulnerability to selfish mining. // Second clause in the if statement reduces the vulnerability to selfish mining.
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf // Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
@ -835,7 +972,7 @@ func (bc *BlockChain) WriteBlockAndState(block *types.Block, receipts []*types.R
return NonStatTy, err return NonStatTy, err
} }
// Write hash preimages // Write hash preimages
if err := WritePreimages(bc.chainDb, block.NumberU64(), state.Preimages()); err != nil { if err := WritePreimages(bc.db, block.NumberU64(), state.Preimages()); err != nil {
return NonStatTy, err return NonStatTy, err
} }
status = CanonStatTy status = CanonStatTy
@ -927,31 +1064,64 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
if err == nil { if err == nil {
err = bc.Validator().ValidateBody(block) err = bc.Validator().ValidateBody(block)
} }
if err != nil { switch {
if err == ErrKnownBlock { case err == ErrKnownBlock:
// Block and state both already known. However if the current block is below
// this number we did a rollback and we should reimport it nonetheless.
if bc.CurrentBlock().NumberU64() >= block.NumberU64() {
stats.ignored++ stats.ignored++
continue continue
} }
if err == consensus.ErrFutureBlock { case err == consensus.ErrFutureBlock:
// Allow up to MaxFuture second in the future blocks. If this limit // Allow up to MaxFuture second in the future blocks. If this limit is exceeded
// is exceeded the chain is discarded and processed at a later time // the chain is discarded and processed at a later time if given.
// if given. max := big.NewInt(time.Now().Unix() + maxTimeFutureBlocks)
max := big.NewInt(time.Now().Unix() + maxTimeFutureBlocks) if block.Time().Cmp(max) > 0 {
if block.Time().Cmp(max) > 0 { return i, events, coalescedLogs, fmt.Errorf("future block: %v > %v", block.Time(), max)
return i, events, coalescedLogs, fmt.Errorf("future block: %v > %v", block.Time(), max) }
bc.futureBlocks.Add(block.Hash(), block)
stats.queued++
continue
case err == consensus.ErrUnknownAncestor && bc.futureBlocks.Contains(block.ParentHash()):
bc.futureBlocks.Add(block.Hash(), block)
stats.queued++
continue
case err == consensus.ErrPrunedAncestor:
// Block competing with the canonical chain, store in the db, but don't process
// until the competitor TD goes above the canonical TD
localTd := bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64())
externTd := new(big.Int).Add(bc.GetTd(block.ParentHash(), block.NumberU64()-1), block.Difficulty())
if localTd.Cmp(externTd) > 0 {
if err = bc.WriteBlockWithoutState(block, externTd); err != nil {
return i, events, coalescedLogs, err
} }
bc.futureBlocks.Add(block.Hash(), block)
stats.queued++
continue continue
} }
// Competitor chain beat canonical, gather all blocks from the common ancestor
var winner []*types.Block
if err == consensus.ErrUnknownAncestor && bc.futureBlocks.Contains(block.ParentHash()) { parent := bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
bc.futureBlocks.Add(block.Hash(), block) for !bc.HasState(parent.Root()) {
stats.queued++ winner = append(winner, parent)
continue parent = bc.GetBlock(parent.ParentHash(), parent.NumberU64()-1)
}
for j := 0; j < len(winner)/2; j++ {
winner[j], winner[len(winner)-1-j] = winner[len(winner)-1-j], winner[j]
}
// Import all the pruned blocks to make the state available
bc.chainmu.Unlock()
_, evs, logs, err := bc.insertChain(winner)
bc.chainmu.Lock()
events, coalescedLogs = evs, logs
if err != nil {
return i, events, coalescedLogs, err
} }
case err != nil:
bc.reportBlock(block, nil, err) bc.reportBlock(block, nil, err)
return i, events, coalescedLogs, err return i, events, coalescedLogs, err
} }
@ -979,8 +1149,10 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
bc.reportBlock(block, receipts, err) bc.reportBlock(block, receipts, err)
return i, events, coalescedLogs, err return i, events, coalescedLogs, err
} }
proctime := time.Since(bstart)
// Write the block to the chain and get the status. // Write the block to the chain and get the status.
status, err := bc.WriteBlockAndState(block, receipts, state) status, err := bc.WriteBlockWithState(block, receipts, state)
if err != nil { if err != nil {
return i, events, coalescedLogs, err return i, events, coalescedLogs, err
} }
@ -994,6 +1166,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
events = append(events, ChainEvent{block, block.Hash(), logs}) events = append(events, ChainEvent{block, block.Hash(), logs})
lastCanon = block lastCanon = block
// Only count canonical blocks for GC processing time
bc.gcproc += proctime
case SideStatTy: case SideStatTy:
log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), "elapsed", log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), "elapsed",
common.PrettyDuration(time.Since(bstart)), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles())) common.PrettyDuration(time.Since(bstart)), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()))
@ -1003,10 +1178,10 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
} }
stats.processed++ stats.processed++
stats.usedGas += usedGas stats.usedGas += usedGas
stats.report(chain, i) stats.report(chain, i, bc.stateCache.TrieDB().Size())
} }
// Append a single chain head event if we've progressed the chain // Append a single chain head event if we've progressed the chain
if lastCanon != nil && bc.LastBlockHash() == lastCanon.Hash() { if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() {
events = append(events, ChainHeadEvent{lastCanon}) events = append(events, ChainHeadEvent{lastCanon})
} }
return 0, events, coalescedLogs, nil return 0, events, coalescedLogs, nil
@ -1026,7 +1201,7 @@ const statsReportLimit = 8 * time.Second
// report prints statistics if some number of blocks have been processed // report prints statistics if some number of blocks have been processed
// or more than a few seconds have passed since the last message. // or more than a few seconds have passed since the last message.
func (st *insertStats) report(chain []*types.Block, index int) { func (st *insertStats) report(chain []*types.Block, index int, cache common.StorageSize) {
// Fetch the timings for the batch // Fetch the timings for the batch
var ( var (
now = mclock.Now() now = mclock.Now()
@ -1041,7 +1216,7 @@ func (st *insertStats) report(chain []*types.Block, index int) {
context := []interface{}{ context := []interface{}{
"blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000, "blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000,
"elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed), "elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed),
"number", end.Number(), "hash", end.Hash(), "number", end.Number(), "hash", end.Hash(), "cache", cache,
} }
if st.queued > 0 { if st.queued > 0 {
context = append(context, []interface{}{"queued", st.queued}...) context = append(context, []interface{}{"queued", st.queued}...)
@ -1077,7 +1252,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
// These logs are later announced as deleted. // These logs are later announced as deleted.
collectLogs = func(h common.Hash) { collectLogs = func(h common.Hash) {
// Coalesce logs and set 'Removed'. // Coalesce logs and set 'Removed'.
receipts := GetBlockReceipts(bc.chainDb, h, bc.hc.GetBlockNumber(h)) receipts := GetBlockReceipts(bc.db, h, bc.hc.GetBlockNumber(h))
for _, receipt := range receipts { for _, receipt := range receipts {
for _, log := range receipt.Logs { for _, log := range receipt.Logs {
del := *log del := *log
@ -1140,24 +1315,23 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
} else { } else {
log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "newnum", newBlock.Number(), "newhash", newBlock.Hash()) log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "newnum", newBlock.Number(), "newhash", newBlock.Hash())
} }
// Insert the new chain, taking care of the proper incremental order
var addedTxs types.Transactions var addedTxs types.Transactions
// insert blocks. Order does not matter. Last block will be written in ImportChain itself which creates the new head properly for i := len(newChain) - 1; i >= 0; i-- {
for _, block := range newChain {
// insert the block in the canonical way, re-writing history // insert the block in the canonical way, re-writing history
bc.insert(block) bc.insert(newChain[i])
// write lookup entries for hash based transaction/receipt searches // write lookup entries for hash based transaction/receipt searches
if err := WriteTxLookupEntries(bc.chainDb, block); err != nil { if err := WriteTxLookupEntries(bc.db, newChain[i]); err != nil {
return err return err
} }
addedTxs = append(addedTxs, block.Transactions()...) addedTxs = append(addedTxs, newChain[i].Transactions()...)
} }
// calculate the difference between deleted and added transactions // calculate the difference between deleted and added transactions
diff := types.TxDifference(deletedTxs, addedTxs) diff := types.TxDifference(deletedTxs, addedTxs)
// When transactions get deleted from the database that means the // When transactions get deleted from the database that means the
// receipts that were created in the fork must also be deleted // receipts that were created in the fork must also be deleted
for _, tx := range diff { for _, tx := range diff {
DeleteTxLookupEntry(bc.chainDb, tx.Hash()) DeleteTxLookupEntry(bc.db, tx.Hash())
} }
if len(deletedLogs) > 0 { if len(deletedLogs) > 0 {
go bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs}) go bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs})
@ -1249,7 +1423,7 @@ Hash: 0x%x
Error: %v Error: %v
############################## ##############################
`, bc.config, block.Number(), block.Hash(), receiptString, err)) `, bc.chainConfig, block.Number(), block.Hash(), receiptString, err))
} }
// InsertHeaderChain attempts to insert the given header chain in to the local // InsertHeaderChain attempts to insert the given header chain in to the local
@ -1356,7 +1530,7 @@ func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
} }
// Config retrieves the blockchain's chain configuration. // Config retrieves the blockchain's chain configuration.
func (bc *BlockChain) Config() *params.ChainConfig { return bc.config } func (bc *BlockChain) Config() *params.ChainConfig { return bc.chainConfig }
// Engine retrieves the blockchain's consensus engine. // Engine retrieves the blockchain's consensus engine.
func (bc *BlockChain) Engine() consensus.Engine { return bc.engine } func (bc *BlockChain) Engine() consensus.Engine { return bc.engine }

View file

@ -46,7 +46,7 @@ func newTestBlockChain(fake bool) *BlockChain {
if !fake { if !fake {
engine = ethash.NewTester() engine = ethash.NewTester()
} }
blockchain, err := NewBlockChain(db, gspec.Config, engine, vm.Config{}) blockchain, err := NewBlockChain(db, nil, gspec.Config, engine, vm.Config{})
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -148,9 +148,9 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
return err return err
} }
blockchain.mu.Lock() blockchain.mu.Lock()
WriteTd(blockchain.chainDb, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash()))) WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
WriteBlock(blockchain.chainDb, block) WriteBlock(blockchain.db, block)
statedb.CommitTo(blockchain.chainDb, false) statedb.Commit(false)
blockchain.mu.Unlock() blockchain.mu.Unlock()
} }
return nil return nil
@ -166,8 +166,8 @@ func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error
} }
// Manually insert the header into the database, but don't reorganise (allows subsequent testing) // Manually insert the header into the database, but don't reorganise (allows subsequent testing)
blockchain.mu.Lock() blockchain.mu.Lock()
WriteTd(blockchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash))) WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash)))
WriteHeader(blockchain.chainDb, header) WriteHeader(blockchain.db, header)
blockchain.mu.Unlock() blockchain.mu.Unlock()
} }
return nil return nil
@ -186,9 +186,9 @@ func TestLastBlock(t *testing.T) {
bchain := newTestBlockChain(false) bchain := newTestBlockChain(false)
defer bchain.Stop() defer bchain.Stop()
block := makeBlockChain(bchain.CurrentBlock(), 1, ethash.NewFaker(), bchain.chainDb, 0)[0] block := makeBlockChain(bchain.CurrentBlock(), 1, ethash.NewFaker(), bchain.db, 0)[0]
bchain.insert(block) bchain.insert(block)
if block.Hash() != GetHeadBlockHash(bchain.chainDb) { if block.Hash() != GetHeadBlockHash(bchain.db) {
t.Errorf("Write/Get HeadBlockHash failed") t.Errorf("Write/Get HeadBlockHash failed")
} }
} }
@ -496,7 +496,7 @@ func testReorgBadHashes(t *testing.T, full bool) {
} }
// Create a new BlockChain and check that it rolled back the state. // Create a new BlockChain and check that it rolled back the state.
ncm, err := NewBlockChain(bc.chainDb, bc.config, ethash.NewFaker(), vm.Config{}) ncm, err := NewBlockChain(bc.db, nil, bc.chainConfig, ethash.NewFaker(), vm.Config{})
if err != nil { if err != nil {
t.Fatalf("failed to create new chain manager: %v", err) t.Fatalf("failed to create new chain manager: %v", err)
} }
@ -609,7 +609,7 @@ func TestFastVsFullChains(t *testing.T) {
// Import the chain as an archive node for the comparison baseline // Import the chain as an archive node for the comparison baseline
archiveDb, _ := ethdb.NewMemDatabase() archiveDb, _ := ethdb.NewMemDatabase()
gspec.MustCommit(archiveDb) gspec.MustCommit(archiveDb)
archive, _ := NewBlockChain(archiveDb, gspec.Config, ethash.NewFaker(), vm.Config{}) archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer archive.Stop() defer archive.Stop()
if n, err := archive.InsertChain(blocks); err != nil { if n, err := archive.InsertChain(blocks); err != nil {
@ -618,7 +618,7 @@ func TestFastVsFullChains(t *testing.T) {
// Fast import the chain as a non-archive node to test // Fast import the chain as a non-archive node to test
fastDb, _ := ethdb.NewMemDatabase() fastDb, _ := ethdb.NewMemDatabase()
gspec.MustCommit(fastDb) gspec.MustCommit(fastDb)
fast, _ := NewBlockChain(fastDb, gspec.Config, ethash.NewFaker(), vm.Config{}) fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer fast.Stop() defer fast.Stop()
headers := make([]*types.Header, len(blocks)) headers := make([]*types.Header, len(blocks))
@ -696,7 +696,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
archiveDb, _ := ethdb.NewMemDatabase() archiveDb, _ := ethdb.NewMemDatabase()
gspec.MustCommit(archiveDb) gspec.MustCommit(archiveDb)
archive, _ := NewBlockChain(archiveDb, gspec.Config, ethash.NewFaker(), vm.Config{}) archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
if n, err := archive.InsertChain(blocks); err != nil { if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err) t.Fatalf("failed to process block %d: %v", n, err)
} }
@ -709,7 +709,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
// Import the chain as a non-archive node and ensure all pointers are updated // Import the chain as a non-archive node and ensure all pointers are updated
fastDb, _ := ethdb.NewMemDatabase() fastDb, _ := ethdb.NewMemDatabase()
gspec.MustCommit(fastDb) gspec.MustCommit(fastDb)
fast, _ := NewBlockChain(fastDb, gspec.Config, ethash.NewFaker(), vm.Config{}) fast, _ := NewBlockChain(fastDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer fast.Stop() defer fast.Stop()
headers := make([]*types.Header, len(blocks)) headers := make([]*types.Header, len(blocks))
@ -730,7 +730,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
lightDb, _ := ethdb.NewMemDatabase() lightDb, _ := ethdb.NewMemDatabase()
gspec.MustCommit(lightDb) gspec.MustCommit(lightDb)
light, _ := NewBlockChain(lightDb, gspec.Config, ethash.NewFaker(), vm.Config{}) light, _ := NewBlockChain(lightDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
if n, err := light.InsertHeaderChain(headers, 1); err != nil { if n, err := light.InsertHeaderChain(headers, 1); err != nil {
t.Fatalf("failed to insert header %d: %v", n, err) t.Fatalf("failed to insert header %d: %v", n, err)
} }
@ -799,7 +799,7 @@ func TestChainTxReorgs(t *testing.T) {
} }
}) })
// Import the chain. This runs all block validation rules. // Import the chain. This runs all block validation rules.
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
if i, err := blockchain.InsertChain(chain); err != nil { if i, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert original chain[%d]: %v", i, err) t.Fatalf("failed to insert original chain[%d]: %v", i, err)
} }
@ -870,7 +870,7 @@ func TestLogReorgs(t *testing.T) {
signer = types.NewEIP155Signer(gspec.Config.ChainId) signer = types.NewEIP155Signer(gspec.Config.ChainId)
) )
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer blockchain.Stop() defer blockchain.Stop()
rmLogsCh := make(chan RemovedLogsEvent) rmLogsCh := make(chan RemovedLogsEvent)
@ -917,7 +917,7 @@ func TestReorgSideEvent(t *testing.T) {
signer = types.NewEIP155Signer(gspec.Config.ChainId) signer = types.NewEIP155Signer(gspec.Config.ChainId)
) )
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer blockchain.Stop() defer blockchain.Stop()
chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {}) chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
@ -992,7 +992,7 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
bc := newTestBlockChain(true) bc := newTestBlockChain(true)
defer bc.Stop() defer bc.Stop()
chain, _ := GenerateChain(bc.config, bc.genesisBlock, ethash.NewFaker(), bc.chainDb, 10, func(i int, gen *BlockGen) {}) chain, _ := GenerateChain(bc.chainConfig, bc.genesisBlock, ethash.NewFaker(), bc.db, 10, func(i int, gen *BlockGen) {})
var pend sync.WaitGroup var pend sync.WaitGroup
pend.Add(len(chain)) pend.Add(len(chain))
@ -1003,14 +1003,14 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
// try to retrieve a block by its canonical hash and see if the block data can be retrieved. // try to retrieve a block by its canonical hash and see if the block data can be retrieved.
for { for {
ch := GetCanonicalHash(bc.chainDb, block.NumberU64()) ch := GetCanonicalHash(bc.db, block.NumberU64())
if ch == (common.Hash{}) { if ch == (common.Hash{}) {
continue // busy wait for canonical hash to be written continue // busy wait for canonical hash to be written
} }
if ch != block.Hash() { if ch != block.Hash() {
t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex()) t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex())
} }
fb := GetBlock(bc.chainDb, ch, block.NumberU64()) fb := GetBlock(bc.db, ch, block.NumberU64())
if fb == nil { if fb == nil {
t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex()) t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
} }
@ -1043,7 +1043,7 @@ func TestEIP155Transition(t *testing.T) {
genesis = gspec.MustCommit(db) genesis = gspec.MustCommit(db)
) )
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer blockchain.Stop() defer blockchain.Stop()
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) { blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, block *BlockGen) {
@ -1151,7 +1151,7 @@ func TestEIP161AccountRemoval(t *testing.T) {
} }
genesis = gspec.MustCommit(db) genesis = gspec.MustCommit(db)
) )
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer blockchain.Stop() defer blockchain.Stop()
blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, block *BlockGen) { blocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, block *BlockGen) {
@ -1197,3 +1197,150 @@ func TestEIP161AccountRemoval(t *testing.T) {
t.Error("account should not exist") t.Error("account should not exist")
} }
} }
// This is a regression test (i.e. as weird as it is, don't delete it ever), which
// tests that under weird reorg conditions the blockchain and its internal header-
// chain return the same latest block/header.
//
// https://github.com/ethereum/go-ethereum/pull/15941
func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
// Generate a canonical chain to act as the main dataset
engine := ethash.NewFaker()
db, _ := ethdb.NewMemDatabase()
genesis := new(Genesis).MustCommit(db)
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
// Generate a bunch of fork blocks, each side forking from the canonical chain
forks := make([]*types.Block, len(blocks))
for i := 0; i < len(forks); i++ {
parent := genesis
if i > 0 {
parent = blocks[i-1]
}
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
forks[i] = fork[0]
}
// Import the canonical and fork chain side by side, verifying the current block
// and current header consistency
diskdb, _ := ethdb.NewMemDatabase()
new(Genesis).MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
for i := 0; i < len(blocks); i++ {
if _, err := chain.InsertChain(blocks[i : i+1]); err != nil {
t.Fatalf("block %d: failed to insert into chain: %v", i, err)
}
if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() {
t.Errorf("block %d: current block/header mismatch: block #%d [%x…], header #%d [%x…]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
}
if _, err := chain.InsertChain(forks[i : i+1]); err != nil {
t.Fatalf(" fork %d: failed to insert into chain: %v", i, err)
}
if chain.CurrentBlock().Hash() != chain.CurrentHeader().Hash() {
t.Errorf(" fork %d: current block/header mismatch: block #%d [%x…], header #%d [%x…]", i, chain.CurrentBlock().Number(), chain.CurrentBlock().Hash().Bytes()[:4], chain.CurrentHeader().Number, chain.CurrentHeader().Hash().Bytes()[:4])
}
}
}
// Tests that importing small side forks doesn't leave junk in the trie database
// cache (which would eventually cause memory issues).
func TestTrieForkGC(t *testing.T) {
// Generate a canonical chain to act as the main dataset
engine := ethash.NewFaker()
db, _ := ethdb.NewMemDatabase()
genesis := new(Genesis).MustCommit(db)
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
// Generate a bunch of fork blocks, each side forking from the canonical chain
forks := make([]*types.Block, len(blocks))
for i := 0; i < len(forks); i++ {
parent := genesis
if i > 0 {
parent = blocks[i-1]
}
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
forks[i] = fork[0]
}
// Import the canonical and fork chain side by side, forcing the trie cache to cache both
diskdb, _ := ethdb.NewMemDatabase()
new(Genesis).MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
for i := 0; i < len(blocks); i++ {
if _, err := chain.InsertChain(blocks[i : i+1]); err != nil {
t.Fatalf("block %d: failed to insert into chain: %v", i, err)
}
if _, err := chain.InsertChain(forks[i : i+1]); err != nil {
t.Fatalf("fork %d: failed to insert into chain: %v", i, err)
}
}
// Dereference all the recent tries and ensure no past trie is left in
for i := 0; i < triesInMemory; i++ {
chain.stateCache.TrieDB().Dereference(blocks[len(blocks)-1-i].Root(), common.Hash{})
chain.stateCache.TrieDB().Dereference(forks[len(blocks)-1-i].Root(), common.Hash{})
}
if len(chain.stateCache.TrieDB().Nodes()) > 0 {
t.Fatalf("stale tries still alive after garbase collection")
}
}
// Tests that doing large reorgs works even if the state associated with the
// forking point is not available any more.
func TestLargeReorgTrieGC(t *testing.T) {
// Generate the original common chain segment and the two competing forks
engine := ethash.NewFaker()
db, _ := ethdb.NewMemDatabase()
genesis := new(Genesis).MustCommit(db)
shared, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 64, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{1}) })
original, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*triesInMemory, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{2}) })
competitor, _ := GenerateChain(params.TestChainConfig, shared[len(shared)-1], engine, db, 2*triesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
// Import the shared chain and the original canonical one
diskdb, _ := ethdb.NewMemDatabase()
new(Genesis).MustCommit(diskdb)
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
if err != nil {
t.Fatalf("failed to create tester chain: %v", err)
}
if _, err := chain.InsertChain(shared); err != nil {
t.Fatalf("failed to insert shared chain: %v", err)
}
if _, err := chain.InsertChain(original); err != nil {
t.Fatalf("failed to insert shared chain: %v", err)
}
// Ensure that the state associated with the forking point is pruned away
if node, _ := chain.stateCache.TrieDB().Node(shared[len(shared)-1].Root()); node != nil {
t.Fatalf("common-but-old ancestor still cache")
}
// Import the competitor chain without exceeding the canonical's TD and ensure
// we have not processed any of the blocks (protection against malicious blocks)
if _, err := chain.InsertChain(competitor[:len(competitor)-2]); err != nil {
t.Fatalf("failed to insert competitor chain: %v", err)
}
for i, block := range competitor[:len(competitor)-2] {
if node, _ := chain.stateCache.TrieDB().Node(block.Root()); node != nil {
t.Fatalf("competitor %d: low TD chain became processed", i)
}
}
// Import the head of the competitor chain, triggering the reorg and ensure we
// successfully reprocess all the stashed away blocks.
if _, err := chain.InsertChain(competitor[len(competitor)-2:]); err != nil {
t.Fatalf("failed to finalize competitor chain: %v", err)
}
for i, block := range competitor[:len(competitor)-triesInMemory] {
if node, _ := chain.stateCache.TrieDB().Node(block.Root()); node != nil {
t.Fatalf("competitor %d: competing chain state missing", i)
}
}
}

View file

@ -203,6 +203,9 @@ func (c *ChainIndexer) eventLoop(currentHeader *types.Header, events chan ChainE
if header.ParentHash != prevHash { if header.ParentHash != prevHash {
// Reorg to the common ancestor (might not exist in light sync mode, skip reorg then) // Reorg to the common ancestor (might not exist in light sync mode, skip reorg then)
// TODO(karalabe, zsfelfoldi): This seems a bit brittle, can we detect this case explicitly? // TODO(karalabe, zsfelfoldi): This seems a bit brittle, can we detect this case explicitly?
// TODO(karalabe): This operation is expensive and might block, causing the event system to
// potentially also lock up. We need to do with on a different thread somehow.
if h := FindCommonAncestor(c.chainDb, prevHeader, header); h != nil { if h := FindCommonAncestor(c.chainDb, prevHeader, header); h != nil {
c.newHead(h.Number.Uint64(), true) c.newHead(h.Number.Uint64(), true)
} }

View file

@ -166,7 +166,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) { genblock := func(i int, parent *types.Block, statedb *state.StateDB) (*types.Block, types.Receipts) {
// TODO(karalabe): This is needed for clique, which depends on multiple blocks. // TODO(karalabe): This is needed for clique, which depends on multiple blocks.
// It's nonetheless ugly to spin up a blockchain here. Get rid of this somehow. // It's nonetheless ugly to spin up a blockchain here. Get rid of this somehow.
blockchain, _ := NewBlockChain(db, config, engine, vm.Config{}) blockchain, _ := NewBlockChain(db, nil, config, engine, vm.Config{})
defer blockchain.Stop() defer blockchain.Stop()
b := &BlockGen{i: i, parent: parent, chain: blocks, chainReader: blockchain, statedb: statedb, config: config, engine: engine} b := &BlockGen{i: i, parent: parent, chain: blocks, chainReader: blockchain, statedb: statedb, config: config, engine: engine}
@ -192,10 +192,13 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
if b.engine != nil { if b.engine != nil {
block, _ := b.engine.Finalize(b.chainReader, b.header, statedb, b.txs, b.uncles, b.receipts) block, _ := b.engine.Finalize(b.chainReader, b.header, statedb, b.txs, b.uncles, b.receipts)
// Write state changes to db // Write state changes to db
_, err := statedb.CommitTo(db, config.IsEIP158(b.header.Number)) root, err := statedb.Commit(config.IsEIP158(b.header.Number))
if err != nil { if err != nil {
panic(fmt.Sprintf("state write error: %v", err)) panic(fmt.Sprintf("state write error: %v", err))
} }
if err := statedb.Database().TrieDB().Commit(root, false); err != nil {
panic(fmt.Sprintf("trie write error: %v", err))
}
return block, b.receipts return block, b.receipts
} }
return nil, nil return nil, nil
@ -246,7 +249,7 @@ func newCanonical(engine consensus.Engine, n int, full bool) (ethdb.Database, *B
db, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
genesis := gspec.MustCommit(db) genesis := gspec.MustCommit(db)
blockchain, _ := NewBlockChain(db, params.AllEthashProtocolChanges, engine, vm.Config{}) blockchain, _ := NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{})
// Create and inject the requested chain // Create and inject the requested chain
if n == 0 { if n == 0 {
return db, blockchain, nil return db, blockchain, nil

View file

@ -79,7 +79,7 @@ func ExampleGenerateChain() {
}) })
// Import the chain. This runs all block validation rules. // Import the chain. This runs all block validation rules.
blockchain, _ := NewBlockChain(db, gspec.Config, ethash.NewFaker(), vm.Config{}) blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{})
defer blockchain.Stop() defer blockchain.Stop()
if i, err := blockchain.InsertChain(chain); err != nil { if i, err := blockchain.InsertChain(chain); err != nil {

View file

@ -45,7 +45,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
proConf.DAOForkBlock = forkBlock proConf.DAOForkBlock = forkBlock
proConf.DAOForkSupport = true proConf.DAOForkSupport = true
proBc, _ := NewBlockChain(proDb, &proConf, ethash.NewFaker(), vm.Config{}) proBc, _ := NewBlockChain(proDb, nil, &proConf, ethash.NewFaker(), vm.Config{})
defer proBc.Stop() defer proBc.Stop()
conDb, _ := ethdb.NewMemDatabase() conDb, _ := ethdb.NewMemDatabase()
@ -55,7 +55,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
conConf.DAOForkBlock = forkBlock conConf.DAOForkBlock = forkBlock
conConf.DAOForkSupport = false conConf.DAOForkSupport = false
conBc, _ := NewBlockChain(conDb, &conConf, ethash.NewFaker(), vm.Config{}) conBc, _ := NewBlockChain(conDb, nil, &conConf, ethash.NewFaker(), vm.Config{})
defer conBc.Stop() defer conBc.Stop()
if _, err := proBc.InsertChain(prefix); err != nil { if _, err := proBc.InsertChain(prefix); err != nil {
@ -69,7 +69,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Create a pro-fork block, and try to feed into the no-fork chain // Create a pro-fork block, and try to feed into the no-fork chain
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
gspec.MustCommit(db) gspec.MustCommit(db)
bc, _ := NewBlockChain(db, &conConf, ethash.NewFaker(), vm.Config{}) bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{})
defer bc.Stop() defer bc.Stop()
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64())) blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
@ -79,6 +79,9 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil { if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import contra-fork chain for expansion: %v", err) t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
} }
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
t.Fatalf("failed to commit contra-fork head for expansion: %v", err)
}
blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
if _, err := conBc.InsertChain(blocks); err == nil { if _, err := conBc.InsertChain(blocks); err == nil {
t.Fatalf("contra-fork chain accepted pro-fork block: %v", blocks[0]) t.Fatalf("contra-fork chain accepted pro-fork block: %v", blocks[0])
@ -91,7 +94,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Create a no-fork block, and try to feed into the pro-fork chain // Create a no-fork block, and try to feed into the pro-fork chain
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
gspec.MustCommit(db) gspec.MustCommit(db)
bc, _ = NewBlockChain(db, &proConf, ethash.NewFaker(), vm.Config{}) bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{})
defer bc.Stop() defer bc.Stop()
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64())) blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
@ -101,6 +104,9 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil { if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import pro-fork chain for expansion: %v", err) t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
} }
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
t.Fatalf("failed to commit pro-fork head for expansion: %v", err)
}
blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
if _, err := proBc.InsertChain(blocks); err == nil { if _, err := proBc.InsertChain(blocks); err == nil {
t.Fatalf("pro-fork chain accepted contra-fork block: %v", blocks[0]) t.Fatalf("pro-fork chain accepted contra-fork block: %v", blocks[0])
@ -114,7 +120,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Verify that contra-forkers accept pro-fork extra-datas after forking finishes // Verify that contra-forkers accept pro-fork extra-datas after forking finishes
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
gspec.MustCommit(db) gspec.MustCommit(db)
bc, _ := NewBlockChain(db, &conConf, ethash.NewFaker(), vm.Config{}) bc, _ := NewBlockChain(db, nil, &conConf, ethash.NewFaker(), vm.Config{})
defer bc.Stop() defer bc.Stop()
blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64())) blocks := conBc.GetBlocksFromHash(conBc.CurrentBlock().Hash(), int(conBc.CurrentBlock().NumberU64()))
@ -124,6 +130,9 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil { if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import contra-fork chain for expansion: %v", err) t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
} }
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
t.Fatalf("failed to commit contra-fork head for expansion: %v", err)
}
blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
if _, err := conBc.InsertChain(blocks); err != nil { if _, err := conBc.InsertChain(blocks); err != nil {
t.Fatalf("contra-fork chain didn't accept pro-fork block post-fork: %v", err) t.Fatalf("contra-fork chain didn't accept pro-fork block post-fork: %v", err)
@ -131,7 +140,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Verify that pro-forkers accept contra-fork extra-datas after forking finishes // Verify that pro-forkers accept contra-fork extra-datas after forking finishes
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
gspec.MustCommit(db) gspec.MustCommit(db)
bc, _ = NewBlockChain(db, &proConf, ethash.NewFaker(), vm.Config{}) bc, _ = NewBlockChain(db, nil, &proConf, ethash.NewFaker(), vm.Config{})
defer bc.Stop() defer bc.Stop()
blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64())) blocks = proBc.GetBlocksFromHash(proBc.CurrentBlock().Hash(), int(proBc.CurrentBlock().NumberU64()))
@ -141,6 +150,9 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil { if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import pro-fork chain for expansion: %v", err) t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
} }
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
t.Fatalf("failed to commit pro-fork head for expansion: %v", err)
}
blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
if _, err := proBc.InsertChain(blocks); err != nil { if _, err := proBc.InsertChain(blocks); err != nil {
t.Fatalf("pro-fork chain didn't accept contra-fork block post-fork: %v", err) t.Fatalf("pro-fork chain didn't accept contra-fork block post-fork: %v", err)

View file

@ -44,6 +44,11 @@ func (gp *GasPool) SubGas(amount uint64) error {
return nil return nil
} }
// Gas returns the amount of gas remaining in the pool.
func (gp *GasPool) Gas() uint64 {
return uint64(*gp)
}
func (gp *GasPool) String() string { func (gp *GasPool) String() string {
return fmt.Sprintf("%d", *gp) return fmt.Sprintf("%d", *gp)
} }

View file

@ -169,10 +169,9 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig
// Check whether the genesis block is already written. // Check whether the genesis block is already written.
if genesis != nil { if genesis != nil {
block, _ := genesis.ToBlock() hash := genesis.ToBlock(nil).Hash()
hash := block.Hash()
if hash != stored { if hash != stored {
return genesis.Config, block.Hash(), &GenesisMismatchError{stored, hash} return genesis.Config, hash, &GenesisMismatchError{stored, hash}
} }
} }
@ -220,9 +219,12 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
} }
} }
// ToBlock creates the block and state of a genesis specification. // ToBlock creates the genesis block and writes state of a genesis specification
func (g *Genesis) ToBlock() (*types.Block, *state.StateDB) { // to the given database (or discards it if nil).
db, _ := ethdb.NewMemDatabase() func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
if db == nil {
db, _ = ethdb.NewMemDatabase()
}
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db)) statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
for addr, account := range g.Alloc { for addr, account := range g.Alloc {
statedb.AddBalance(addr, account.Balance) statedb.AddBalance(addr, account.Balance)
@ -252,19 +254,19 @@ func (g *Genesis) ToBlock() (*types.Block, *state.StateDB) {
if g.Difficulty == nil { if g.Difficulty == nil {
head.Difficulty = params.GenesisDifficulty head.Difficulty = params.GenesisDifficulty
} }
return types.NewBlock(head, nil, nil, nil), statedb statedb.Commit(false)
statedb.Database().TrieDB().Commit(root, true)
return types.NewBlock(head, nil, nil, nil)
} }
// Commit writes the block and state of a genesis specification to the database. // Commit writes the block and state of a genesis specification to the database.
// The block is committed as the canonical head block. // The block is committed as the canonical head block.
func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) { func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) {
block, statedb := g.ToBlock() block := g.ToBlock(db)
if block.Number().Sign() != 0 { if block.Number().Sign() != 0 {
return nil, fmt.Errorf("can't commit genesis block with number > 0") return nil, fmt.Errorf("can't commit genesis block with number > 0")
} }
if _, err := statedb.CommitTo(db, false); err != nil {
return nil, fmt.Errorf("cannot write state: %v", err)
}
if err := WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty); err != nil { if err := WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty); err != nil {
return nil, err return nil, err
} }

View file

@ -30,11 +30,11 @@ import (
) )
func TestDefaultGenesisBlock(t *testing.T) { func TestDefaultGenesisBlock(t *testing.T) {
block, _ := DefaultGenesisBlock().ToBlock() block := DefaultGenesisBlock().ToBlock(nil)
if block.Hash() != params.MainnetGenesisHash { if block.Hash() != params.MainnetGenesisHash {
t.Errorf("wrong mainnet genesis hash, got %v, want %v", block.Hash(), params.MainnetGenesisHash) t.Errorf("wrong mainnet genesis hash, got %v, want %v", block.Hash(), params.MainnetGenesisHash)
} }
block, _ = DefaultTestnetGenesisBlock().ToBlock() block = DefaultTestnetGenesisBlock().ToBlock(nil)
if block.Hash() != params.TestnetGenesisHash { if block.Hash() != params.TestnetGenesisHash {
t.Errorf("wrong testnet genesis hash, got %v, want %v", block.Hash(), params.TestnetGenesisHash) t.Errorf("wrong testnet genesis hash, got %v, want %v", block.Hash(), params.TestnetGenesisHash)
} }
@ -118,7 +118,7 @@ func TestSetupGenesis(t *testing.T) {
// Commit the 'old' genesis block with Homestead transition at #2. // Commit the 'old' genesis block with Homestead transition at #2.
// Advance to block #4, past the homestead transition block of customg. // Advance to block #4, past the homestead transition block of customg.
genesis := oldcustomg.MustCommit(db) genesis := oldcustomg.MustCommit(db)
bc, _ := NewBlockChain(db, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{}) bc, _ := NewBlockChain(db, nil, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{})
defer bc.Stop() defer bc.Stop()
bc.SetValidator(bproc{}) bc.SetValidator(bproc{})
bc.InsertChain(makeBlockChainWithDiff(genesis, []int{2, 3, 4, 5}, 0)) bc.InsertChain(makeBlockChainWithDiff(genesis, []int{2, 3, 4, 5}, 0))

View file

@ -40,16 +40,23 @@ const (
// Database wraps access to tries and contract code. // Database wraps access to tries and contract code.
type Database interface { type Database interface {
// Accessing tries:
// OpenTrie opens the main account trie. // OpenTrie opens the main account trie.
// OpenStorageTrie opens the storage trie of an account.
OpenTrie(root common.Hash) (Trie, error) OpenTrie(root common.Hash) (Trie, error)
// OpenStorageTrie opens the storage trie of an account.
OpenStorageTrie(addrHash, root common.Hash) (Trie, error) OpenStorageTrie(addrHash, root common.Hash) (Trie, error)
// Accessing contract code:
ContractCode(addrHash, codeHash common.Hash) ([]byte, error)
ContractCodeSize(addrHash, codeHash common.Hash) (int, error)
// CopyTrie returns an independent copy of the given trie. // CopyTrie returns an independent copy of the given trie.
CopyTrie(Trie) Trie CopyTrie(Trie) Trie
// ContractCode retrieves a particular contract's code.
ContractCode(addrHash, codeHash common.Hash) ([]byte, error)
// ContractCodeSize retrieves a particular contracts code's size.
ContractCodeSize(addrHash, codeHash common.Hash) (int, error)
// TrieDB retrieves the low level trie database used for data storage.
TrieDB() *trie.Database
} }
// Trie is a Ethereum Merkle Trie. // Trie is a Ethereum Merkle Trie.
@ -57,26 +64,33 @@ type Trie interface {
TryGet(key []byte) ([]byte, error) TryGet(key []byte) ([]byte, error)
TryUpdate(key, value []byte) error TryUpdate(key, value []byte) error
TryDelete(key []byte) error TryDelete(key []byte) error
CommitTo(trie.DatabaseWriter) (common.Hash, error) Commit(onleaf trie.LeafCallback) (common.Hash, error)
Hash() common.Hash Hash() common.Hash
NodeIterator(startKey []byte) trie.NodeIterator NodeIterator(startKey []byte) trie.NodeIterator
GetKey([]byte) []byte // TODO(fjl): remove this when SecureTrie is removed GetKey([]byte) []byte // TODO(fjl): remove this when SecureTrie is removed
Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error
} }
// NewDatabase creates a backing store for state. The returned database is safe for // NewDatabase creates a backing store for state. The returned database is safe for
// concurrent use and retains cached trie nodes in memory. // concurrent use and retains cached trie nodes in memory. The pool is an optional
// intermediate trie-node memory pool between the low level storage layer and the
// high level trie abstraction.
func NewDatabase(db ethdb.Database) Database { func NewDatabase(db ethdb.Database) Database {
csc, _ := lru.New(codeSizeCacheSize) csc, _ := lru.New(codeSizeCacheSize)
return &cachingDB{db: db, codeSizeCache: csc} return &cachingDB{
db: trie.NewDatabase(db),
codeSizeCache: csc,
}
} }
type cachingDB struct { type cachingDB struct {
db ethdb.Database db *trie.Database
mu sync.Mutex mu sync.Mutex
pastTries []*trie.SecureTrie pastTries []*trie.SecureTrie
codeSizeCache *lru.Cache codeSizeCache *lru.Cache
} }
// OpenTrie opens the main account trie.
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) { func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
db.mu.Lock() db.mu.Lock()
defer db.mu.Unlock() defer db.mu.Unlock()
@ -105,10 +119,12 @@ func (db *cachingDB) pushTrie(t *trie.SecureTrie) {
} }
} }
// OpenStorageTrie opens the storage trie of an account.
func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) { func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) {
return trie.NewSecure(root, db.db, 0) return trie.NewSecure(root, db.db, 0)
} }
// CopyTrie returns an independent copy of the given trie.
func (db *cachingDB) CopyTrie(t Trie) Trie { func (db *cachingDB) CopyTrie(t Trie) Trie {
switch t := t.(type) { switch t := t.(type) {
case cachedTrie: case cachedTrie:
@ -120,14 +136,16 @@ func (db *cachingDB) CopyTrie(t Trie) Trie {
} }
} }
// ContractCode retrieves a particular contract's code.
func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) { func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {
code, err := db.db.Get(codeHash[:]) code, err := db.db.Node(codeHash)
if err == nil { if err == nil {
db.codeSizeCache.Add(codeHash, len(code)) db.codeSizeCache.Add(codeHash, len(code))
} }
return code, err return code, err
} }
// ContractCodeSize retrieves a particular contracts code's size.
func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) { func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) {
if cached, ok := db.codeSizeCache.Get(codeHash); ok { if cached, ok := db.codeSizeCache.Get(codeHash); ok {
return cached.(int), nil return cached.(int), nil
@ -139,16 +157,25 @@ func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, erro
return len(code), err return len(code), err
} }
// TrieDB retrieves any intermediate trie-node caching layer.
func (db *cachingDB) TrieDB() *trie.Database {
return db.db
}
// cachedTrie inserts its trie into a cachingDB on commit. // cachedTrie inserts its trie into a cachingDB on commit.
type cachedTrie struct { type cachedTrie struct {
*trie.SecureTrie *trie.SecureTrie
db *cachingDB db *cachingDB
} }
func (m cachedTrie) CommitTo(dbw trie.DatabaseWriter) (common.Hash, error) { func (m cachedTrie) Commit(onleaf trie.LeafCallback) (common.Hash, error) {
root, err := m.SecureTrie.CommitTo(dbw) root, err := m.SecureTrie.Commit(onleaf)
if err == nil { if err == nil {
m.db.pushTrie(m.SecureTrie) m.db.pushTrie(m.SecureTrie)
} }
return root, err return root, err
} }
func (m cachedTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error {
return m.SecureTrie.Prove(key, fromLevel, proofDb)
}

View file

@ -21,12 +21,13 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
) )
// Tests that the node iterator indeed walks over the entire database contents. // Tests that the node iterator indeed walks over the entire database contents.
func TestNodeIteratorCoverage(t *testing.T) { func TestNodeIteratorCoverage(t *testing.T) {
// Create some arbitrary test state to iterate // Create some arbitrary test state to iterate
db, mem, root, _ := makeTestState() db, root, _ := makeTestState()
state, err := New(root, db) state, err := New(root, db)
if err != nil { if err != nil {
@ -39,14 +40,18 @@ func TestNodeIteratorCoverage(t *testing.T) {
hashes[it.Hash] = struct{}{} hashes[it.Hash] = struct{}{}
} }
} }
// Cross check the iterated hashes and the database/nodepool content
// Cross check the hashes and the database itself
for hash := range hashes { for hash := range hashes {
if _, err := mem.Get(hash.Bytes()); err != nil { if _, err := db.TrieDB().Node(hash); err != nil {
t.Errorf("failed to retrieve reported node %x: %v", hash, err) t.Errorf("failed to retrieve reported node %x", hash)
} }
} }
for _, key := range mem.Keys() { for _, hash := range db.TrieDB().Nodes() {
if _, ok := hashes[hash]; !ok {
t.Errorf("state entry not reported %x", hash)
}
}
for _, key := range db.TrieDB().DiskDB().(*ethdb.MemDatabase).Keys() {
if bytes.HasPrefix(key, []byte("secure-key-")) { if bytes.HasPrefix(key, []byte("secure-key-")) {
continue continue
} }

View file

@ -25,7 +25,6 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
) )
var emptyCodeHash = crypto.Keccak256(nil) var emptyCodeHash = crypto.Keccak256(nil)
@ -238,12 +237,12 @@ func (self *stateObject) updateRoot(db Database) {
// CommitTrie the storage trie of the object to dwb. // CommitTrie the storage trie of the object to dwb.
// This updates the trie root. // This updates the trie root.
func (self *stateObject) CommitTrie(db Database, dbw trie.DatabaseWriter) error { func (self *stateObject) CommitTrie(db Database) error {
self.updateTrie(db) self.updateTrie(db)
if self.dbErr != nil { if self.dbErr != nil {
return self.dbErr return self.dbErr
} }
root, err := self.trie.CommitTo(dbw) root, err := self.trie.Commit(nil)
if err == nil { if err == nil {
self.data.Root = root self.data.Root = root
} }

View file

@ -48,7 +48,7 @@ func (s *StateSuite) TestDump(c *checker.C) {
// write some of them to the trie // write some of them to the trie
s.state.updateStateObject(obj1) s.state.updateStateObject(obj1)
s.state.updateStateObject(obj2) s.state.updateStateObject(obj2)
s.state.CommitTo(s.db, false) s.state.Commit(false)
// check that dump contains the state objects that are in trie // check that dump contains the state objects that are in trie
got := string(s.state.Dump()) got := string(s.state.Dump())
@ -97,7 +97,7 @@ func (s *StateSuite) TestNull(c *checker.C) {
//value := common.FromHex("0x823140710bf13990e4500136726d8b55") //value := common.FromHex("0x823140710bf13990e4500136726d8b55")
var value common.Hash var value common.Hash
s.state.SetState(address, common.Hash{}, value) s.state.SetState(address, common.Hash{}, value)
s.state.CommitTo(s.db, false) s.state.Commit(false)
value = s.state.GetState(address, common.Hash{}) value = s.state.GetState(address, common.Hash{})
if !common.EmptyHash(value) { if !common.EmptyHash(value) {
c.Errorf("expected empty hash. got %x", value) c.Errorf("expected empty hash. got %x", value)
@ -155,7 +155,7 @@ func TestSnapshot2(t *testing.T) {
so0.deleted = false so0.deleted = false
state.setStateObject(so0) state.setStateObject(so0)
root, _ := state.CommitTo(db, false) root, _ := state.Commit(false)
state.Reset(root) state.Reset(root)
// and one with deleted == true // and one with deleted == true

View file

@ -36,6 +36,14 @@ type revision struct {
journalIndex int journalIndex int
} }
var (
// emptyState is the known hash of an empty state trie entry.
emptyState = crypto.Keccak256Hash(nil)
// emptyCode is the known hash of the empty EVM bytecode.
emptyCode = crypto.Keccak256Hash(nil)
)
// StateDBs within the ethereum protocol are used to store anything // StateDBs within the ethereum protocol are used to store anything
// within the merkle trie. StateDBs take care of caching and storing // within the merkle trie. StateDBs take care of caching and storing
// nested states. It's the general query interface to retrieve: // nested states. It's the general query interface to retrieve:
@ -235,6 +243,11 @@ func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
return common.Hash{} return common.Hash{}
} }
// Database retrieves the low level database supporting the lower level trie ops.
func (self *StateDB) Database() Database {
return self.db
}
// StorageTrie returns the storage trie of an account. // StorageTrie returns the storage trie of an account.
// The return value is a copy and is nil for non-existent accounts. // The return value is a copy and is nil for non-existent accounts.
func (self *StateDB) StorageTrie(a common.Address) Trie { func (self *StateDB) StorageTrie(a common.Address) Trie {
@ -568,8 +581,8 @@ func (s *StateDB) clearJournalAndRefund() {
s.refund = 0 s.refund = 0
} }
// CommitTo writes the state to the given database. // Commit writes the state to the underlying in-memory trie database.
func (s *StateDB) CommitTo(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (root common.Hash, err error) { func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) {
defer s.clearJournalAndRefund() defer s.clearJournalAndRefund()
// Commit objects to the trie. // Commit objects to the trie.
@ -583,13 +596,11 @@ func (s *StateDB) CommitTo(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (ro
case isDirty: case isDirty:
// Write any contract code associated with the state object // Write any contract code associated with the state object
if stateObject.code != nil && stateObject.dirtyCode { if stateObject.code != nil && stateObject.dirtyCode {
if err := dbw.Put(stateObject.CodeHash(), stateObject.code); err != nil { s.db.TrieDB().Insert(common.BytesToHash(stateObject.CodeHash()), stateObject.code)
return common.Hash{}, err
}
stateObject.dirtyCode = false stateObject.dirtyCode = false
} }
// Write any storage changes in the state object to its storage trie. // Write any storage changes in the state object to its storage trie.
if err := stateObject.CommitTrie(s.db, dbw); err != nil { if err := stateObject.CommitTrie(s.db); err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
// Update the object in the main account trie. // Update the object in the main account trie.
@ -598,7 +609,20 @@ func (s *StateDB) CommitTo(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (ro
delete(s.stateObjectsDirty, addr) delete(s.stateObjectsDirty, addr)
} }
// Write trie changes. // Write trie changes.
root, err = s.trie.CommitTo(dbw) root, err = s.trie.Commit(func(leaf []byte, parent common.Hash) error {
var account Account
if err := rlp.DecodeBytes(leaf, &account); err != nil {
return nil
}
if account.Root != emptyState {
s.db.TrieDB().Reference(account.Root, parent)
}
code := common.BytesToHash(account.CodeHash)
if code != emptyCode {
s.db.TrieDB().Reference(code, parent)
}
return nil
})
log.Debug("Trie cache stats after commit", "misses", trie.CacheMisses(), "unloads", trie.CacheUnloads()) log.Debug("Trie cache stats after commit", "misses", trie.CacheMisses(), "unloads", trie.CacheUnloads())
return root, err return root, err
} }

View file

@ -97,10 +97,10 @@ func TestIntermediateLeaks(t *testing.T) {
} }
// Commit and cross check the databases. // Commit and cross check the databases.
if _, err := transState.CommitTo(transDb, false); err != nil { if _, err := transState.Commit(false); err != nil {
t.Fatalf("failed to commit transition state: %v", err) t.Fatalf("failed to commit transition state: %v", err)
} }
if _, err := finalState.CommitTo(finalDb, false); err != nil { if _, err := finalState.Commit(false); err != nil {
t.Fatalf("failed to commit final state: %v", err) t.Fatalf("failed to commit final state: %v", err)
} }
for _, key := range finalDb.Keys() { for _, key := range finalDb.Keys() {
@ -122,8 +122,8 @@ func TestIntermediateLeaks(t *testing.T) {
// https://github.com/ethereum/go-ethereum/pull/15549. // https://github.com/ethereum/go-ethereum/pull/15549.
func TestCopy(t *testing.T) { func TestCopy(t *testing.T) {
// Create a random state test to copy and modify "independently" // Create a random state test to copy and modify "independently"
mem, _ := ethdb.NewMemDatabase() db, _ := ethdb.NewMemDatabase()
orig, _ := New(common.Hash{}, NewDatabase(mem)) orig, _ := New(common.Hash{}, NewDatabase(db))
for i := byte(0); i < 255; i++ { for i := byte(0); i < 255; i++ {
obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i})) obj := orig.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
@ -346,11 +346,10 @@ func (test *snapshotTest) run() bool {
} }
action.fn(action, state) action.fn(action, state)
} }
// Revert all snapshots in reverse order. Each revert must yield a state // Revert all snapshots in reverse order. Each revert must yield a state
// that is equivalent to fresh state with all actions up the snapshot applied. // that is equivalent to fresh state with all actions up the snapshot applied.
for sindex--; sindex >= 0; sindex-- { for sindex--; sindex >= 0; sindex-- {
checkstate, _ := New(common.Hash{}, NewDatabase(db)) checkstate, _ := New(common.Hash{}, state.Database())
for _, action := range test.actions[:test.snapshots[sindex]] { for _, action := range test.actions[:test.snapshots[sindex]] {
action.fn(action, checkstate) action.fn(action, checkstate)
} }
@ -409,7 +408,7 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
func (s *StateSuite) TestTouchDelete(c *check.C) { func (s *StateSuite) TestTouchDelete(c *check.C) {
s.state.GetOrNewStateObject(common.Address{}) s.state.GetOrNewStateObject(common.Address{})
root, _ := s.state.CommitTo(s.db, false) root, _ := s.state.Commit(false)
s.state.Reset(root) s.state.Reset(root)
snapshot := s.state.Snapshot() snapshot := s.state.Snapshot()
@ -417,7 +416,6 @@ func (s *StateSuite) TestTouchDelete(c *check.C) {
if len(s.state.stateObjectsDirty) != 1 { if len(s.state.stateObjectsDirty) != 1 {
c.Fatal("expected one dirty state object") c.Fatal("expected one dirty state object")
} }
s.state.RevertToSnapshot(snapshot) s.state.RevertToSnapshot(snapshot)
if len(s.state.stateObjectsDirty) != 0 { if len(s.state.stateObjectsDirty) != 0 {
c.Fatal("expected no dirty state object") c.Fatal("expected no dirty state object")

View file

@ -36,10 +36,10 @@ type testAccount struct {
} }
// makeTestState create a sample test state to test node-wise reconstruction. // makeTestState create a sample test state to test node-wise reconstruction.
func makeTestState() (Database, *ethdb.MemDatabase, common.Hash, []*testAccount) { func makeTestState() (Database, common.Hash, []*testAccount) {
// Create an empty state // Create an empty state
mem, _ := ethdb.NewMemDatabase() diskdb, _ := ethdb.NewMemDatabase()
db := NewDatabase(mem) db := NewDatabase(diskdb)
state, _ := New(common.Hash{}, db) state, _ := New(common.Hash{}, db)
// Fill it with some arbitrary data // Fill it with some arbitrary data
@ -61,10 +61,10 @@ func makeTestState() (Database, *ethdb.MemDatabase, common.Hash, []*testAccount)
state.updateStateObject(obj) state.updateStateObject(obj)
accounts = append(accounts, acc) accounts = append(accounts, acc)
} }
root, _ := state.CommitTo(mem, false) root, _ := state.Commit(false)
// Return the generated state // Return the generated state
return db, mem, root, accounts return db, root, accounts
} }
// checkStateAccounts cross references a reconstructed state with an expected // checkStateAccounts cross references a reconstructed state with an expected
@ -96,7 +96,7 @@ func checkTrieConsistency(db ethdb.Database, root common.Hash) error {
if v, _ := db.Get(root[:]); v == nil { if v, _ := db.Get(root[:]); v == nil {
return nil // Consider a non existent state consistent. return nil // Consider a non existent state consistent.
} }
trie, err := trie.New(root, db) trie, err := trie.New(root, trie.NewDatabase(db))
if err != nil { if err != nil {
return err return err
} }
@ -138,7 +138,7 @@ func TestIterativeStateSyncBatched(t *testing.T) { testIterativeStateSync(t,
func testIterativeStateSync(t *testing.T, batch int) { func testIterativeStateSync(t *testing.T, batch int) {
// Create a random state to copy // Create a random state to copy
_, srcMem, srcRoot, srcAccounts := makeTestState() srcDb, srcRoot, srcAccounts := makeTestState()
// Create a destination state and sync with the scheduler // Create a destination state and sync with the scheduler
dstDb, _ := ethdb.NewMemDatabase() dstDb, _ := ethdb.NewMemDatabase()
@ -148,9 +148,9 @@ func testIterativeStateSync(t *testing.T, batch int) {
for len(queue) > 0 { for len(queue) > 0 {
results := make([]trie.SyncResult, len(queue)) results := make([]trie.SyncResult, len(queue))
for i, hash := range queue { for i, hash := range queue {
data, err := srcMem.Get(hash.Bytes()) data, err := srcDb.TrieDB().Node(hash)
if err != nil { if err != nil {
t.Fatalf("failed to retrieve node data for %x: %v", hash, err) t.Fatalf("failed to retrieve node data for %x", hash)
} }
results[i] = trie.SyncResult{Hash: hash, Data: data} results[i] = trie.SyncResult{Hash: hash, Data: data}
} }
@ -170,7 +170,7 @@ func testIterativeStateSync(t *testing.T, batch int) {
// partial results are returned, and the others sent only later. // partial results are returned, and the others sent only later.
func TestIterativeDelayedStateSync(t *testing.T) { func TestIterativeDelayedStateSync(t *testing.T) {
// Create a random state to copy // Create a random state to copy
_, srcMem, srcRoot, srcAccounts := makeTestState() srcDb, srcRoot, srcAccounts := makeTestState()
// Create a destination state and sync with the scheduler // Create a destination state and sync with the scheduler
dstDb, _ := ethdb.NewMemDatabase() dstDb, _ := ethdb.NewMemDatabase()
@ -181,9 +181,9 @@ func TestIterativeDelayedStateSync(t *testing.T) {
// Sync only half of the scheduled nodes // Sync only half of the scheduled nodes
results := make([]trie.SyncResult, len(queue)/2+1) results := make([]trie.SyncResult, len(queue)/2+1)
for i, hash := range queue[:len(results)] { for i, hash := range queue[:len(results)] {
data, err := srcMem.Get(hash.Bytes()) data, err := srcDb.TrieDB().Node(hash)
if err != nil { if err != nil {
t.Fatalf("failed to retrieve node data for %x: %v", hash, err) t.Fatalf("failed to retrieve node data for %x", hash)
} }
results[i] = trie.SyncResult{Hash: hash, Data: data} results[i] = trie.SyncResult{Hash: hash, Data: data}
} }
@ -207,7 +207,7 @@ func TestIterativeRandomStateSyncBatched(t *testing.T) { testIterativeRandomS
func testIterativeRandomStateSync(t *testing.T, batch int) { func testIterativeRandomStateSync(t *testing.T, batch int) {
// Create a random state to copy // Create a random state to copy
_, srcMem, srcRoot, srcAccounts := makeTestState() srcDb, srcRoot, srcAccounts := makeTestState()
// Create a destination state and sync with the scheduler // Create a destination state and sync with the scheduler
dstDb, _ := ethdb.NewMemDatabase() dstDb, _ := ethdb.NewMemDatabase()
@ -221,9 +221,9 @@ func testIterativeRandomStateSync(t *testing.T, batch int) {
// Fetch all the queued nodes in a random order // Fetch all the queued nodes in a random order
results := make([]trie.SyncResult, 0, len(queue)) results := make([]trie.SyncResult, 0, len(queue))
for hash := range queue { for hash := range queue {
data, err := srcMem.Get(hash.Bytes()) data, err := srcDb.TrieDB().Node(hash)
if err != nil { if err != nil {
t.Fatalf("failed to retrieve node data for %x: %v", hash, err) t.Fatalf("failed to retrieve node data for %x", hash)
} }
results = append(results, trie.SyncResult{Hash: hash, Data: data}) results = append(results, trie.SyncResult{Hash: hash, Data: data})
} }
@ -247,7 +247,7 @@ func testIterativeRandomStateSync(t *testing.T, batch int) {
// partial results are returned (Even those randomly), others sent only later. // partial results are returned (Even those randomly), others sent only later.
func TestIterativeRandomDelayedStateSync(t *testing.T) { func TestIterativeRandomDelayedStateSync(t *testing.T) {
// Create a random state to copy // Create a random state to copy
_, srcMem, srcRoot, srcAccounts := makeTestState() srcDb, srcRoot, srcAccounts := makeTestState()
// Create a destination state and sync with the scheduler // Create a destination state and sync with the scheduler
dstDb, _ := ethdb.NewMemDatabase() dstDb, _ := ethdb.NewMemDatabase()
@ -263,9 +263,9 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
for hash := range queue { for hash := range queue {
delete(queue, hash) delete(queue, hash)
data, err := srcMem.Get(hash.Bytes()) data, err := srcDb.TrieDB().Node(hash)
if err != nil { if err != nil {
t.Fatalf("failed to retrieve node data for %x: %v", hash, err) t.Fatalf("failed to retrieve node data for %x", hash)
} }
results = append(results, trie.SyncResult{Hash: hash, Data: data}) results = append(results, trie.SyncResult{Hash: hash, Data: data})
@ -292,9 +292,9 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
// the database. // the database.
func TestIncompleteStateSync(t *testing.T) { func TestIncompleteStateSync(t *testing.T) {
// Create a random state to copy // Create a random state to copy
_, srcMem, srcRoot, srcAccounts := makeTestState() srcDb, srcRoot, srcAccounts := makeTestState()
checkTrieConsistency(srcMem, srcRoot) checkTrieConsistency(srcDb.TrieDB().DiskDB().(ethdb.Database), srcRoot)
// Create a destination state and sync with the scheduler // Create a destination state and sync with the scheduler
dstDb, _ := ethdb.NewMemDatabase() dstDb, _ := ethdb.NewMemDatabase()
@ -306,9 +306,9 @@ func TestIncompleteStateSync(t *testing.T) {
// Fetch a batch of state nodes // Fetch a batch of state nodes
results := make([]trie.SyncResult, len(queue)) results := make([]trie.SyncResult, len(queue))
for i, hash := range queue { for i, hash := range queue {
data, err := srcMem.Get(hash.Bytes()) data, err := srcDb.TrieDB().Node(hash)
if err != nil { if err != nil {
t.Fatalf("failed to retrieve node data for %x: %v", hash, err) t.Fatalf("failed to retrieve node data for %x", hash)
} }
results[i] = trie.SyncResult{Hash: hash, Data: data} results[i] = trie.SyncResult{Hash: hash, Data: data}
} }

View file

@ -215,6 +215,9 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
// Pay intrinsic gas // Pay intrinsic gas
gas, err := IntrinsicGas(st.data, contractCreation, homestead) gas, err := IntrinsicGas(st.data, contractCreation, homestead)
if err != nil {
return nil, 0, false, err
}
if err = st.useGas(gas); err != nil { if err = st.useGas(gas); err != nil {
return nil, 0, false, err return nil, 0, false, err
} }

View file

@ -78,8 +78,8 @@ func pricedTransaction(nonce uint64, gaslimit uint64, gasprice *big.Int, key *ec
} }
func setupTxPool() (*TxPool, *ecdsa.PrivateKey) { func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
db, _ := ethdb.NewMemDatabase() diskdb, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db)) statedb, _ := state.New(common.Hash{}, state.NewDatabase(diskdb))
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)} blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
key, _ := crypto.GenerateKey() key, _ := crypto.GenerateKey()

View file

@ -25,6 +25,7 @@ import (
"sort" "sort"
"sync/atomic" "sync/atomic"
"time" "time"
"unsafe"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
@ -121,6 +122,12 @@ func (h *Header) HashNoNonce() common.Hash {
}) })
} }
// Size returns the approximate memory used by all internal contents. It is used
// to approximate and limit the memory consumption of various caches.
func (h *Header) Size() common.StorageSize {
return common.StorageSize(unsafe.Sizeof(*h)) + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen()+h.Time.BitLen())/8)
}
func rlpHash(x interface{}) (h common.Hash) { func rlpHash(x interface{}) (h common.Hash) {
hw := sha3.NewKeccak256() hw := sha3.NewKeccak256()
rlp.Encode(hw, x) rlp.Encode(hw, x)
@ -322,6 +329,8 @@ func (b *Block) HashNoNonce() common.Hash {
return b.header.HashNoNonce() return b.header.HashNoNonce()
} }
// Size returns the true RLP encoded storage size of the block, either by encoding
// and returning it, or returning a previsouly cached value.
func (b *Block) Size() common.StorageSize { func (b *Block) Size() common.StorageSize {
if size := b.size.Load(); size != nil { if size := b.size.Load(); size != nil {
return size.(common.StorageSize) return size.(common.StorageSize)

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