mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 17:33:47 +00:00
Update Ethereum
This commit is contained in:
commit
693f3e5b43
175 changed files with 3715 additions and 2277 deletions
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
|
|
@ -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/EthereumCommonwealth/go-callisto/wiki/bitchin-tricks) wiki page for more info
|
in the right hands. Please check our [Wiki page](https://github.com/EthereumCommonwealth/go-callisto/wiki) for more info
|
||||||
and help.
|
and help.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
|
||||||
14
.mailmap
14
.mailmap
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,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
92
AUTHORS
|
|
@ -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>
|
||||||
|
|
|
||||||
31
README.md
31
README.md
|
|
@ -53,16 +53,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/EthereumCommonwealth/go-callisto/wiki/JavaScript-Console),
|
* Start up Geth's built-in interactive [JavaScript console](https://github.com/EthereumCommonwealth/go-callisto/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/EthereumCommonwealth/go-callisto/wiki/Management-APIs).
|
as well as Geth's own [management APIs](https://github.com/EthereumCommonwealth/go-callisto/wiki/Management-APIs).
|
||||||
|
|
@ -77,12 +75,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:
|
||||||
|
|
||||||
|
|
@ -93,12 +90,20 @@ Specifying the `--testnet` flag however will reconfigure your Geth instance a bi
|
||||||
`geth attach <datadir>/testnet/geth.ipc`. Windows users are not affected by this.
|
`geth attach <datadir>/testnet/geth.ipc`. Windows users are not affected by this.
|
||||||
* Instead of connecting the main Ethereum network, the client will connect to the test network,
|
* Instead of connecting the main Ethereum network, the client will connect to the test network,
|
||||||
which uses different P2P bootnodes, different network IDs and genesis states.
|
which uses different P2P bootnodes, different network IDs and genesis states.
|
||||||
|
|
||||||
*Note: Although there are some internal protective measures to prevent transactions from crossing
|
*Note: Although there are some internal protective measures to prevent transactions from crossing
|
||||||
over between the main network and test network, you should make sure to always use separate accounts
|
over between the main network and test network, you should make sure to always use separate accounts
|
||||||
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:
|
||||||
|
|
@ -122,10 +127,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.
|
||||||
|
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
1.8.0
|
1.8.1
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ 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{
|
backend := &SimulatedBackend{
|
||||||
database: database,
|
database: database,
|
||||||
|
|
@ -102,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.
|
||||||
|
|
@ -309,8 +311,10 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -386,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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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"))
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,12 @@ func main() {
|
||||||
utils.Fatalf("%v", err)
|
utils.Fatalf("%v", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if _, err := discover.ListenUDP(nodeKey, conn, realaddr, nil, "", 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 (
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2017 The go-ethereum Authors
|
// Copyright 2018 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
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2017 The go-ethereum Authors
|
// Copyright 2018 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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,11 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/EthereumCommonwealth/go-callisto/cmd/utils"
|
"github.com/EthereumCommonwealth/go-callisto/cmd/utils"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/internal/debug"
|
"github.com/EthereumCommonwealth/go-callisto/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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
@ -676,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
|
||||||
|
|
@ -714,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
|
||||||
}
|
}
|
||||||
|
|
@ -789,20 +812,40 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
|
||||||
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 {
|
||||||
|
|
@ -999,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)
|
||||||
}
|
}
|
||||||
|
|
@ -1135,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"
|
||||||
|
|
@ -1187,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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ import (
|
||||||
"github.com/EthereumCommonwealth/go-callisto/p2p/discover"
|
"github.com/EthereumCommonwealth/go-callisto/p2p/discover"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/p2p/nat"
|
"github.com/EthereumCommonwealth/go-callisto/p2p/nat"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/whisper/mailserver"
|
"github.com/EthereumCommonwealth/go-callisto/whisper/mailserver"
|
||||||
whisper "github.com/EthereumCommonwealth/go-callisto/whisper/whisperv5"
|
whisper "github.com/EthereumCommonwealth/go-callisto/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)
|
||||||
|
|
@ -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(¶ms)
|
msg, err := whisper.NewSentMessage(¶ms)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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/EthereumCommonwealth/go-callisto && \
|
git clone --depth 1 --branch release/1.8 https://github.com/EthereumCommonwealth/go-callisto && \
|
||||||
(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 && \
|
||||||
|
|
|
||||||
|
|
@ -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/EthereumCommonwealth/go-callisto && \
|
git clone --depth 1 --branch release/1.8 https://github.com/EthereumCommonwealth/go-callisto && \
|
||||||
(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 && \
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ import (
|
||||||
"github.com/EthereumCommonwealth/go-callisto/rlp"
|
"github.com/EthereumCommonwealth/go-callisto/rlp"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/trie"
|
"github.com/EthereumCommonwealth/go-callisto/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(¤tBlock); 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,13 +479,13 @@ 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
|
||||||
|
|
@ -469,7 +494,7 @@ func (bc *BlockChain) insert(block *types.Block) {
|
||||||
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
|
||||||
|
|
@ -1146,7 +1321,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
||||||
// insert the block in the canonical way, re-writing history
|
// insert the block in the canonical way, re-writing history
|
||||||
bc.insert(newChain[i])
|
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, newChain[i]); err != nil {
|
if err := WriteTxLookupEntries(bc.db, newChain[i]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
addedTxs = append(addedTxs, newChain[i].Transactions()...)
|
addedTxs = append(addedTxs, newChain[i].Transactions()...)
|
||||||
|
|
@ -1156,7 +1331,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
||||||
// 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})
|
||||||
|
|
@ -1248,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
|
||||||
|
|
@ -1355,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 }
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
@ -1226,7 +1226,7 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
||||||
diskdb, _ := ethdb.NewMemDatabase()
|
diskdb, _ := ethdb.NewMemDatabase()
|
||||||
new(Genesis).MustCommit(diskdb)
|
new(Genesis).MustCommit(diskdb)
|
||||||
|
|
||||||
chain, err := NewBlockChain(diskdb, params.TestChainConfig, engine, vm.Config{})
|
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create tester chain: %v", err)
|
t.Fatalf("failed to create tester chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -1245,3 +1245,102 @@ func TestBlockchainHeaderchainReorgConsistency(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -222,9 +221,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)
|
||||||
|
|
@ -254,19 +256,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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,13 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/EthereumCommonwealth/go-callisto/common"
|
"github.com/EthereumCommonwealth/go-callisto/common"
|
||||||
|
"github.com/EthereumCommonwealth/go-callisto/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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,6 @@ import (
|
||||||
"github.com/EthereumCommonwealth/go-callisto/common"
|
"github.com/EthereumCommonwealth/go-callisto/common"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/crypto"
|
"github.com/EthereumCommonwealth/go-callisto/crypto"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/rlp"
|
"github.com/EthereumCommonwealth/go-callisto/rlp"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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/EthereumCommonwealth/go-callisto/pull/15549.
|
// https://github.com/EthereumCommonwealth/go-callisto/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")
|
||||||
|
|
|
||||||
|
|
@ -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}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/EthereumCommonwealth/go-callisto/common"
|
"github.com/EthereumCommonwealth/go-callisto/common"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/common/hexutil"
|
"github.com/EthereumCommonwealth/go-callisto/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)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/EthereumCommonwealth/go-callisto/common"
|
"github.com/EthereumCommonwealth/go-callisto/common"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/common/hexutil"
|
"github.com/EthereumCommonwealth/go-callisto/common/hexutil"
|
||||||
|
|
@ -136,6 +137,18 @@ func (r *Receipt) statusEncoding() []byte {
|
||||||
return r.PostState
|
return r.PostState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size returns the approximate memory used by all internal contents. It is used
|
||||||
|
// to approximate and limit the memory consumption of various caches.
|
||||||
|
func (r *Receipt) Size() common.StorageSize {
|
||||||
|
size := common.StorageSize(unsafe.Sizeof(*r)) + common.StorageSize(len(r.PostState))
|
||||||
|
|
||||||
|
size += common.StorageSize(len(r.Logs)) * common.StorageSize(unsafe.Sizeof(Log{}))
|
||||||
|
for _, log := range r.Logs {
|
||||||
|
size += common.StorageSize(len(log.Topics)*common.HashLength + len(log.Data))
|
||||||
|
}
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
// String implements the Stringer interface.
|
// String implements the Stringer interface.
|
||||||
func (r *Receipt) String() string {
|
func (r *Receipt) String() string {
|
||||||
if len(r.PostState) == 0 {
|
if len(r.PostState) == 0 {
|
||||||
|
|
|
||||||
|
|
@ -206,6 +206,8 @@ func (tx *Transaction) Hash() common.Hash {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Size returns the true RLP encoded storage size of the transaction, either by
|
||||||
|
// encoding and returning it, or returning a previsouly cached value.
|
||||||
func (tx *Transaction) Size() common.StorageSize {
|
func (tx *Transaction) Size() common.StorageSize {
|
||||||
if size := tx.size.Load(); size != nil {
|
if size := tx.size.Load(); size != nil {
|
||||||
return size.(common.StorageSize)
|
return size.(common.StorageSize)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,19 @@
|
||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,19 @@
|
||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package vm
|
package vm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/EthereumCommonwealth/go-callisto/common"
|
|
||||||
"github.com/EthereumCommonwealth/go-callisto/common/math"
|
"github.com/EthereumCommonwealth/go-callisto/common/math"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/crypto"
|
|
||||||
"github.com/EthereumCommonwealth/go-callisto/params"
|
"github.com/EthereumCommonwealth/go-callisto/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -123,11 +121,6 @@ func (in *Interpreter) Run(contract *Contract, input []byte) (ret []byte, err er
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
codehash := contract.CodeHash // codehash is used when doing jump dest caching
|
|
||||||
if codehash == (common.Hash{}) {
|
|
||||||
codehash = crypto.Keccak256Hash(contract.Code)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
op OpCode // current opcode
|
op OpCode // current opcode
|
||||||
mem = NewMemory() // bound memory
|
mem = NewMemory() // bound memory
|
||||||
|
|
|
||||||
|
|
@ -462,11 +462,11 @@ func (api *PrivateDebugAPI) getModifiedAccounts(startBlock, endBlock *types.Bloc
|
||||||
return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startBlock.Number().Uint64(), endBlock.Number().Uint64())
|
return nil, fmt.Errorf("start block height (%d) must be less than end block height (%d)", startBlock.Number().Uint64(), endBlock.Number().Uint64())
|
||||||
}
|
}
|
||||||
|
|
||||||
oldTrie, err := trie.NewSecure(startBlock.Root(), api.eth.chainDb, 0)
|
oldTrie, err := trie.NewSecure(startBlock.Root(), trie.NewDatabase(api.eth.chainDb), 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
newTrie, err := trie.NewSecure(endBlock.Root(), api.eth.chainDb, 0)
|
newTrie, err := trie.NewSecure(endBlock.Root(), trie.NewDatabase(api.eth.chainDb), 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2016 The go-ethereum Authors
|
// Copyright 2017 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/EthereumCommonwealth/go-callisto/common"
|
"github.com/EthereumCommonwealth/go-callisto/common"
|
||||||
|
|
@ -34,7 +33,6 @@ import (
|
||||||
"github.com/EthereumCommonwealth/go-callisto/core/types"
|
"github.com/EthereumCommonwealth/go-callisto/core/types"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/core/vm"
|
"github.com/EthereumCommonwealth/go-callisto/core/vm"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/eth/tracers"
|
"github.com/EthereumCommonwealth/go-callisto/eth/tracers"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/ethdb"
|
|
||||||
"github.com/EthereumCommonwealth/go-callisto/internal/ethapi"
|
"github.com/EthereumCommonwealth/go-callisto/internal/ethapi"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/log"
|
"github.com/EthereumCommonwealth/go-callisto/log"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/rlp"
|
"github.com/EthereumCommonwealth/go-callisto/rlp"
|
||||||
|
|
@ -72,6 +70,7 @@ type txTraceResult struct {
|
||||||
type blockTraceTask struct {
|
type blockTraceTask struct {
|
||||||
statedb *state.StateDB // Intermediate state prepped for tracing
|
statedb *state.StateDB // Intermediate state prepped for tracing
|
||||||
block *types.Block // Block to trace the transactions from
|
block *types.Block // Block to trace the transactions from
|
||||||
|
rootref common.Hash // Trie root reference held for this task
|
||||||
results []*txTraceResult // Trace results procudes by the task
|
results []*txTraceResult // Trace results procudes by the task
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,59 +89,6 @@ type txTraceTask struct {
|
||||||
index int // Transaction offset in the block
|
index int // Transaction offset in the block
|
||||||
}
|
}
|
||||||
|
|
||||||
// ephemeralDatabase is a memory wrapper around a proper database, which acts as
|
|
||||||
// an ephemeral write layer. This construct is used by the chain tracer to write
|
|
||||||
// state tries for intermediate blocks without serializing to disk, but at the
|
|
||||||
// same time to allow disk fallback for reads that do no hit the memory layer.
|
|
||||||
type ephemeralDatabase struct {
|
|
||||||
diskdb ethdb.Database // Persistent disk database to fall back to with reads
|
|
||||||
memdb *ethdb.MemDatabase // Ephemeral memory database for primary reads and writes
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *ephemeralDatabase) Put(key []byte, value []byte) error { return db.memdb.Put(key, value) }
|
|
||||||
func (db *ephemeralDatabase) Delete(key []byte) error { return errors.New("delete not supported") }
|
|
||||||
func (db *ephemeralDatabase) Close() { db.memdb.Close() }
|
|
||||||
func (db *ephemeralDatabase) NewBatch() ethdb.Batch {
|
|
||||||
return db.memdb.NewBatch()
|
|
||||||
}
|
|
||||||
func (db *ephemeralDatabase) Has(key []byte) (bool, error) {
|
|
||||||
if has, _ := db.memdb.Has(key); has {
|
|
||||||
return has, nil
|
|
||||||
}
|
|
||||||
return db.diskdb.Has(key)
|
|
||||||
}
|
|
||||||
func (db *ephemeralDatabase) Get(key []byte) ([]byte, error) {
|
|
||||||
if blob, _ := db.memdb.Get(key); blob != nil {
|
|
||||||
return blob, nil
|
|
||||||
}
|
|
||||||
return db.diskdb.Get(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prune does a state sync into a new memory write layer and replaces the old one.
|
|
||||||
// This allows us to discard entries that are no longer referenced from the current
|
|
||||||
// state.
|
|
||||||
func (db *ephemeralDatabase) Prune(root common.Hash) {
|
|
||||||
// Pull the still relevant state data into memory
|
|
||||||
sync := state.NewStateSync(root, db.diskdb)
|
|
||||||
for sync.Pending() > 0 {
|
|
||||||
hash := sync.Missing(1)[0]
|
|
||||||
|
|
||||||
// Move the next trie node from the memory layer into a sync struct
|
|
||||||
node, err := db.memdb.Get(hash[:])
|
|
||||||
if err != nil {
|
|
||||||
panic(err) // memdb must have the data
|
|
||||||
}
|
|
||||||
if _, _, err := sync.Process([]trie.SyncResult{{Hash: hash, Data: node}}); err != nil {
|
|
||||||
panic(err) // it's not possible to fail processing a node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Discard the old memory layer and write a new one
|
|
||||||
db.memdb, _ = ethdb.NewMemDatabaseWithCap(db.memdb.Len())
|
|
||||||
if _, err := sync.Commit(db); err != nil {
|
|
||||||
panic(err) // writing into a memdb cannot fail
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TraceChain returns the structured logs created during the execution of EVM
|
// TraceChain returns the structured logs created during the execution of EVM
|
||||||
// between two blocks (excluding start) and returns them as a JSON object.
|
// between two blocks (excluding start) and returns them as a JSON object.
|
||||||
func (api *PrivateDebugAPI) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) {
|
func (api *PrivateDebugAPI) TraceChain(ctx context.Context, start, end rpc.BlockNumber, config *TraceConfig) (*rpc.Subscription, error) {
|
||||||
|
|
@ -188,19 +134,15 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
||||||
|
|
||||||
// Ensure we have a valid starting state before doing any work
|
// Ensure we have a valid starting state before doing any work
|
||||||
origin := start.NumberU64()
|
origin := start.NumberU64()
|
||||||
|
database := state.NewDatabase(api.eth.ChainDb())
|
||||||
|
|
||||||
memdb, _ := ethdb.NewMemDatabase()
|
|
||||||
db := &ephemeralDatabase{
|
|
||||||
diskdb: api.eth.ChainDb(),
|
|
||||||
memdb: memdb,
|
|
||||||
}
|
|
||||||
if number := start.NumberU64(); number > 0 {
|
if number := start.NumberU64(); number > 0 {
|
||||||
start = api.eth.blockchain.GetBlock(start.ParentHash(), start.NumberU64()-1)
|
start = api.eth.blockchain.GetBlock(start.ParentHash(), start.NumberU64()-1)
|
||||||
if start == nil {
|
if start == nil {
|
||||||
return nil, fmt.Errorf("parent block #%d not found", number-1)
|
return nil, fmt.Errorf("parent block #%d not found", number-1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
statedb, err := state.New(start.Root(), state.NewDatabase(db))
|
statedb, err := state.New(start.Root(), database)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If the starting state is missing, allow some number of blocks to be reexecuted
|
// If the starting state is missing, allow some number of blocks to be reexecuted
|
||||||
reexec := defaultTraceReexec
|
reexec := defaultTraceReexec
|
||||||
|
|
@ -213,7 +155,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
||||||
if start == nil {
|
if start == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if statedb, err = state.New(start.Root(), state.NewDatabase(db)); err == nil {
|
if statedb, err = state.New(start.Root(), database); err == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -256,7 +198,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
||||||
res, err := api.traceTx(ctx, msg, vmctx, task.statedb, config)
|
res, err := api.traceTx(ctx, msg, vmctx, task.statedb, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
task.results[i] = &txTraceResult{Error: err.Error()}
|
task.results[i] = &txTraceResult{Error: err.Error()}
|
||||||
log.Warn("Tracing failed", "err", err)
|
log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
task.statedb.DeleteSuicides()
|
task.statedb.DeleteSuicides()
|
||||||
|
|
@ -273,7 +215,6 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
||||||
}
|
}
|
||||||
// Start a goroutine to feed all the blocks into the tracers
|
// Start a goroutine to feed all the blocks into the tracers
|
||||||
begin := time.Now()
|
begin := time.Now()
|
||||||
complete := start.NumberU64()
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
var (
|
var (
|
||||||
|
|
@ -281,6 +222,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
||||||
number uint64
|
number uint64
|
||||||
traced uint64
|
traced uint64
|
||||||
failed error
|
failed error
|
||||||
|
proot common.Hash
|
||||||
)
|
)
|
||||||
// Ensure everything is properly cleaned up on any exit path
|
// Ensure everything is properly cleaned up on any exit path
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -308,7 +250,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
||||||
// Print progress logs if long enough time elapsed
|
// Print progress logs if long enough time elapsed
|
||||||
if time.Since(logged) > 8*time.Second {
|
if time.Since(logged) > 8*time.Second {
|
||||||
if number > origin {
|
if number > origin {
|
||||||
log.Info("Tracing chain segment", "start", origin, "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin))
|
log.Info("Tracing chain segment", "start", origin, "end", end.NumberU64(), "current", number, "transactions", traced, "elapsed", time.Since(begin), "memory", database.TrieDB().Size())
|
||||||
} else {
|
} else {
|
||||||
log.Info("Preparing state for chain trace", "block", number, "start", origin, "elapsed", time.Since(begin))
|
log.Info("Preparing state for chain trace", "block", number, "start", origin, "elapsed", time.Since(begin))
|
||||||
}
|
}
|
||||||
|
|
@ -325,13 +267,11 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
||||||
txs := block.Transactions()
|
txs := block.Transactions()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case tasks <- &blockTraceTask{statedb: statedb.Copy(), block: block, results: make([]*txTraceResult, len(txs))}:
|
case tasks <- &blockTraceTask{statedb: statedb.Copy(), block: block, rootref: proot, results: make([]*txTraceResult, len(txs))}:
|
||||||
case <-notifier.Closed():
|
case <-notifier.Closed():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
traced += uint64(len(txs))
|
traced += uint64(len(txs))
|
||||||
} else {
|
|
||||||
atomic.StoreUint64(&complete, number)
|
|
||||||
}
|
}
|
||||||
// Generate the next state snapshot fast without tracing
|
// Generate the next state snapshot fast without tracing
|
||||||
_, _, _, err := api.eth.blockchain.Processor().Process(block, statedb, vm.Config{})
|
_, _, _, err := api.eth.blockchain.Processor().Process(block, statedb, vm.Config{})
|
||||||
|
|
@ -340,7 +280,7 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// Finalize the state so any modifications are written to the trie
|
// Finalize the state so any modifications are written to the trie
|
||||||
root, err := statedb.CommitTo(db, true)
|
root, err := statedb.Commit(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
failed = err
|
failed = err
|
||||||
break
|
break
|
||||||
|
|
@ -349,26 +289,14 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
||||||
failed = err
|
failed = err
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// After every N blocks, prune the database to only retain relevant data
|
// Reference the trie twice, once for us, once for the trancer
|
||||||
if (number-start.NumberU64())%4096 == 0 {
|
database.TrieDB().Reference(root, common.Hash{})
|
||||||
// Wait until currently pending trace jobs finish
|
if number >= origin {
|
||||||
for atomic.LoadUint64(&complete) != number {
|
database.TrieDB().Reference(root, common.Hash{})
|
||||||
select {
|
|
||||||
case <-time.After(100 * time.Millisecond):
|
|
||||||
case <-notifier.Closed():
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// No more concurrent access at this point, prune the database
|
|
||||||
var (
|
|
||||||
nodes = db.memdb.Len()
|
|
||||||
start = time.Now()
|
|
||||||
)
|
|
||||||
db.Prune(root)
|
|
||||||
log.Info("Pruned tracer state entries", "deleted", nodes-db.memdb.Len(), "left", db.memdb.Len(), "elapsed", time.Since(start))
|
|
||||||
|
|
||||||
statedb, _ = state.New(root, state.NewDatabase(db))
|
|
||||||
}
|
}
|
||||||
|
// Dereference all past tries we ourselves are done working with
|
||||||
|
database.TrieDB().Dereference(proot, common.Hash{})
|
||||||
|
proot = root
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -387,12 +315,14 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
||||||
}
|
}
|
||||||
done[uint64(result.Block)] = result
|
done[uint64(result.Block)] = result
|
||||||
|
|
||||||
|
// Dereference any paret tries held in memory by this task
|
||||||
|
database.TrieDB().Dereference(res.rootref, common.Hash{})
|
||||||
|
|
||||||
// Stream completed traces to the user, aborting on the first error
|
// Stream completed traces to the user, aborting on the first error
|
||||||
for result, ok := done[next]; ok; result, ok = done[next] {
|
for result, ok := done[next]; ok; result, ok = done[next] {
|
||||||
if len(result.Traces) > 0 || next == end.NumberU64() {
|
if len(result.Traces) > 0 || next == end.NumberU64() {
|
||||||
notifier.Notify(sub.ID, result)
|
notifier.Notify(sub.ID, result)
|
||||||
}
|
}
|
||||||
atomic.StoreUint64(&complete, next)
|
|
||||||
delete(done, next)
|
delete(done, next)
|
||||||
next++
|
next++
|
||||||
}
|
}
|
||||||
|
|
@ -544,18 +474,14 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
|
||||||
}
|
}
|
||||||
// Otherwise try to reexec blocks until we find a state or reach our limit
|
// Otherwise try to reexec blocks until we find a state or reach our limit
|
||||||
origin := block.NumberU64()
|
origin := block.NumberU64()
|
||||||
|
database := state.NewDatabase(api.eth.ChainDb())
|
||||||
|
|
||||||
memdb, _ := ethdb.NewMemDatabase()
|
|
||||||
db := &ephemeralDatabase{
|
|
||||||
diskdb: api.eth.ChainDb(),
|
|
||||||
memdb: memdb,
|
|
||||||
}
|
|
||||||
for i := uint64(0); i < reexec; i++ {
|
for i := uint64(0); i < reexec; i++ {
|
||||||
block = api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
block = api.eth.blockchain.GetBlock(block.ParentHash(), block.NumberU64()-1)
|
||||||
if block == nil {
|
if block == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if statedb, err = state.New(block.Root(), state.NewDatabase(db)); err == nil {
|
if statedb, err = state.New(block.Root(), database); err == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -571,6 +497,7 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
|
||||||
var (
|
var (
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
logged time.Time
|
logged time.Time
|
||||||
|
proot common.Hash
|
||||||
)
|
)
|
||||||
for block.NumberU64() < origin {
|
for block.NumberU64() < origin {
|
||||||
// Print progress logs if long enough time elapsed
|
// Print progress logs if long enough time elapsed
|
||||||
|
|
@ -587,26 +514,18 @@ func (api *PrivateDebugAPI) computeStateDB(block *types.Block, reexec uint64) (*
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// Finalize the state so any modifications are written to the trie
|
// Finalize the state so any modifications are written to the trie
|
||||||
root, err := statedb.CommitTo(db, true)
|
root, err := statedb.Commit(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := statedb.Reset(root); err != nil {
|
if err := statedb.Reset(root); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// After every N blocks, prune the database to only retain relevant data
|
database.TrieDB().Reference(root, common.Hash{})
|
||||||
if block.NumberU64()%4096 == 0 || block.NumberU64() == origin {
|
database.TrieDB().Dereference(proot, common.Hash{})
|
||||||
var (
|
proot = root
|
||||||
nodes = db.memdb.Len()
|
|
||||||
begin = time.Now()
|
|
||||||
)
|
|
||||||
db.Prune(root)
|
|
||||||
log.Info("Pruned tracer state entries", "deleted", nodes-db.memdb.Len(), "left", db.memdb.Len(), "elapsed", time.Since(begin))
|
|
||||||
|
|
||||||
statedb, _ = state.New(root, state.NewDatabase(db))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
log.Info("Historical state regenerated", "block", block.NumberU64(), "elapsed", time.Since(start))
|
log.Info("Historical state regenerated", "block", block.NumberU64(), "elapsed", time.Since(start), "size", database.TrieDB().Size())
|
||||||
return statedb, nil
|
return statedb, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -144,9 +144,11 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||||
}
|
}
|
||||||
core.WriteBlockChainVersion(chainDb, core.BlockChainVersion)
|
core.WriteBlockChainVersion(chainDb, core.BlockChainVersion)
|
||||||
}
|
}
|
||||||
|
var (
|
||||||
vmConfig := vm.Config{EnablePreimageRecording: config.EnablePreimageRecording}
|
vmConfig = vm.Config{EnablePreimageRecording: config.EnablePreimageRecording}
|
||||||
eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.engine, vmConfig)
|
cacheConfig = &core.CacheConfig{Disabled: config.NoPruning, TrieNodeLimit: config.TrieCache, TrieTimeLimit: config.TrieTimeout}
|
||||||
|
)
|
||||||
|
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, eth.chainConfig, eth.engine, vmConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -393,10 +395,10 @@ func (s *Ethereum) Start(srvr *p2p.Server) error {
|
||||||
// Figure out a max peers count based on the server limits
|
// Figure out a max peers count based on the server limits
|
||||||
maxPeers := srvr.MaxPeers
|
maxPeers := srvr.MaxPeers
|
||||||
if s.config.LightServ > 0 {
|
if s.config.LightServ > 0 {
|
||||||
maxPeers -= s.config.LightPeers
|
if s.config.LightPeers >= srvr.MaxPeers {
|
||||||
if maxPeers < srvr.MaxPeers/2 {
|
return fmt.Errorf("invalid peer config: light peer count (%d) >= total peer count (%d)", s.config.LightPeers, srvr.MaxPeers)
|
||||||
maxPeers = srvr.MaxPeers / 2
|
|
||||||
}
|
}
|
||||||
|
maxPeers -= s.config.LightPeers
|
||||||
}
|
}
|
||||||
// Start the networking layer and the light server if requested
|
// Start the networking layer and the light server if requested
|
||||||
s.protocolManager.Start(maxPeers)
|
s.protocolManager.Start(maxPeers)
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/EthereumCommonwealth/go-callisto/common"
|
"github.com/EthereumCommonwealth/go-callisto/common"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/common/hexutil"
|
"github.com/EthereumCommonwealth/go-callisto/common/hexutil"
|
||||||
|
|
@ -43,8 +44,10 @@ var DefaultConfig = Config{
|
||||||
DatasetsOnDisk: 2,
|
DatasetsOnDisk: 2,
|
||||||
},
|
},
|
||||||
NetworkId: 1,
|
NetworkId: 1,
|
||||||
LightPeers: 20,
|
LightPeers: 100,
|
||||||
DatabaseCache: 128,
|
DatabaseCache: 768,
|
||||||
|
TrieCache: 256,
|
||||||
|
TrieTimeout: 5 * time.Minute,
|
||||||
GasPrice: big.NewInt(1800 * params.Shannon),
|
GasPrice: big.NewInt(1800 * params.Shannon),
|
||||||
|
|
||||||
TxPool: core.DefaultTxPoolConfig,
|
TxPool: core.DefaultTxPoolConfig,
|
||||||
|
|
@ -78,6 +81,7 @@ type Config struct {
|
||||||
// Protocol options
|
// Protocol options
|
||||||
NetworkId uint64 // Network ID to use for selecting peers to connect to
|
NetworkId uint64 // Network ID to use for selecting peers to connect to
|
||||||
SyncMode downloader.SyncMode
|
SyncMode downloader.SyncMode
|
||||||
|
NoPruning bool
|
||||||
|
|
||||||
// Light client options
|
// Light client options
|
||||||
LightServ int `toml:",omitempty"` // Maximum percentage of time allowed for serving LES requests
|
LightServ int `toml:",omitempty"` // Maximum percentage of time allowed for serving LES requests
|
||||||
|
|
@ -87,6 +91,8 @@ type Config struct {
|
||||||
SkipBcVersionCheck bool `toml:"-"`
|
SkipBcVersionCheck bool `toml:"-"`
|
||||||
DatabaseHandles int `toml:"-"`
|
DatabaseHandles int `toml:"-"`
|
||||||
DatabaseCache int
|
DatabaseCache int
|
||||||
|
TrieCache int
|
||||||
|
TrieTimeout time.Duration
|
||||||
|
|
||||||
// Mining-related options
|
// Mining-related options
|
||||||
Etherbase common.Address `toml:",omitempty"`
|
Etherbase common.Address `toml:",omitempty"`
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,8 @@
|
||||||
package downloader
|
package downloader
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
@ -61,12 +59,11 @@ var (
|
||||||
maxHeadersProcess = 2048 // Number of header download results to import at once into the chain
|
maxHeadersProcess = 2048 // Number of header download results to import at once into the chain
|
||||||
maxResultsProcess = 2048 // Number of content download results to import at once into the chain
|
maxResultsProcess = 2048 // Number of content download results to import at once into the chain
|
||||||
|
|
||||||
fsHeaderCheckFrequency = 100 // Verification frequency of the downloaded headers during fast sync
|
fsHeaderCheckFrequency = 100 // Verification frequency of the downloaded headers during fast sync
|
||||||
fsHeaderSafetyNet = 2048 // Number of headers to discard in case a chain violation is detected
|
fsHeaderSafetyNet = 2048 // Number of headers to discard in case a chain violation is detected
|
||||||
fsHeaderForceVerify = 24 // Number of headers to verify before and after the pivot to accept it
|
fsHeaderForceVerify = 24 // Number of headers to verify before and after the pivot to accept it
|
||||||
fsPivotInterval = 256 // Number of headers out of which to randomize the pivot point
|
fsHeaderContCheck = 3 * time.Second // Time interval to check for header continuations during state download
|
||||||
fsMinFullBlocks = 64 // Number of blocks to retrieve fully even in fast sync
|
fsMinFullBlocks = 64 // Number of blocks to retrieve fully even in fast sync
|
||||||
fsCriticalTrials = uint32(32) // Number of times to retry in the cricical section before bailing
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -102,9 +99,6 @@ type Downloader struct {
|
||||||
peers *peerSet // Set of active peers from which download can proceed
|
peers *peerSet // Set of active peers from which download can proceed
|
||||||
stateDB ethdb.Database
|
stateDB ethdb.Database
|
||||||
|
|
||||||
fsPivotLock *types.Header // Pivot header on critical section entry (cannot change between retries)
|
|
||||||
fsPivotFails uint32 // Number of subsequent fast sync failures in the critical section
|
|
||||||
|
|
||||||
rttEstimate uint64 // Round trip time to target for download requests
|
rttEstimate uint64 // Round trip time to target for download requests
|
||||||
rttConfidence uint64 // Confidence in the estimated RTT (unit: millionths to allow atomic ops)
|
rttConfidence uint64 // Confidence in the estimated RTT (unit: millionths to allow atomic ops)
|
||||||
|
|
||||||
|
|
@ -124,6 +118,7 @@ type Downloader struct {
|
||||||
synchroniseMock func(id string, hash common.Hash) error // Replacement for synchronise during testing
|
synchroniseMock func(id string, hash common.Hash) error // Replacement for synchronise during testing
|
||||||
synchronising int32
|
synchronising int32
|
||||||
notified int32
|
notified int32
|
||||||
|
committed int32
|
||||||
|
|
||||||
// Channels
|
// Channels
|
||||||
headerCh chan dataPack // [eth/62] Channel receiving inbound block headers
|
headerCh chan dataPack // [eth/62] Channel receiving inbound block headers
|
||||||
|
|
@ -156,7 +151,7 @@ type Downloader struct {
|
||||||
// LightChain encapsulates functions required to synchronise a light chain.
|
// LightChain encapsulates functions required to synchronise a light chain.
|
||||||
type LightChain interface {
|
type LightChain interface {
|
||||||
// HasHeader verifies a header's presence in the local chain.
|
// HasHeader verifies a header's presence in the local chain.
|
||||||
HasHeader(h common.Hash, number uint64) bool
|
HasHeader(common.Hash, uint64) bool
|
||||||
|
|
||||||
// GetHeaderByHash retrieves a header from the local chain.
|
// GetHeaderByHash retrieves a header from the local chain.
|
||||||
GetHeaderByHash(common.Hash) *types.Header
|
GetHeaderByHash(common.Hash) *types.Header
|
||||||
|
|
@ -164,8 +159,8 @@ type LightChain interface {
|
||||||
// CurrentHeader retrieves the head header from the local chain.
|
// CurrentHeader retrieves the head header from the local chain.
|
||||||
CurrentHeader() *types.Header
|
CurrentHeader() *types.Header
|
||||||
|
|
||||||
// GetTdByHash returns the total difficulty of a local block.
|
// GetTd returns the total difficulty of a local block.
|
||||||
GetTdByHash(common.Hash) *big.Int
|
GetTd(common.Hash, uint64) *big.Int
|
||||||
|
|
||||||
// InsertHeaderChain inserts a batch of headers into the local chain.
|
// InsertHeaderChain inserts a batch of headers into the local chain.
|
||||||
InsertHeaderChain([]*types.Header, int) (int, error)
|
InsertHeaderChain([]*types.Header, int) (int, error)
|
||||||
|
|
@ -178,8 +173,8 @@ type LightChain interface {
|
||||||
type BlockChain interface {
|
type BlockChain interface {
|
||||||
LightChain
|
LightChain
|
||||||
|
|
||||||
// HasBlockAndState verifies block and associated states' presence in the local chain.
|
// HasBlock verifies a block's presence in the local chain.
|
||||||
HasBlockAndState(common.Hash) bool
|
HasBlock(common.Hash, uint64) bool
|
||||||
|
|
||||||
// GetBlockByHash retrieves a block from the local chain.
|
// GetBlockByHash retrieves a block from the local chain.
|
||||||
GetBlockByHash(common.Hash) *types.Block
|
GetBlockByHash(common.Hash) *types.Block
|
||||||
|
|
@ -271,7 +266,6 @@ func (d *Downloader) Synchronising() bool {
|
||||||
// RegisterPeer injects a new download peer into the set of block source to be
|
// RegisterPeer injects a new download peer into the set of block source to be
|
||||||
// used for fetching hashes and blocks from.
|
// used for fetching hashes and blocks from.
|
||||||
func (d *Downloader) RegisterPeer(id string, version int, peer Peer) error {
|
func (d *Downloader) RegisterPeer(id string, version int, peer Peer) error {
|
||||||
|
|
||||||
logger := log.New("peer", id)
|
logger := log.New("peer", id)
|
||||||
logger.Trace("Registering sync peer")
|
logger.Trace("Registering sync peer")
|
||||||
if err := d.peers.Register(newPeerConnection(id, version, peer, logger)); err != nil {
|
if err := d.peers.Register(newPeerConnection(id, version, peer, logger)); err != nil {
|
||||||
|
|
@ -324,8 +318,13 @@ func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode
|
||||||
errEmptyHeaderSet, errPeersUnavailable, errTooOld,
|
errEmptyHeaderSet, errPeersUnavailable, errTooOld,
|
||||||
errInvalidAncestor, errInvalidChain:
|
errInvalidAncestor, errInvalidChain:
|
||||||
log.Warn("Synchronisation failed, dropping peer", "peer", id, "err", err)
|
log.Warn("Synchronisation failed, dropping peer", "peer", id, "err", err)
|
||||||
d.dropPeer(id)
|
if d.dropPeer == nil {
|
||||||
|
// The dropPeer method is nil when `--copydb` is used for a local copy.
|
||||||
|
// Timeouts can occur if e.g. compaction hits at the wrong time, and can be ignored
|
||||||
|
log.Warn("Downloader wants to drop peer, but peerdrop-function is not set", "peer", id)
|
||||||
|
} else {
|
||||||
|
d.dropPeer(id)
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
log.Warn("Synchronisation failed, retrying", "err", err)
|
log.Warn("Synchronisation failed, retrying", "err", err)
|
||||||
}
|
}
|
||||||
|
|
@ -386,9 +385,7 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode
|
||||||
|
|
||||||
// Set the requested sync mode, unless it's forbidden
|
// Set the requested sync mode, unless it's forbidden
|
||||||
d.mode = mode
|
d.mode = mode
|
||||||
if d.mode == FastSync && atomic.LoadUint32(&d.fsPivotFails) >= fsCriticalTrials {
|
|
||||||
d.mode = FullSync
|
|
||||||
}
|
|
||||||
// Retrieve the origin peer and initiate the downloading process
|
// Retrieve the origin peer and initiate the downloading process
|
||||||
p := d.peers.Peer(id)
|
p := d.peers.Peer(id)
|
||||||
if p == nil {
|
if p == nil {
|
||||||
|
|
@ -436,57 +433,40 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I
|
||||||
d.syncStatsChainHeight = height
|
d.syncStatsChainHeight = height
|
||||||
d.syncStatsLock.Unlock()
|
d.syncStatsLock.Unlock()
|
||||||
|
|
||||||
// Initiate the sync using a concurrent header and content retrieval algorithm
|
// Ensure our origin point is below any fast sync pivot point
|
||||||
pivot := uint64(0)
|
pivot := uint64(0)
|
||||||
switch d.mode {
|
if d.mode == FastSync {
|
||||||
case LightSync:
|
if height <= uint64(fsMinFullBlocks) {
|
||||||
pivot = height
|
origin = 0
|
||||||
case FastSync:
|
|
||||||
// Calculate the new fast/slow sync pivot point
|
|
||||||
if d.fsPivotLock == nil {
|
|
||||||
pivotOffset, err := rand.Int(rand.Reader, big.NewInt(int64(fsPivotInterval)))
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("Failed to access crypto random source: %v", err))
|
|
||||||
}
|
|
||||||
if height > uint64(fsMinFullBlocks)+pivotOffset.Uint64() {
|
|
||||||
pivot = height - uint64(fsMinFullBlocks) - pivotOffset.Uint64()
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Pivot point locked in, use this and do not pick a new one!
|
pivot = height - uint64(fsMinFullBlocks)
|
||||||
pivot = d.fsPivotLock.Number.Uint64()
|
if pivot <= origin {
|
||||||
}
|
|
||||||
// If the point is below the origin, move origin back to ensure state download
|
|
||||||
if pivot < origin {
|
|
||||||
if pivot > 0 {
|
|
||||||
origin = pivot - 1
|
origin = pivot - 1
|
||||||
} else {
|
|
||||||
origin = 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.Debug("Fast syncing until pivot block", "pivot", pivot)
|
|
||||||
}
|
}
|
||||||
d.queue.Prepare(origin+1, d.mode, pivot, latest)
|
d.committed = 1
|
||||||
|
if d.mode == FastSync && pivot != 0 {
|
||||||
|
d.committed = 0
|
||||||
|
}
|
||||||
|
// Initiate the sync using a concurrent header and content retrieval algorithm
|
||||||
|
d.queue.Prepare(origin+1, d.mode)
|
||||||
if d.syncInitHook != nil {
|
if d.syncInitHook != nil {
|
||||||
d.syncInitHook(origin, height)
|
d.syncInitHook(origin, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchers := []func() error{
|
fetchers := []func() error{
|
||||||
func() error { return d.fetchHeaders(p, origin+1) }, // Headers are always retrieved
|
func() error { return d.fetchHeaders(p, origin+1, pivot) }, // Headers are always retrieved
|
||||||
func() error { return d.fetchBodies(origin + 1) }, // Bodies are retrieved during normal and fast sync
|
func() error { return d.fetchBodies(origin + 1) }, // Bodies are retrieved during normal and fast sync
|
||||||
func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during fast sync
|
func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during fast sync
|
||||||
func() error { return d.processHeaders(origin+1, td) },
|
func() error { return d.processHeaders(origin+1, pivot, td) },
|
||||||
}
|
}
|
||||||
if d.mode == FastSync {
|
if d.mode == FastSync {
|
||||||
fetchers = append(fetchers, func() error { return d.processFastSyncContent(latest) })
|
fetchers = append(fetchers, func() error { return d.processFastSyncContent(latest) })
|
||||||
} else if d.mode == FullSync {
|
} else if d.mode == FullSync {
|
||||||
fetchers = append(fetchers, d.processFullSyncContent)
|
fetchers = append(fetchers, d.processFullSyncContent)
|
||||||
}
|
}
|
||||||
err = d.spawnSync(fetchers)
|
return d.spawnSync(fetchers)
|
||||||
if err != nil && d.mode == FastSync && d.fsPivotLock != nil {
|
|
||||||
// If sync failed in the critical section, bump the fail counter.
|
|
||||||
atomic.AddUint32(&d.fsPivotFails, 1)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// spawnSync runs d.process and all given fetcher functions to completion in
|
// spawnSync runs d.process and all given fetcher functions to completion in
|
||||||
|
|
@ -602,7 +582,6 @@ func (d *Downloader) findAncestor(p *peerConnection, height uint64) (uint64, err
|
||||||
// Figure out the valid ancestor range to prevent rewrite attacks
|
// Figure out the valid ancestor range to prevent rewrite attacks
|
||||||
floor, ceil := int64(-1), d.lightchain.CurrentHeader().Number.Uint64()
|
floor, ceil := int64(-1), d.lightchain.CurrentHeader().Number.Uint64()
|
||||||
|
|
||||||
p.log.Debug("Looking for common ancestor", "local", ceil, "remote", height)
|
|
||||||
if d.mode == FullSync {
|
if d.mode == FullSync {
|
||||||
ceil = d.blockchain.CurrentBlock().NumberU64()
|
ceil = d.blockchain.CurrentBlock().NumberU64()
|
||||||
} else if d.mode == FastSync {
|
} else if d.mode == FastSync {
|
||||||
|
|
@ -611,6 +590,8 @@ func (d *Downloader) findAncestor(p *peerConnection, height uint64) (uint64, err
|
||||||
if ceil >= MaxForkAncestry {
|
if ceil >= MaxForkAncestry {
|
||||||
floor = int64(ceil - MaxForkAncestry)
|
floor = int64(ceil - MaxForkAncestry)
|
||||||
}
|
}
|
||||||
|
p.log.Debug("Looking for common ancestor", "local", ceil, "remote", height)
|
||||||
|
|
||||||
// Request the topmost blocks to short circuit binary ancestor lookup
|
// Request the topmost blocks to short circuit binary ancestor lookup
|
||||||
head := ceil
|
head := ceil
|
||||||
if head > height {
|
if head > height {
|
||||||
|
|
@ -666,7 +647,7 @@ func (d *Downloader) findAncestor(p *peerConnection, height uint64) (uint64, err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Otherwise check if we already know the header or not
|
// Otherwise check if we already know the header or not
|
||||||
if (d.mode == FullSync && d.blockchain.HasBlockAndState(headers[i].Hash())) || (d.mode != FullSync && d.lightchain.HasHeader(headers[i].Hash(), headers[i].Number.Uint64())) {
|
if (d.mode == FullSync && d.blockchain.HasBlock(headers[i].Hash(), headers[i].Number.Uint64())) || (d.mode != FullSync && d.lightchain.HasHeader(headers[i].Hash(), headers[i].Number.Uint64())) {
|
||||||
number, hash = headers[i].Number.Uint64(), headers[i].Hash()
|
number, hash = headers[i].Number.Uint64(), headers[i].Hash()
|
||||||
|
|
||||||
// If every header is known, even future ones, the peer straight out lied about its head
|
// If every header is known, even future ones, the peer straight out lied about its head
|
||||||
|
|
@ -731,7 +712,7 @@ func (d *Downloader) findAncestor(p *peerConnection, height uint64) (uint64, err
|
||||||
arrived = true
|
arrived = true
|
||||||
|
|
||||||
// Modify the search interval based on the response
|
// Modify the search interval based on the response
|
||||||
if (d.mode == FullSync && !d.blockchain.HasBlockAndState(headers[0].Hash())) || (d.mode != FullSync && !d.lightchain.HasHeader(headers[0].Hash(), headers[0].Number.Uint64())) {
|
if (d.mode == FullSync && !d.blockchain.HasBlock(headers[0].Hash(), headers[0].Number.Uint64())) || (d.mode != FullSync && !d.lightchain.HasHeader(headers[0].Hash(), headers[0].Number.Uint64())) {
|
||||||
end = check
|
end = check
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -769,7 +750,7 @@ func (d *Downloader) findAncestor(p *peerConnection, height uint64) (uint64, err
|
||||||
// other peers are only accepted if they map cleanly to the skeleton. If no one
|
// other peers are only accepted if they map cleanly to the skeleton. If no one
|
||||||
// can fill in the skeleton - not even the origin peer - it's assumed invalid and
|
// can fill in the skeleton - not even the origin peer - it's assumed invalid and
|
||||||
// the origin is dropped.
|
// the origin is dropped.
|
||||||
func (d *Downloader) fetchHeaders(p *peerConnection, from uint64) error {
|
func (d *Downloader) fetchHeaders(p *peerConnection, from uint64, pivot uint64) error {
|
||||||
p.log.Debug("Directing header downloads", "origin", from)
|
p.log.Debug("Directing header downloads", "origin", from)
|
||||||
defer p.log.Debug("Header download terminated")
|
defer p.log.Debug("Header download terminated")
|
||||||
|
|
||||||
|
|
@ -820,6 +801,18 @@ func (d *Downloader) fetchHeaders(p *peerConnection, from uint64) error {
|
||||||
}
|
}
|
||||||
// If no more headers are inbound, notify the content fetchers and return
|
// If no more headers are inbound, notify the content fetchers and return
|
||||||
if packet.Items() == 0 {
|
if packet.Items() == 0 {
|
||||||
|
// Don't abort header fetches while the pivot is downloading
|
||||||
|
if atomic.LoadInt32(&d.committed) == 0 && pivot <= from {
|
||||||
|
p.log.Debug("No headers, waiting for pivot commit")
|
||||||
|
select {
|
||||||
|
case <-time.After(fsHeaderContCheck):
|
||||||
|
getHeaders(from)
|
||||||
|
continue
|
||||||
|
case <-d.cancelCh:
|
||||||
|
return errCancelHeaderFetch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Pivot done (or not in fast sync) and no more headers, terminate the process
|
||||||
p.log.Debug("No more headers available")
|
p.log.Debug("No more headers available")
|
||||||
select {
|
select {
|
||||||
case d.headerProcCh <- nil:
|
case d.headerProcCh <- nil:
|
||||||
|
|
@ -853,6 +846,12 @@ func (d *Downloader) fetchHeaders(p *peerConnection, from uint64) error {
|
||||||
getHeaders(from)
|
getHeaders(from)
|
||||||
|
|
||||||
case <-timeout.C:
|
case <-timeout.C:
|
||||||
|
if d.dropPeer == nil {
|
||||||
|
// The dropPeer method is nil when `--copydb` is used for a local copy.
|
||||||
|
// Timeouts can occur if e.g. compaction hits at the wrong time, and can be ignored
|
||||||
|
p.log.Warn("Downloader wants to drop peer, but peerdrop-function is not set", "peer", p.id)
|
||||||
|
break
|
||||||
|
}
|
||||||
// Header retrieval timed out, consider the peer bad and drop
|
// Header retrieval timed out, consider the peer bad and drop
|
||||||
p.log.Debug("Header request timed out", "elapsed", ttl)
|
p.log.Debug("Header request timed out", "elapsed", ttl)
|
||||||
headerTimeoutMeter.Mark(1)
|
headerTimeoutMeter.Mark(1)
|
||||||
|
|
@ -1071,7 +1070,13 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
|
||||||
setIdle(peer, 0)
|
setIdle(peer, 0)
|
||||||
} else {
|
} else {
|
||||||
peer.log.Debug("Stalling delivery, dropping", "type", kind)
|
peer.log.Debug("Stalling delivery, dropping", "type", kind)
|
||||||
d.dropPeer(pid)
|
if d.dropPeer == nil {
|
||||||
|
// The dropPeer method is nil when `--copydb` is used for a local copy.
|
||||||
|
// Timeouts can occur if e.g. compaction hits at the wrong time, and can be ignored
|
||||||
|
peer.log.Warn("Downloader wants to drop peer, but peerdrop-function is not set", "peer", pid)
|
||||||
|
} else {
|
||||||
|
d.dropPeer(pid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1112,10 +1117,8 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
|
||||||
}
|
}
|
||||||
if request.From > 0 {
|
if request.From > 0 {
|
||||||
peer.log.Trace("Requesting new batch of data", "type", kind, "from", request.From)
|
peer.log.Trace("Requesting new batch of data", "type", kind, "from", request.From)
|
||||||
} else if len(request.Headers) > 0 {
|
|
||||||
peer.log.Trace("Requesting new batch of data", "type", kind, "count", len(request.Headers), "from", request.Headers[0].Number)
|
|
||||||
} else {
|
} else {
|
||||||
peer.log.Trace("Requesting new batch of data", "type", kind, "count", len(request.Hashes))
|
peer.log.Trace("Requesting new batch of data", "type", kind, "count", len(request.Headers), "from", request.Headers[0].Number)
|
||||||
}
|
}
|
||||||
// Fetch the chunk and make sure any errors return the hashes to the queue
|
// Fetch the chunk and make sure any errors return the hashes to the queue
|
||||||
if fetchHook != nil {
|
if fetchHook != nil {
|
||||||
|
|
@ -1143,10 +1146,7 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
|
||||||
// processHeaders takes batches of retrieved headers from an input channel and
|
// processHeaders takes batches of retrieved headers from an input channel and
|
||||||
// keeps processing and scheduling them into the header chain and downloader's
|
// keeps processing and scheduling them into the header chain and downloader's
|
||||||
// queue until the stream ends or a failure occurs.
|
// queue until the stream ends or a failure occurs.
|
||||||
func (d *Downloader) processHeaders(origin uint64, td *big.Int) error {
|
func (d *Downloader) processHeaders(origin uint64, pivot uint64, td *big.Int) error {
|
||||||
// Calculate the pivoting point for switching from fast to slow sync
|
|
||||||
pivot := d.queue.FastSyncPivot()
|
|
||||||
|
|
||||||
// Keep a count of uncertain headers to roll back
|
// Keep a count of uncertain headers to roll back
|
||||||
rollback := []*types.Header{}
|
rollback := []*types.Header{}
|
||||||
defer func() {
|
defer func() {
|
||||||
|
|
@ -1171,19 +1171,6 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error {
|
||||||
"header", fmt.Sprintf("%d->%d", lastHeader, d.lightchain.CurrentHeader().Number),
|
"header", fmt.Sprintf("%d->%d", lastHeader, d.lightchain.CurrentHeader().Number),
|
||||||
"fast", fmt.Sprintf("%d->%d", lastFastBlock, curFastBlock),
|
"fast", fmt.Sprintf("%d->%d", lastFastBlock, curFastBlock),
|
||||||
"block", fmt.Sprintf("%d->%d", lastBlock, curBlock))
|
"block", fmt.Sprintf("%d->%d", lastBlock, curBlock))
|
||||||
|
|
||||||
// If we're already past the pivot point, this could be an attack, thread carefully
|
|
||||||
if rollback[len(rollback)-1].Number.Uint64() > pivot {
|
|
||||||
// If we didn't ever fail, lock in the pivot header (must! not! change!)
|
|
||||||
if atomic.LoadUint32(&d.fsPivotFails) == 0 {
|
|
||||||
for _, header := range rollback {
|
|
||||||
if header.Number.Uint64() == pivot {
|
|
||||||
log.Warn("Fast-sync pivot locked in", "number", pivot, "hash", header.Hash())
|
|
||||||
d.fsPivotLock = header
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -1218,7 +1205,8 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error {
|
||||||
// L: Request new headers up from 11 (R's TD was higher, it must have something)
|
// L: Request new headers up from 11 (R's TD was higher, it must have something)
|
||||||
// R: Nothing to give
|
// R: Nothing to give
|
||||||
if d.mode != LightSync {
|
if d.mode != LightSync {
|
||||||
if !gotHeaders && td.Cmp(d.blockchain.GetTdByHash(d.blockchain.CurrentBlock().Hash())) > 0 {
|
head := d.blockchain.CurrentBlock()
|
||||||
|
if !gotHeaders && td.Cmp(d.blockchain.GetTd(head.Hash(), head.NumberU64())) > 0 {
|
||||||
return errStallingPeer
|
return errStallingPeer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1230,7 +1218,8 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error {
|
||||||
// queued for processing when the header download completes. However, as long as the
|
// queued for processing when the header download completes. However, as long as the
|
||||||
// peer gave us something useful, we're already happy/progressed (above check).
|
// peer gave us something useful, we're already happy/progressed (above check).
|
||||||
if d.mode == FastSync || d.mode == LightSync {
|
if d.mode == FastSync || d.mode == LightSync {
|
||||||
if td.Cmp(d.lightchain.GetTdByHash(d.lightchain.CurrentHeader().Hash())) > 0 {
|
head := d.lightchain.CurrentHeader()
|
||||||
|
if td.Cmp(d.lightchain.GetTd(head.Hash(), head.Number.Uint64())) > 0 {
|
||||||
return errStallingPeer
|
return errStallingPeer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1283,13 +1272,6 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error {
|
||||||
rollback = append(rollback[:0], rollback[len(rollback)-fsHeaderSafetyNet:]...)
|
rollback = append(rollback[:0], rollback[len(rollback)-fsHeaderSafetyNet:]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If we're fast syncing and just pulled in the pivot, make sure it's the one locked in
|
|
||||||
if d.mode == FastSync && d.fsPivotLock != nil && chunk[0].Number.Uint64() <= pivot && chunk[len(chunk)-1].Number.Uint64() >= pivot {
|
|
||||||
if pivot := chunk[int(pivot-chunk[0].Number.Uint64())]; pivot.Hash() != d.fsPivotLock.Hash() {
|
|
||||||
log.Warn("Pivot doesn't match locked in one", "remoteNumber", pivot.Number, "remoteHash", pivot.Hash(), "localNumber", d.fsPivotLock.Number, "localHash", d.fsPivotLock.Hash())
|
|
||||||
return errInvalidChain
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Unless we're doing light chains, schedule the headers for associated content retrieval
|
// Unless we're doing light chains, schedule the headers for associated content retrieval
|
||||||
if d.mode == FullSync || d.mode == FastSync {
|
if d.mode == FullSync || d.mode == FastSync {
|
||||||
// If we've reached the allowed number of pending headers, stall a bit
|
// If we've reached the allowed number of pending headers, stall a bit
|
||||||
|
|
@ -1324,7 +1306,7 @@ func (d *Downloader) processHeaders(origin uint64, td *big.Int) error {
|
||||||
// processFullSyncContent takes fetch results from the queue and imports them into the chain.
|
// processFullSyncContent takes fetch results from the queue and imports them into the chain.
|
||||||
func (d *Downloader) processFullSyncContent() error {
|
func (d *Downloader) processFullSyncContent() error {
|
||||||
for {
|
for {
|
||||||
results := d.queue.WaitResults()
|
results := d.queue.Results(true)
|
||||||
if len(results) == 0 {
|
if len(results) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -1338,30 +1320,28 @@ func (d *Downloader) processFullSyncContent() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Downloader) importBlockResults(results []*fetchResult) error {
|
func (d *Downloader) importBlockResults(results []*fetchResult) error {
|
||||||
for len(results) != 0 {
|
// Check for any early termination requests
|
||||||
// Check for any termination requests. This makes clean shutdown faster.
|
if len(results) == 0 {
|
||||||
select {
|
return nil
|
||||||
case <-d.quitCh:
|
}
|
||||||
return errCancelContentProcessing
|
select {
|
||||||
default:
|
case <-d.quitCh:
|
||||||
}
|
return errCancelContentProcessing
|
||||||
// Retrieve the a batch of results to import
|
default:
|
||||||
items := int(math.Min(float64(len(results)), float64(maxResultsProcess)))
|
}
|
||||||
first, last := results[0].Header, results[items-1].Header
|
// Retrieve the a batch of results to import
|
||||||
log.Debug("Inserting downloaded chain", "items", len(results),
|
first, last := results[0].Header, results[len(results)-1].Header
|
||||||
"firstnum", first.Number, "firsthash", first.Hash(),
|
log.Debug("Inserting downloaded chain", "items", len(results),
|
||||||
"lastnum", last.Number, "lasthash", last.Hash(),
|
"firstnum", first.Number, "firsthash", first.Hash(),
|
||||||
)
|
"lastnum", last.Number, "lasthash", last.Hash(),
|
||||||
blocks := make([]*types.Block, items)
|
)
|
||||||
for i, result := range results[:items] {
|
blocks := make([]*types.Block, len(results))
|
||||||
blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)
|
for i, result := range results {
|
||||||
}
|
blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)
|
||||||
if index, err := d.blockchain.InsertChain(blocks); err != nil {
|
}
|
||||||
log.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err)
|
if index, err := d.blockchain.InsertChain(blocks); err != nil {
|
||||||
return errInvalidChain
|
log.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err)
|
||||||
}
|
return errInvalidChain
|
||||||
// Shift the results to the next batch
|
|
||||||
results = results[items:]
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -1369,35 +1349,92 @@ func (d *Downloader) importBlockResults(results []*fetchResult) error {
|
||||||
// processFastSyncContent takes fetch results from the queue and writes them to the
|
// processFastSyncContent takes fetch results from the queue and writes them to the
|
||||||
// database. It also controls the synchronisation of state nodes of the pivot block.
|
// database. It also controls the synchronisation of state nodes of the pivot block.
|
||||||
func (d *Downloader) processFastSyncContent(latest *types.Header) error {
|
func (d *Downloader) processFastSyncContent(latest *types.Header) error {
|
||||||
// Start syncing state of the reported head block.
|
// Start syncing state of the reported head block. This should get us most of
|
||||||
// This should get us most of the state of the pivot block.
|
// the state of the pivot block.
|
||||||
stateSync := d.syncState(latest.Root)
|
stateSync := d.syncState(latest.Root)
|
||||||
defer stateSync.Cancel()
|
defer stateSync.Cancel()
|
||||||
go func() {
|
go func() {
|
||||||
if err := stateSync.Wait(); err != nil {
|
if err := stateSync.Wait(); err != nil && err != errCancelStateFetch {
|
||||||
d.queue.Close() // wake up WaitResults
|
d.queue.Close() // wake up WaitResults
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
// Figure out the ideal pivot block. Note, that this goalpost may move if the
|
||||||
pivot := d.queue.FastSyncPivot()
|
// sync takes long enough for the chain head to move significantly.
|
||||||
|
pivot := uint64(0)
|
||||||
|
if height := latest.Number.Uint64(); height > uint64(fsMinFullBlocks) {
|
||||||
|
pivot = height - uint64(fsMinFullBlocks)
|
||||||
|
}
|
||||||
|
// To cater for moving pivot points, track the pivot block and subsequently
|
||||||
|
// accumulated download results separatey.
|
||||||
|
var (
|
||||||
|
oldPivot *fetchResult // Locked in pivot block, might change eventually
|
||||||
|
oldTail []*fetchResult // Downloaded content after the pivot
|
||||||
|
)
|
||||||
for {
|
for {
|
||||||
results := d.queue.WaitResults()
|
// Wait for the next batch of downloaded data to be available, and if the pivot
|
||||||
|
// block became stale, move the goalpost
|
||||||
|
results := d.queue.Results(oldPivot == nil) // Block if we're not monitoring pivot staleness
|
||||||
if len(results) == 0 {
|
if len(results) == 0 {
|
||||||
return stateSync.Cancel()
|
// If pivot sync is done, stop
|
||||||
|
if oldPivot == nil {
|
||||||
|
return stateSync.Cancel()
|
||||||
|
}
|
||||||
|
// If sync failed, stop
|
||||||
|
select {
|
||||||
|
case <-d.cancelCh:
|
||||||
|
return stateSync.Cancel()
|
||||||
|
default:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if d.chainInsertHook != nil {
|
if d.chainInsertHook != nil {
|
||||||
d.chainInsertHook(results)
|
d.chainInsertHook(results)
|
||||||
}
|
}
|
||||||
|
if oldPivot != nil {
|
||||||
|
results = append(append([]*fetchResult{oldPivot}, oldTail...), results...)
|
||||||
|
}
|
||||||
|
// Split around the pivot block and process the two sides via fast/full sync
|
||||||
|
if atomic.LoadInt32(&d.committed) == 0 {
|
||||||
|
latest = results[len(results)-1].Header
|
||||||
|
if height := latest.Number.Uint64(); height > pivot+2*uint64(fsMinFullBlocks) {
|
||||||
|
log.Warn("Pivot became stale, moving", "old", pivot, "new", height-uint64(fsMinFullBlocks))
|
||||||
|
pivot = height - uint64(fsMinFullBlocks)
|
||||||
|
}
|
||||||
|
}
|
||||||
P, beforeP, afterP := splitAroundPivot(pivot, results)
|
P, beforeP, afterP := splitAroundPivot(pivot, results)
|
||||||
if err := d.commitFastSyncData(beforeP, stateSync); err != nil {
|
if err := d.commitFastSyncData(beforeP, stateSync); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if P != nil {
|
if P != nil {
|
||||||
stateSync.Cancel()
|
// If new pivot block found, cancel old state retrieval and restart
|
||||||
if err := d.commitPivotBlock(P); err != nil {
|
if oldPivot != P {
|
||||||
return err
|
stateSync.Cancel()
|
||||||
|
|
||||||
|
stateSync = d.syncState(P.Header.Root)
|
||||||
|
defer stateSync.Cancel()
|
||||||
|
go func() {
|
||||||
|
if err := stateSync.Wait(); err != nil && err != errCancelStateFetch {
|
||||||
|
d.queue.Close() // wake up WaitResults
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
oldPivot = P
|
||||||
|
}
|
||||||
|
// Wait for completion, occasionally checking for pivot staleness
|
||||||
|
select {
|
||||||
|
case <-stateSync.done:
|
||||||
|
if stateSync.err != nil {
|
||||||
|
return stateSync.err
|
||||||
|
}
|
||||||
|
if err := d.commitPivotBlock(P); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
oldPivot = nil
|
||||||
|
|
||||||
|
case <-time.After(time.Second):
|
||||||
|
oldTail = afterP
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Fast sync done, pivot commit done, full import
|
||||||
if err := d.importBlockResults(afterP); err != nil {
|
if err := d.importBlockResults(afterP); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -1420,52 +1457,49 @@ func splitAroundPivot(pivot uint64, results []*fetchResult) (p *fetchResult, bef
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Downloader) commitFastSyncData(results []*fetchResult, stateSync *stateSync) error {
|
func (d *Downloader) commitFastSyncData(results []*fetchResult, stateSync *stateSync) error {
|
||||||
for len(results) != 0 {
|
// Check for any early termination requests
|
||||||
// Check for any termination requests.
|
if len(results) == 0 {
|
||||||
select {
|
return nil
|
||||||
case <-d.quitCh:
|
}
|
||||||
return errCancelContentProcessing
|
select {
|
||||||
case <-stateSync.done:
|
case <-d.quitCh:
|
||||||
if err := stateSync.Wait(); err != nil {
|
return errCancelContentProcessing
|
||||||
return err
|
case <-stateSync.done:
|
||||||
}
|
if err := stateSync.Wait(); err != nil {
|
||||||
default:
|
return err
|
||||||
}
|
}
|
||||||
// Retrieve the a batch of results to import
|
default:
|
||||||
items := int(math.Min(float64(len(results)), float64(maxResultsProcess)))
|
}
|
||||||
first, last := results[0].Header, results[items-1].Header
|
// Retrieve the a batch of results to import
|
||||||
log.Debug("Inserting fast-sync blocks", "items", len(results),
|
first, last := results[0].Header, results[len(results)-1].Header
|
||||||
"firstnum", first.Number, "firsthash", first.Hash(),
|
log.Debug("Inserting fast-sync blocks", "items", len(results),
|
||||||
"lastnumn", last.Number, "lasthash", last.Hash(),
|
"firstnum", first.Number, "firsthash", first.Hash(),
|
||||||
)
|
"lastnumn", last.Number, "lasthash", last.Hash(),
|
||||||
blocks := make([]*types.Block, items)
|
)
|
||||||
receipts := make([]types.Receipts, items)
|
blocks := make([]*types.Block, len(results))
|
||||||
for i, result := range results[:items] {
|
receipts := make([]types.Receipts, len(results))
|
||||||
blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)
|
for i, result := range results {
|
||||||
receipts[i] = result.Receipts
|
blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)
|
||||||
}
|
receipts[i] = result.Receipts
|
||||||
if index, err := d.blockchain.InsertReceiptChain(blocks, receipts); err != nil {
|
}
|
||||||
log.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err)
|
if index, err := d.blockchain.InsertReceiptChain(blocks, receipts); err != nil {
|
||||||
return errInvalidChain
|
log.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err)
|
||||||
}
|
return errInvalidChain
|
||||||
// Shift the results to the next batch
|
|
||||||
results = results[items:]
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Downloader) commitPivotBlock(result *fetchResult) error {
|
func (d *Downloader) commitPivotBlock(result *fetchResult) error {
|
||||||
b := types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)
|
block := types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)
|
||||||
// Sync the pivot block state. This should complete reasonably quickly because
|
log.Debug("Committing fast sync pivot as new head", "number", block.Number(), "hash", block.Hash())
|
||||||
// we've already synced up to the reported head block state earlier.
|
if _, err := d.blockchain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{result.Receipts}); err != nil {
|
||||||
if err := d.syncState(b.Root()).Wait(); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
log.Debug("Committing fast sync pivot as new head", "number", b.Number(), "hash", b.Hash())
|
if err := d.blockchain.FastSyncCommitHead(block.Hash()); err != nil {
|
||||||
if _, err := d.blockchain.InsertReceiptChain([]*types.Block{b}, []types.Receipts{result.Receipts}); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return d.blockchain.FastSyncCommitHead(b.Hash())
|
atomic.StoreInt32(&d.committed, 1)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeliverHeaders injects a new batch of block headers received from a remote
|
// DeliverHeaders injects a new batch of block headers received from a remote
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ import (
|
||||||
"github.com/EthereumCommonwealth/go-callisto/common"
|
"github.com/EthereumCommonwealth/go-callisto/common"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/consensus/ethash"
|
"github.com/EthereumCommonwealth/go-callisto/consensus/ethash"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/core"
|
"github.com/EthereumCommonwealth/go-callisto/core"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/core/state"
|
|
||||||
"github.com/EthereumCommonwealth/go-callisto/core/types"
|
"github.com/EthereumCommonwealth/go-callisto/core/types"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/crypto"
|
"github.com/EthereumCommonwealth/go-callisto/crypto"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/ethdb"
|
"github.com/EthereumCommonwealth/go-callisto/ethdb"
|
||||||
|
|
@ -45,8 +44,8 @@ var (
|
||||||
// Reduce some of the parameters to make the tester faster.
|
// Reduce some of the parameters to make the tester faster.
|
||||||
func init() {
|
func init() {
|
||||||
MaxForkAncestry = uint64(10000)
|
MaxForkAncestry = uint64(10000)
|
||||||
blockCacheLimit = 1024
|
blockCacheItems = 1024
|
||||||
fsCriticalTrials = 10
|
fsHeaderContCheck = 500 * time.Millisecond
|
||||||
}
|
}
|
||||||
|
|
||||||
// downloadTester is a test simulator for mocking out local block chain.
|
// downloadTester is a test simulator for mocking out local block chain.
|
||||||
|
|
@ -222,14 +221,9 @@ func (dl *downloadTester) HasHeader(hash common.Hash, number uint64) bool {
|
||||||
return dl.GetHeaderByHash(hash) != nil
|
return dl.GetHeaderByHash(hash) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasBlockAndState checks if a block and associated state is present in the testers canonical chain.
|
// HasBlock checks if a block is present in the testers canonical chain.
|
||||||
func (dl *downloadTester) HasBlockAndState(hash common.Hash) bool {
|
func (dl *downloadTester) HasBlock(hash common.Hash, number uint64) bool {
|
||||||
block := dl.GetBlockByHash(hash)
|
return dl.GetBlockByHash(hash) != nil
|
||||||
if block == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
_, err := dl.stateDb.Get(block.Root().Bytes())
|
|
||||||
return err == nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHeader retrieves a header from the testers canonical chain.
|
// GetHeader retrieves a header from the testers canonical chain.
|
||||||
|
|
@ -293,14 +287,14 @@ func (dl *downloadTester) CurrentFastBlock() *types.Block {
|
||||||
func (dl *downloadTester) FastSyncCommitHead(hash common.Hash) error {
|
func (dl *downloadTester) FastSyncCommitHead(hash common.Hash) error {
|
||||||
// For now only check that the state trie is correct
|
// For now only check that the state trie is correct
|
||||||
if block := dl.GetBlockByHash(hash); block != nil {
|
if block := dl.GetBlockByHash(hash); block != nil {
|
||||||
_, err := trie.NewSecure(block.Root(), dl.stateDb, 0)
|
_, err := trie.NewSecure(block.Root(), trie.NewDatabase(dl.stateDb), 0)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return fmt.Errorf("non existent block: %x", hash[:4])
|
return fmt.Errorf("non existent block: %x", hash[:4])
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTdByHash retrieves the block's total difficulty from the canonical chain.
|
// GetTd retrieves the block's total difficulty from the canonical chain.
|
||||||
func (dl *downloadTester) GetTdByHash(hash common.Hash) *big.Int {
|
func (dl *downloadTester) GetTd(hash common.Hash, number uint64) *big.Int {
|
||||||
dl.lock.RLock()
|
dl.lock.RLock()
|
||||||
defer dl.lock.RUnlock()
|
defer dl.lock.RUnlock()
|
||||||
|
|
||||||
|
|
@ -619,28 +613,22 @@ func assertOwnChain(t *testing.T, tester *downloadTester, length int) {
|
||||||
// number of items of the various chain components.
|
// number of items of the various chain components.
|
||||||
func assertOwnForkedChain(t *testing.T, tester *downloadTester, common int, lengths []int) {
|
func assertOwnForkedChain(t *testing.T, tester *downloadTester, common int, lengths []int) {
|
||||||
// Initialize the counters for the first fork
|
// Initialize the counters for the first fork
|
||||||
headers, blocks := lengths[0], lengths[0]
|
headers, blocks, receipts := lengths[0], lengths[0], lengths[0]-fsMinFullBlocks
|
||||||
|
|
||||||
minReceipts, maxReceipts := lengths[0]-fsMinFullBlocks-fsPivotInterval, lengths[0]-fsMinFullBlocks
|
if receipts < 0 {
|
||||||
if minReceipts < 0 {
|
receipts = 1
|
||||||
minReceipts = 1
|
|
||||||
}
|
|
||||||
if maxReceipts < 0 {
|
|
||||||
maxReceipts = 1
|
|
||||||
}
|
}
|
||||||
// Update the counters for each subsequent fork
|
// Update the counters for each subsequent fork
|
||||||
for _, length := range lengths[1:] {
|
for _, length := range lengths[1:] {
|
||||||
headers += length - common
|
headers += length - common
|
||||||
blocks += length - common
|
blocks += length - common
|
||||||
|
receipts += length - common - fsMinFullBlocks
|
||||||
minReceipts += length - common - fsMinFullBlocks - fsPivotInterval
|
|
||||||
maxReceipts += length - common - fsMinFullBlocks
|
|
||||||
}
|
}
|
||||||
switch tester.downloader.mode {
|
switch tester.downloader.mode {
|
||||||
case FullSync:
|
case FullSync:
|
||||||
minReceipts, maxReceipts = 1, 1
|
receipts = 1
|
||||||
case LightSync:
|
case LightSync:
|
||||||
blocks, minReceipts, maxReceipts = 1, 1, 1
|
blocks, receipts = 1, 1
|
||||||
}
|
}
|
||||||
if hs := len(tester.ownHeaders); hs != headers {
|
if hs := len(tester.ownHeaders); hs != headers {
|
||||||
t.Fatalf("synchronised headers mismatch: have %v, want %v", hs, headers)
|
t.Fatalf("synchronised headers mismatch: have %v, want %v", hs, headers)
|
||||||
|
|
@ -648,11 +636,12 @@ func assertOwnForkedChain(t *testing.T, tester *downloadTester, common int, leng
|
||||||
if bs := len(tester.ownBlocks); bs != blocks {
|
if bs := len(tester.ownBlocks); bs != blocks {
|
||||||
t.Fatalf("synchronised blocks mismatch: have %v, want %v", bs, blocks)
|
t.Fatalf("synchronised blocks mismatch: have %v, want %v", bs, blocks)
|
||||||
}
|
}
|
||||||
if rs := len(tester.ownReceipts); rs < minReceipts || rs > maxReceipts {
|
if rs := len(tester.ownReceipts); rs != receipts {
|
||||||
t.Fatalf("synchronised receipts mismatch: have %v, want between [%v, %v]", rs, minReceipts, maxReceipts)
|
t.Fatalf("synchronised receipts mismatch: have %v, want %v", rs, receipts)
|
||||||
}
|
}
|
||||||
// Verify the state trie too for fast syncs
|
// Verify the state trie too for fast syncs
|
||||||
if tester.downloader.mode == FastSync {
|
/*if tester.downloader.mode == FastSync {
|
||||||
|
pivot := uint64(0)
|
||||||
var index int
|
var index int
|
||||||
if pivot := int(tester.downloader.queue.fastSyncPivot); pivot < common {
|
if pivot := int(tester.downloader.queue.fastSyncPivot); pivot < common {
|
||||||
index = pivot
|
index = pivot
|
||||||
|
|
@ -660,11 +649,11 @@ func assertOwnForkedChain(t *testing.T, tester *downloadTester, common int, leng
|
||||||
index = len(tester.ownHashes) - lengths[len(lengths)-1] + int(tester.downloader.queue.fastSyncPivot)
|
index = len(tester.ownHashes) - lengths[len(lengths)-1] + int(tester.downloader.queue.fastSyncPivot)
|
||||||
}
|
}
|
||||||
if index > 0 {
|
if index > 0 {
|
||||||
if statedb, err := state.New(tester.ownHeaders[tester.ownHashes[index]].Root, state.NewDatabase(tester.stateDb)); statedb == nil || err != nil {
|
if statedb, err := state.New(tester.ownHeaders[tester.ownHashes[index]].Root, state.NewDatabase(trie.NewDatabase(tester.stateDb))); statedb == nil || err != nil {
|
||||||
t.Fatalf("state reconstruction failed: %v", err)
|
t.Fatalf("state reconstruction failed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that simple synchronization against a canonical chain works correctly.
|
// Tests that simple synchronization against a canonical chain works correctly.
|
||||||
|
|
@ -684,7 +673,7 @@ func testCanonicalSynchronisation(t *testing.T, protocol int, mode SyncMode) {
|
||||||
defer tester.terminate()
|
defer tester.terminate()
|
||||||
|
|
||||||
// Create a small enough block chain to download
|
// Create a small enough block chain to download
|
||||||
targetBlocks := blockCacheLimit - 15
|
targetBlocks := blockCacheItems - 15
|
||||||
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
||||||
|
|
||||||
tester.newPeer("peer", protocol, hashes, headers, blocks, receipts)
|
tester.newPeer("peer", protocol, hashes, headers, blocks, receipts)
|
||||||
|
|
@ -710,7 +699,7 @@ func testThrottling(t *testing.T, protocol int, mode SyncMode) {
|
||||||
defer tester.terminate()
|
defer tester.terminate()
|
||||||
|
|
||||||
// Create a long block chain to download and the tester
|
// Create a long block chain to download and the tester
|
||||||
targetBlocks := 8 * blockCacheLimit
|
targetBlocks := 8 * blockCacheItems
|
||||||
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
||||||
|
|
||||||
tester.newPeer("peer", protocol, hashes, headers, blocks, receipts)
|
tester.newPeer("peer", protocol, hashes, headers, blocks, receipts)
|
||||||
|
|
@ -745,9 +734,9 @@ func testThrottling(t *testing.T, protocol int, mode SyncMode) {
|
||||||
cached = len(tester.downloader.queue.blockDonePool)
|
cached = len(tester.downloader.queue.blockDonePool)
|
||||||
if mode == FastSync {
|
if mode == FastSync {
|
||||||
if receipts := len(tester.downloader.queue.receiptDonePool); receipts < cached {
|
if receipts := len(tester.downloader.queue.receiptDonePool); receipts < cached {
|
||||||
if tester.downloader.queue.resultCache[receipts].Header.Number.Uint64() < tester.downloader.queue.fastSyncPivot {
|
//if tester.downloader.queue.resultCache[receipts].Header.Number.Uint64() < tester.downloader.queue.fastSyncPivot {
|
||||||
cached = receipts
|
cached = receipts
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
frozen = int(atomic.LoadUint32(&blocked))
|
frozen = int(atomic.LoadUint32(&blocked))
|
||||||
|
|
@ -755,7 +744,7 @@ func testThrottling(t *testing.T, protocol int, mode SyncMode) {
|
||||||
tester.downloader.queue.lock.Unlock()
|
tester.downloader.queue.lock.Unlock()
|
||||||
tester.lock.Unlock()
|
tester.lock.Unlock()
|
||||||
|
|
||||||
if cached == blockCacheLimit || retrieved+cached+frozen == targetBlocks+1 {
|
if cached == blockCacheItems || retrieved+cached+frozen == targetBlocks+1 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -765,8 +754,8 @@ func testThrottling(t *testing.T, protocol int, mode SyncMode) {
|
||||||
tester.lock.RLock()
|
tester.lock.RLock()
|
||||||
retrieved = len(tester.ownBlocks)
|
retrieved = len(tester.ownBlocks)
|
||||||
tester.lock.RUnlock()
|
tester.lock.RUnlock()
|
||||||
if cached != blockCacheLimit && retrieved+cached+frozen != targetBlocks+1 {
|
if cached != blockCacheItems && retrieved+cached+frozen != targetBlocks+1 {
|
||||||
t.Fatalf("block count mismatch: have %v, want %v (owned %v, blocked %v, target %v)", cached, blockCacheLimit, retrieved, frozen, targetBlocks+1)
|
t.Fatalf("block count mismatch: have %v, want %v (owned %v, blocked %v, target %v)", cached, blockCacheItems, retrieved, frozen, targetBlocks+1)
|
||||||
}
|
}
|
||||||
// Permit the blocked blocks to import
|
// Permit the blocked blocks to import
|
||||||
if atomic.LoadUint32(&blocked) > 0 {
|
if atomic.LoadUint32(&blocked) > 0 {
|
||||||
|
|
@ -974,7 +963,7 @@ func testCancel(t *testing.T, protocol int, mode SyncMode) {
|
||||||
defer tester.terminate()
|
defer tester.terminate()
|
||||||
|
|
||||||
// Create a small enough block chain to download and the tester
|
// Create a small enough block chain to download and the tester
|
||||||
targetBlocks := blockCacheLimit - 15
|
targetBlocks := blockCacheItems - 15
|
||||||
if targetBlocks >= MaxHashFetch {
|
if targetBlocks >= MaxHashFetch {
|
||||||
targetBlocks = MaxHashFetch - 15
|
targetBlocks = MaxHashFetch - 15
|
||||||
}
|
}
|
||||||
|
|
@ -1016,12 +1005,12 @@ func testMultiSynchronisation(t *testing.T, protocol int, mode SyncMode) {
|
||||||
|
|
||||||
// Create various peers with various parts of the chain
|
// Create various peers with various parts of the chain
|
||||||
targetPeers := 8
|
targetPeers := 8
|
||||||
targetBlocks := targetPeers*blockCacheLimit - 15
|
targetBlocks := targetPeers*blockCacheItems - 15
|
||||||
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
||||||
|
|
||||||
for i := 0; i < targetPeers; i++ {
|
for i := 0; i < targetPeers; i++ {
|
||||||
id := fmt.Sprintf("peer #%d", i)
|
id := fmt.Sprintf("peer #%d", i)
|
||||||
tester.newPeer(id, protocol, hashes[i*blockCacheLimit:], headers, blocks, receipts)
|
tester.newPeer(id, protocol, hashes[i*blockCacheItems:], headers, blocks, receipts)
|
||||||
}
|
}
|
||||||
if err := tester.sync("peer #0", nil, mode); err != nil {
|
if err := tester.sync("peer #0", nil, mode); err != nil {
|
||||||
t.Fatalf("failed to synchronise blocks: %v", err)
|
t.Fatalf("failed to synchronise blocks: %v", err)
|
||||||
|
|
@ -1045,7 +1034,7 @@ func testMultiProtoSync(t *testing.T, protocol int, mode SyncMode) {
|
||||||
defer tester.terminate()
|
defer tester.terminate()
|
||||||
|
|
||||||
// Create a small enough block chain to download
|
// Create a small enough block chain to download
|
||||||
targetBlocks := blockCacheLimit - 15
|
targetBlocks := blockCacheItems - 15
|
||||||
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
||||||
|
|
||||||
// Create peers of every type
|
// Create peers of every type
|
||||||
|
|
@ -1084,7 +1073,7 @@ func testEmptyShortCircuit(t *testing.T, protocol int, mode SyncMode) {
|
||||||
defer tester.terminate()
|
defer tester.terminate()
|
||||||
|
|
||||||
// Create a block chain to download
|
// Create a block chain to download
|
||||||
targetBlocks := 2*blockCacheLimit - 15
|
targetBlocks := 2*blockCacheItems - 15
|
||||||
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
||||||
|
|
||||||
tester.newPeer("peer", protocol, hashes, headers, blocks, receipts)
|
tester.newPeer("peer", protocol, hashes, headers, blocks, receipts)
|
||||||
|
|
@ -1110,8 +1099,8 @@ func testEmptyShortCircuit(t *testing.T, protocol int, mode SyncMode) {
|
||||||
bodiesNeeded++
|
bodiesNeeded++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for hash, receipt := range receipts {
|
for _, receipt := range receipts {
|
||||||
if mode == FastSync && len(receipt) > 0 && headers[hash].Number.Uint64() <= tester.downloader.queue.fastSyncPivot {
|
if mode == FastSync && len(receipt) > 0 {
|
||||||
receiptsNeeded++
|
receiptsNeeded++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1139,7 +1128,7 @@ func testMissingHeaderAttack(t *testing.T, protocol int, mode SyncMode) {
|
||||||
defer tester.terminate()
|
defer tester.terminate()
|
||||||
|
|
||||||
// Create a small enough block chain to download
|
// Create a small enough block chain to download
|
||||||
targetBlocks := blockCacheLimit - 15
|
targetBlocks := blockCacheItems - 15
|
||||||
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
||||||
|
|
||||||
// Attempt a full sync with an attacker feeding gapped headers
|
// Attempt a full sync with an attacker feeding gapped headers
|
||||||
|
|
@ -1174,7 +1163,7 @@ func testShiftedHeaderAttack(t *testing.T, protocol int, mode SyncMode) {
|
||||||
defer tester.terminate()
|
defer tester.terminate()
|
||||||
|
|
||||||
// Create a small enough block chain to download
|
// Create a small enough block chain to download
|
||||||
targetBlocks := blockCacheLimit - 15
|
targetBlocks := blockCacheItems - 15
|
||||||
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
||||||
|
|
||||||
// Attempt a full sync with an attacker feeding shifted headers
|
// Attempt a full sync with an attacker feeding shifted headers
|
||||||
|
|
@ -1208,7 +1197,7 @@ func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) {
|
||||||
defer tester.terminate()
|
defer tester.terminate()
|
||||||
|
|
||||||
// Create a small enough block chain to download
|
// Create a small enough block chain to download
|
||||||
targetBlocks := 3*fsHeaderSafetyNet + fsPivotInterval + fsMinFullBlocks
|
targetBlocks := 3*fsHeaderSafetyNet + 256 + fsMinFullBlocks
|
||||||
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
||||||
|
|
||||||
// Attempt to sync with an attacker that feeds junk during the fast sync phase.
|
// Attempt to sync with an attacker that feeds junk during the fast sync phase.
|
||||||
|
|
@ -1248,7 +1237,6 @@ func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) {
|
||||||
tester.newPeer("withhold-attack", protocol, hashes, headers, blocks, receipts)
|
tester.newPeer("withhold-attack", protocol, hashes, headers, blocks, receipts)
|
||||||
missing = 3*fsHeaderSafetyNet + MaxHeaderFetch + 1
|
missing = 3*fsHeaderSafetyNet + MaxHeaderFetch + 1
|
||||||
|
|
||||||
tester.downloader.fsPivotFails = 0
|
|
||||||
tester.downloader.syncInitHook = func(uint64, uint64) {
|
tester.downloader.syncInitHook = func(uint64, uint64) {
|
||||||
for i := missing; i <= len(hashes); i++ {
|
for i := missing; i <= len(hashes); i++ {
|
||||||
delete(tester.peerHeaders["withhold-attack"], hashes[len(hashes)-i])
|
delete(tester.peerHeaders["withhold-attack"], hashes[len(hashes)-i])
|
||||||
|
|
@ -1267,8 +1255,6 @@ func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) {
|
||||||
t.Errorf("fast sync pivot block #%d not rolled back", head)
|
t.Errorf("fast sync pivot block #%d not rolled back", head)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tester.downloader.fsPivotFails = fsCriticalTrials
|
|
||||||
|
|
||||||
// Synchronise with the valid peer and make sure sync succeeds. Since the last
|
// Synchronise with the valid peer and make sure sync succeeds. Since the last
|
||||||
// rollback should also disable fast syncing for this process, verify that we
|
// rollback should also disable fast syncing for this process, verify that we
|
||||||
// did a fresh full sync. Note, we can't assert anything about the receipts
|
// did a fresh full sync. Note, we can't assert anything about the receipts
|
||||||
|
|
@ -1383,7 +1369,7 @@ func testSyncProgress(t *testing.T, protocol int, mode SyncMode) {
|
||||||
defer tester.terminate()
|
defer tester.terminate()
|
||||||
|
|
||||||
// Create a small enough block chain to download
|
// Create a small enough block chain to download
|
||||||
targetBlocks := blockCacheLimit - 15
|
targetBlocks := blockCacheItems - 15
|
||||||
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
||||||
|
|
||||||
// Set a sync init hook to catch progress changes
|
// Set a sync init hook to catch progress changes
|
||||||
|
|
@ -1532,7 +1518,7 @@ func testFailedSyncProgress(t *testing.T, protocol int, mode SyncMode) {
|
||||||
defer tester.terminate()
|
defer tester.terminate()
|
||||||
|
|
||||||
// Create a small enough block chain to download
|
// Create a small enough block chain to download
|
||||||
targetBlocks := blockCacheLimit - 15
|
targetBlocks := blockCacheItems - 15
|
||||||
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
||||||
|
|
||||||
// Set a sync init hook to catch progress changes
|
// Set a sync init hook to catch progress changes
|
||||||
|
|
@ -1609,7 +1595,7 @@ func testFakedSyncProgress(t *testing.T, protocol int, mode SyncMode) {
|
||||||
defer tester.terminate()
|
defer tester.terminate()
|
||||||
|
|
||||||
// Create a small block chain
|
// Create a small block chain
|
||||||
targetBlocks := blockCacheLimit - 15
|
targetBlocks := blockCacheItems - 15
|
||||||
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks+3, 0, tester.genesis, nil, false)
|
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks+3, 0, tester.genesis, nil, false)
|
||||||
|
|
||||||
// Set a sync init hook to catch progress changes
|
// Set a sync init hook to catch progress changes
|
||||||
|
|
@ -1697,6 +1683,7 @@ func TestDeliverHeadersHang(t *testing.T) {
|
||||||
type floodingTestPeer struct {
|
type floodingTestPeer struct {
|
||||||
peer Peer
|
peer Peer
|
||||||
tester *downloadTester
|
tester *downloadTester
|
||||||
|
pend sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ftp *floodingTestPeer) Head() (common.Hash, *big.Int) { return ftp.peer.Head() }
|
func (ftp *floodingTestPeer) Head() (common.Hash, *big.Int) { return ftp.peer.Head() }
|
||||||
|
|
@ -1717,9 +1704,12 @@ func (ftp *floodingTestPeer) RequestHeadersByNumber(from uint64, count, skip int
|
||||||
deliveriesDone := make(chan struct{}, 500)
|
deliveriesDone := make(chan struct{}, 500)
|
||||||
for i := 0; i < cap(deliveriesDone); i++ {
|
for i := 0; i < cap(deliveriesDone); i++ {
|
||||||
peer := fmt.Sprintf("fake-peer%d", i)
|
peer := fmt.Sprintf("fake-peer%d", i)
|
||||||
|
ftp.pend.Add(1)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
ftp.tester.downloader.DeliverHeaders(peer, []*types.Header{{}, {}, {}, {}})
|
ftp.tester.downloader.DeliverHeaders(peer, []*types.Header{{}, {}, {}, {}})
|
||||||
deliveriesDone <- struct{}{}
|
deliveriesDone <- struct{}{}
|
||||||
|
ftp.pend.Done()
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
// Deliver the actual requested headers.
|
// Deliver the actual requested headers.
|
||||||
|
|
@ -1751,110 +1741,15 @@ func testDeliverHeadersHang(t *testing.T, protocol int, mode SyncMode) {
|
||||||
// Whenever the downloader requests headers, flood it with
|
// Whenever the downloader requests headers, flood it with
|
||||||
// a lot of unrequested header deliveries.
|
// a lot of unrequested header deliveries.
|
||||||
tester.downloader.peers.peers["peer"].peer = &floodingTestPeer{
|
tester.downloader.peers.peers["peer"].peer = &floodingTestPeer{
|
||||||
tester.downloader.peers.peers["peer"].peer,
|
peer: tester.downloader.peers.peers["peer"].peer,
|
||||||
tester,
|
tester: tester,
|
||||||
}
|
}
|
||||||
if err := tester.sync("peer", nil, mode); err != nil {
|
if err := tester.sync("peer", nil, mode); err != nil {
|
||||||
t.Errorf("sync failed: %v", err)
|
t.Errorf("test %d: sync failed: %v", i, err)
|
||||||
}
|
}
|
||||||
tester.terminate()
|
tester.terminate()
|
||||||
|
|
||||||
|
// Flush all goroutines to prevent messing with subsequent tests
|
||||||
|
tester.downloader.peers.peers["peer"].peer.(*floodingTestPeer).pend.Wait()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that if fast sync aborts in the critical section, it can restart a few
|
|
||||||
// times before giving up.
|
|
||||||
// We use data driven subtests to manage this so that it will be parallel on its own
|
|
||||||
// and not with the other tests, avoiding intermittent failures.
|
|
||||||
func TestFastCriticalRestarts(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
protocol int
|
|
||||||
progress bool
|
|
||||||
}{
|
|
||||||
{63, false},
|
|
||||||
{64, false},
|
|
||||||
{63, true},
|
|
||||||
{64, true},
|
|
||||||
}
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(fmt.Sprintf("protocol %d progress %v", tc.protocol, tc.progress), func(t *testing.T) {
|
|
||||||
testFastCriticalRestarts(t, tc.protocol, tc.progress)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testFastCriticalRestarts(t *testing.T, protocol int, progress bool) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tester := newTester()
|
|
||||||
defer tester.terminate()
|
|
||||||
|
|
||||||
// Create a large enough blockchin to actually fast sync on
|
|
||||||
targetBlocks := fsMinFullBlocks + 2*fsPivotInterval - 15
|
|
||||||
hashes, headers, blocks, receipts := tester.makeChain(targetBlocks, 0, tester.genesis, nil, false)
|
|
||||||
|
|
||||||
// Create a tester peer with a critical section header missing (force failures)
|
|
||||||
tester.newPeer("peer", protocol, hashes, headers, blocks, receipts)
|
|
||||||
delete(tester.peerHeaders["peer"], hashes[fsMinFullBlocks-1])
|
|
||||||
tester.downloader.dropPeer = func(id string) {} // We reuse the same "faulty" peer throughout the test
|
|
||||||
|
|
||||||
// Remove all possible pivot state roots and slow down replies (test failure resets later)
|
|
||||||
for i := 0; i < fsPivotInterval; i++ {
|
|
||||||
tester.peerMissingStates["peer"][headers[hashes[fsMinFullBlocks+i]].Root] = true
|
|
||||||
}
|
|
||||||
(tester.downloader.peers.peers["peer"].peer).(*downloadTesterPeer).setDelay(500 * time.Millisecond) // Enough to reach the critical section
|
|
||||||
|
|
||||||
// Synchronise with the peer a few times and make sure they fail until the retry limit
|
|
||||||
for i := 0; i < int(fsCriticalTrials)-1; i++ {
|
|
||||||
// Attempt a sync and ensure it fails properly
|
|
||||||
if err := tester.sync("peer", nil, FastSync); err == nil {
|
|
||||||
t.Fatalf("failing fast sync succeeded: %v", err)
|
|
||||||
}
|
|
||||||
time.Sleep(150 * time.Millisecond) // Make sure no in-flight requests remain
|
|
||||||
|
|
||||||
// If it's the first failure, pivot should be locked => reenable all others to detect pivot changes
|
|
||||||
if i == 0 {
|
|
||||||
time.Sleep(150 * time.Millisecond) // Make sure no in-flight requests remain
|
|
||||||
if tester.downloader.fsPivotLock == nil {
|
|
||||||
time.Sleep(400 * time.Millisecond) // Make sure the first huge timeout expires too
|
|
||||||
t.Fatalf("pivot block not locked in after critical section failure")
|
|
||||||
}
|
|
||||||
tester.lock.Lock()
|
|
||||||
tester.peerHeaders["peer"][hashes[fsMinFullBlocks-1]] = headers[hashes[fsMinFullBlocks-1]]
|
|
||||||
tester.peerMissingStates["peer"] = map[common.Hash]bool{tester.downloader.fsPivotLock.Root: true}
|
|
||||||
(tester.downloader.peers.peers["peer"].peer).(*downloadTesterPeer).setDelay(0)
|
|
||||||
tester.lock.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Return all nodes if we're testing fast sync progression
|
|
||||||
if progress {
|
|
||||||
tester.lock.Lock()
|
|
||||||
tester.peerMissingStates["peer"] = map[common.Hash]bool{}
|
|
||||||
tester.lock.Unlock()
|
|
||||||
|
|
||||||
if err := tester.sync("peer", nil, FastSync); err != nil {
|
|
||||||
t.Fatalf("failed to synchronise blocks in progressed fast sync: %v", err)
|
|
||||||
}
|
|
||||||
time.Sleep(150 * time.Millisecond) // Make sure no in-flight requests remain
|
|
||||||
|
|
||||||
if fails := atomic.LoadUint32(&tester.downloader.fsPivotFails); fails != 1 {
|
|
||||||
t.Fatalf("progressed pivot trial count mismatch: have %v, want %v", fails, 1)
|
|
||||||
}
|
|
||||||
assertOwnChain(t, tester, targetBlocks+1)
|
|
||||||
} else {
|
|
||||||
if err := tester.sync("peer", nil, FastSync); err == nil {
|
|
||||||
t.Fatalf("succeeded to synchronise blocks in failed fast sync")
|
|
||||||
}
|
|
||||||
time.Sleep(150 * time.Millisecond) // Make sure no in-flight requests remain
|
|
||||||
|
|
||||||
if fails := atomic.LoadUint32(&tester.downloader.fsPivotFails); fails != fsCriticalTrials {
|
|
||||||
t.Fatalf("failed pivot trial count mismatch: have %v, want %v", fails, fsCriticalTrials)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Retry limit exhausted, downloader will switch to full sync, should succeed
|
|
||||||
if err := tester.sync("peer", nil, FastSync); err != nil {
|
|
||||||
t.Fatalf("failed to synchronise blocks in slow sync: %v", err)
|
|
||||||
}
|
|
||||||
// Note, we can't assert the chain here because the test asserter assumes sync
|
|
||||||
// completed using a single mode of operation, whereas fast-then-slow can result
|
|
||||||
// in arbitrary intermediate state that's not cleanly verifiable.
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,11 @@ import (
|
||||||
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
|
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
|
||||||
)
|
)
|
||||||
|
|
||||||
var blockCacheLimit = 8192 // Maximum number of blocks to cache before throttling the download
|
var (
|
||||||
|
blockCacheItems = 8192 // Maximum number of blocks to cache before throttling the download
|
||||||
|
blockCacheMemory = 64 * 1024 * 1024 // Maximum amount of memory to use for block caching
|
||||||
|
blockCacheSizeWeight = 0.1 // Multiplier to approximate the average block size based on past ones
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errNoFetchesPending = errors.New("no fetches pending")
|
errNoFetchesPending = errors.New("no fetches pending")
|
||||||
|
|
@ -41,17 +45,17 @@ var (
|
||||||
|
|
||||||
// fetchRequest is a currently running data retrieval operation.
|
// fetchRequest is a currently running data retrieval operation.
|
||||||
type fetchRequest struct {
|
type fetchRequest struct {
|
||||||
Peer *peerConnection // Peer to which the request was sent
|
Peer *peerConnection // Peer to which the request was sent
|
||||||
From uint64 // [eth/62] Requested chain element index (used for skeleton fills only)
|
From uint64 // [eth/62] Requested chain element index (used for skeleton fills only)
|
||||||
Hashes map[common.Hash]int // [eth/61] Requested hashes with their insertion index (priority)
|
Headers []*types.Header // [eth/62] Requested headers, sorted by request order
|
||||||
Headers []*types.Header // [eth/62] Requested headers, sorted by request order
|
Time time.Time // Time when the request was made
|
||||||
Time time.Time // Time when the request was made
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// fetchResult is a struct collecting partial results from data fetchers until
|
// fetchResult is a struct collecting partial results from data fetchers until
|
||||||
// all outstanding pieces complete and the result as a whole can be processed.
|
// all outstanding pieces complete and the result as a whole can be processed.
|
||||||
type fetchResult struct {
|
type fetchResult struct {
|
||||||
Pending int // Number of data fetches still pending
|
Pending int // Number of data fetches still pending
|
||||||
|
Hash common.Hash // Hash of the header to prevent recalculating
|
||||||
|
|
||||||
Header *types.Header
|
Header *types.Header
|
||||||
Uncles []*types.Header
|
Uncles []*types.Header
|
||||||
|
|
@ -61,12 +65,10 @@ type fetchResult struct {
|
||||||
|
|
||||||
// queue represents hashes that are either need fetching or are being fetched
|
// queue represents hashes that are either need fetching or are being fetched
|
||||||
type queue struct {
|
type queue struct {
|
||||||
mode SyncMode // Synchronisation mode to decide on the block parts to schedule for fetching
|
mode SyncMode // Synchronisation mode to decide on the block parts to schedule for fetching
|
||||||
fastSyncPivot uint64 // Block number where the fast sync pivots into archive synchronisation mode
|
|
||||||
|
|
||||||
headerHead common.Hash // [eth/62] Hash of the last queued header to verify order
|
|
||||||
|
|
||||||
// Headers are "special", they download in batches, supported by a skeleton chain
|
// Headers are "special", they download in batches, supported by a skeleton chain
|
||||||
|
headerHead common.Hash // [eth/62] Hash of the last queued header to verify order
|
||||||
headerTaskPool map[uint64]*types.Header // [eth/62] Pending header retrieval tasks, mapping starting indexes to skeleton headers
|
headerTaskPool map[uint64]*types.Header // [eth/62] Pending header retrieval tasks, mapping starting indexes to skeleton headers
|
||||||
headerTaskQueue *prque.Prque // [eth/62] Priority queue of the skeleton indexes to fetch the filling headers for
|
headerTaskQueue *prque.Prque // [eth/62] Priority queue of the skeleton indexes to fetch the filling headers for
|
||||||
headerPeerMiss map[string]map[uint64]struct{} // [eth/62] Set of per-peer header batches known to be unavailable
|
headerPeerMiss map[string]map[uint64]struct{} // [eth/62] Set of per-peer header batches known to be unavailable
|
||||||
|
|
@ -87,8 +89,9 @@ type queue struct {
|
||||||
receiptPendPool map[string]*fetchRequest // [eth/63] Currently pending receipt retrieval operations
|
receiptPendPool map[string]*fetchRequest // [eth/63] Currently pending receipt retrieval operations
|
||||||
receiptDonePool map[common.Hash]struct{} // [eth/63] Set of the completed receipt fetches
|
receiptDonePool map[common.Hash]struct{} // [eth/63] Set of the completed receipt fetches
|
||||||
|
|
||||||
resultCache []*fetchResult // Downloaded but not yet delivered fetch results
|
resultCache []*fetchResult // Downloaded but not yet delivered fetch results
|
||||||
resultOffset uint64 // Offset of the first cached fetch result in the block chain
|
resultOffset uint64 // Offset of the first cached fetch result in the block chain
|
||||||
|
resultSize common.StorageSize // Approximate size of a block (exponential moving average)
|
||||||
|
|
||||||
lock *sync.Mutex
|
lock *sync.Mutex
|
||||||
active *sync.Cond
|
active *sync.Cond
|
||||||
|
|
@ -109,7 +112,7 @@ func newQueue() *queue {
|
||||||
receiptTaskQueue: prque.New(),
|
receiptTaskQueue: prque.New(),
|
||||||
receiptPendPool: make(map[string]*fetchRequest),
|
receiptPendPool: make(map[string]*fetchRequest),
|
||||||
receiptDonePool: make(map[common.Hash]struct{}),
|
receiptDonePool: make(map[common.Hash]struct{}),
|
||||||
resultCache: make([]*fetchResult, blockCacheLimit),
|
resultCache: make([]*fetchResult, blockCacheItems),
|
||||||
active: sync.NewCond(lock),
|
active: sync.NewCond(lock),
|
||||||
lock: lock,
|
lock: lock,
|
||||||
}
|
}
|
||||||
|
|
@ -122,10 +125,8 @@ func (q *queue) Reset() {
|
||||||
|
|
||||||
q.closed = false
|
q.closed = false
|
||||||
q.mode = FullSync
|
q.mode = FullSync
|
||||||
q.fastSyncPivot = 0
|
|
||||||
|
|
||||||
q.headerHead = common.Hash{}
|
q.headerHead = common.Hash{}
|
||||||
|
|
||||||
q.headerPendPool = make(map[string]*fetchRequest)
|
q.headerPendPool = make(map[string]*fetchRequest)
|
||||||
|
|
||||||
q.blockTaskPool = make(map[common.Hash]*types.Header)
|
q.blockTaskPool = make(map[common.Hash]*types.Header)
|
||||||
|
|
@ -138,7 +139,7 @@ func (q *queue) Reset() {
|
||||||
q.receiptPendPool = make(map[string]*fetchRequest)
|
q.receiptPendPool = make(map[string]*fetchRequest)
|
||||||
q.receiptDonePool = make(map[common.Hash]struct{})
|
q.receiptDonePool = make(map[common.Hash]struct{})
|
||||||
|
|
||||||
q.resultCache = make([]*fetchResult, blockCacheLimit)
|
q.resultCache = make([]*fetchResult, blockCacheItems)
|
||||||
q.resultOffset = 0
|
q.resultOffset = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -214,27 +215,13 @@ func (q *queue) Idle() bool {
|
||||||
return (queued + pending + cached) == 0
|
return (queued + pending + cached) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// FastSyncPivot retrieves the currently used fast sync pivot point.
|
|
||||||
func (q *queue) FastSyncPivot() uint64 {
|
|
||||||
q.lock.Lock()
|
|
||||||
defer q.lock.Unlock()
|
|
||||||
|
|
||||||
return q.fastSyncPivot
|
|
||||||
}
|
|
||||||
|
|
||||||
// ShouldThrottleBlocks checks if the download should be throttled (active block (body)
|
// ShouldThrottleBlocks checks if the download should be throttled (active block (body)
|
||||||
// fetches exceed block cache).
|
// fetches exceed block cache).
|
||||||
func (q *queue) ShouldThrottleBlocks() bool {
|
func (q *queue) ShouldThrottleBlocks() bool {
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
defer q.lock.Unlock()
|
defer q.lock.Unlock()
|
||||||
|
|
||||||
// Calculate the currently in-flight block (body) requests
|
return q.resultSlots(q.blockPendPool, q.blockDonePool) <= 0
|
||||||
pending := 0
|
|
||||||
for _, request := range q.blockPendPool {
|
|
||||||
pending += len(request.Hashes) + len(request.Headers)
|
|
||||||
}
|
|
||||||
// Throttle if more blocks (bodies) are in-flight than free space in the cache
|
|
||||||
return pending >= len(q.resultCache)-len(q.blockDonePool)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShouldThrottleReceipts checks if the download should be throttled (active receipt
|
// ShouldThrottleReceipts checks if the download should be throttled (active receipt
|
||||||
|
|
@ -243,13 +230,39 @@ func (q *queue) ShouldThrottleReceipts() bool {
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
defer q.lock.Unlock()
|
defer q.lock.Unlock()
|
||||||
|
|
||||||
// Calculate the currently in-flight receipt requests
|
return q.resultSlots(q.receiptPendPool, q.receiptDonePool) <= 0
|
||||||
pending := 0
|
}
|
||||||
for _, request := range q.receiptPendPool {
|
|
||||||
pending += len(request.Headers)
|
// resultSlots calculates the number of results slots available for requests
|
||||||
|
// whilst adhering to both the item and the memory limit too of the results
|
||||||
|
// cache.
|
||||||
|
func (q *queue) resultSlots(pendPool map[string]*fetchRequest, donePool map[common.Hash]struct{}) int {
|
||||||
|
// Calculate the maximum length capped by the memory limit
|
||||||
|
limit := len(q.resultCache)
|
||||||
|
if common.StorageSize(len(q.resultCache))*q.resultSize > common.StorageSize(blockCacheMemory) {
|
||||||
|
limit = int((common.StorageSize(blockCacheMemory) + q.resultSize - 1) / q.resultSize)
|
||||||
}
|
}
|
||||||
// Throttle if more receipts are in-flight than free space in the cache
|
// Calculate the number of slots already finished
|
||||||
return pending >= len(q.resultCache)-len(q.receiptDonePool)
|
finished := 0
|
||||||
|
for _, result := range q.resultCache[:limit] {
|
||||||
|
if result == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if _, ok := donePool[result.Hash]; ok {
|
||||||
|
finished++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Calculate the number of slots currently downloading
|
||||||
|
pending := 0
|
||||||
|
for _, request := range pendPool {
|
||||||
|
for _, header := range request.Headers {
|
||||||
|
if header.Number.Uint64() < q.resultOffset+uint64(limit) {
|
||||||
|
pending++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Return the free slots to distribute
|
||||||
|
return limit - finished - pending
|
||||||
}
|
}
|
||||||
|
|
||||||
// ScheduleSkeleton adds a batch of header retrieval tasks to the queue to fill
|
// ScheduleSkeleton adds a batch of header retrieval tasks to the queue to fill
|
||||||
|
|
@ -323,8 +336,7 @@ func (q *queue) Schedule(headers []*types.Header, from uint64) []*types.Header {
|
||||||
q.blockTaskPool[hash] = header
|
q.blockTaskPool[hash] = header
|
||||||
q.blockTaskQueue.Push(header, -float32(header.Number.Uint64()))
|
q.blockTaskQueue.Push(header, -float32(header.Number.Uint64()))
|
||||||
|
|
||||||
if q.mode == FastSync && header.Number.Uint64() <= q.fastSyncPivot {
|
if q.mode == FastSync {
|
||||||
// Fast phase of the fast sync, retrieve receipts too
|
|
||||||
q.receiptTaskPool[hash] = header
|
q.receiptTaskPool[hash] = header
|
||||||
q.receiptTaskQueue.Push(header, -float32(header.Number.Uint64()))
|
q.receiptTaskQueue.Push(header, -float32(header.Number.Uint64()))
|
||||||
}
|
}
|
||||||
|
|
@ -335,18 +347,25 @@ func (q *queue) Schedule(headers []*types.Header, from uint64) []*types.Header {
|
||||||
return inserts
|
return inserts
|
||||||
}
|
}
|
||||||
|
|
||||||
// WaitResults retrieves and permanently removes a batch of fetch
|
// Results retrieves and permanently removes a batch of fetch results from
|
||||||
// results from the cache. the result slice will be empty if the queue
|
// the cache. the result slice will be empty if the queue has been closed.
|
||||||
// has been closed.
|
func (q *queue) Results(block bool) []*fetchResult {
|
||||||
func (q *queue) WaitResults() []*fetchResult {
|
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
defer q.lock.Unlock()
|
defer q.lock.Unlock()
|
||||||
|
|
||||||
|
// Count the number of items available for processing
|
||||||
nproc := q.countProcessableItems()
|
nproc := q.countProcessableItems()
|
||||||
for nproc == 0 && !q.closed {
|
for nproc == 0 && !q.closed {
|
||||||
|
if !block {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
q.active.Wait()
|
q.active.Wait()
|
||||||
nproc = q.countProcessableItems()
|
nproc = q.countProcessableItems()
|
||||||
}
|
}
|
||||||
|
// Since we have a batch limit, don't pull more into "dangling" memory
|
||||||
|
if nproc > maxResultsProcess {
|
||||||
|
nproc = maxResultsProcess
|
||||||
|
}
|
||||||
results := make([]*fetchResult, nproc)
|
results := make([]*fetchResult, nproc)
|
||||||
copy(results, q.resultCache[:nproc])
|
copy(results, q.resultCache[:nproc])
|
||||||
if len(results) > 0 {
|
if len(results) > 0 {
|
||||||
|
|
@ -363,6 +382,21 @@ func (q *queue) WaitResults() []*fetchResult {
|
||||||
}
|
}
|
||||||
// Advance the expected block number of the first cache entry.
|
// Advance the expected block number of the first cache entry.
|
||||||
q.resultOffset += uint64(nproc)
|
q.resultOffset += uint64(nproc)
|
||||||
|
|
||||||
|
// Recalculate the result item weights to prevent memory exhaustion
|
||||||
|
for _, result := range results {
|
||||||
|
size := result.Header.Size()
|
||||||
|
for _, uncle := range result.Uncles {
|
||||||
|
size += uncle.Size()
|
||||||
|
}
|
||||||
|
for _, receipt := range result.Receipts {
|
||||||
|
size += receipt.Size()
|
||||||
|
}
|
||||||
|
for _, tx := range result.Transactions {
|
||||||
|
size += tx.Size()
|
||||||
|
}
|
||||||
|
q.resultSize = common.StorageSize(blockCacheSizeWeight)*size + (1-common.StorageSize(blockCacheSizeWeight))*q.resultSize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
@ -370,21 +404,9 @@ func (q *queue) WaitResults() []*fetchResult {
|
||||||
// countProcessableItems counts the processable items.
|
// countProcessableItems counts the processable items.
|
||||||
func (q *queue) countProcessableItems() int {
|
func (q *queue) countProcessableItems() int {
|
||||||
for i, result := range q.resultCache {
|
for i, result := range q.resultCache {
|
||||||
// Don't process incomplete or unavailable items.
|
|
||||||
if result == nil || result.Pending > 0 {
|
if result == nil || result.Pending > 0 {
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
// Stop before processing the pivot block to ensure that
|
|
||||||
// resultCache has space for fsHeaderForceVerify items. Not
|
|
||||||
// doing this could leave us unable to download the required
|
|
||||||
// amount of headers.
|
|
||||||
if q.mode == FastSync && result.Header.Number.Uint64() == q.fastSyncPivot {
|
|
||||||
for j := 0; j < fsHeaderForceVerify; j++ {
|
|
||||||
if i+j+1 >= len(q.resultCache) || q.resultCache[i+j+1] == nil {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return len(q.resultCache)
|
return len(q.resultCache)
|
||||||
}
|
}
|
||||||
|
|
@ -473,10 +495,8 @@ func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common
|
||||||
return nil, false, nil
|
return nil, false, nil
|
||||||
}
|
}
|
||||||
// Calculate an upper limit on the items we might fetch (i.e. throttling)
|
// Calculate an upper limit on the items we might fetch (i.e. throttling)
|
||||||
space := len(q.resultCache) - len(donePool)
|
space := q.resultSlots(pendPool, donePool)
|
||||||
for _, request := range pendPool {
|
|
||||||
space -= len(request.Headers)
|
|
||||||
}
|
|
||||||
// Retrieve a batch of tasks, skipping previously failed ones
|
// Retrieve a batch of tasks, skipping previously failed ones
|
||||||
send := make([]*types.Header, 0, count)
|
send := make([]*types.Header, 0, count)
|
||||||
skip := make([]*types.Header, 0)
|
skip := make([]*types.Header, 0)
|
||||||
|
|
@ -484,6 +504,7 @@ func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common
|
||||||
progress := false
|
progress := false
|
||||||
for proc := 0; proc < space && len(send) < count && !taskQueue.Empty(); proc++ {
|
for proc := 0; proc < space && len(send) < count && !taskQueue.Empty(); proc++ {
|
||||||
header := taskQueue.PopItem().(*types.Header)
|
header := taskQueue.PopItem().(*types.Header)
|
||||||
|
hash := header.Hash()
|
||||||
|
|
||||||
// If we're the first to request this task, initialise the result container
|
// If we're the first to request this task, initialise the result container
|
||||||
index := int(header.Number.Int64() - int64(q.resultOffset))
|
index := int(header.Number.Int64() - int64(q.resultOffset))
|
||||||
|
|
@ -493,18 +514,19 @@ func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common
|
||||||
}
|
}
|
||||||
if q.resultCache[index] == nil {
|
if q.resultCache[index] == nil {
|
||||||
components := 1
|
components := 1
|
||||||
if q.mode == FastSync && header.Number.Uint64() <= q.fastSyncPivot {
|
if q.mode == FastSync {
|
||||||
components = 2
|
components = 2
|
||||||
}
|
}
|
||||||
q.resultCache[index] = &fetchResult{
|
q.resultCache[index] = &fetchResult{
|
||||||
Pending: components,
|
Pending: components,
|
||||||
|
Hash: hash,
|
||||||
Header: header,
|
Header: header,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If this fetch task is a noop, skip this fetch operation
|
// If this fetch task is a noop, skip this fetch operation
|
||||||
if isNoop(header) {
|
if isNoop(header) {
|
||||||
donePool[header.Hash()] = struct{}{}
|
donePool[hash] = struct{}{}
|
||||||
delete(taskPool, header.Hash())
|
delete(taskPool, hash)
|
||||||
|
|
||||||
space, proc = space-1, proc-1
|
space, proc = space-1, proc-1
|
||||||
q.resultCache[index].Pending--
|
q.resultCache[index].Pending--
|
||||||
|
|
@ -512,7 +534,7 @@ func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Otherwise unless the peer is known not to have the data, add to the retrieve list
|
// Otherwise unless the peer is known not to have the data, add to the retrieve list
|
||||||
if p.Lacks(header.Hash()) {
|
if p.Lacks(hash) {
|
||||||
skip = append(skip, header)
|
skip = append(skip, header)
|
||||||
} else {
|
} else {
|
||||||
send = append(send, header)
|
send = append(send, header)
|
||||||
|
|
@ -565,9 +587,6 @@ func (q *queue) cancel(request *fetchRequest, taskQueue *prque.Prque, pendPool m
|
||||||
if request.From > 0 {
|
if request.From > 0 {
|
||||||
taskQueue.Push(request.From, -float32(request.From))
|
taskQueue.Push(request.From, -float32(request.From))
|
||||||
}
|
}
|
||||||
for hash, index := range request.Hashes {
|
|
||||||
taskQueue.Push(hash, float32(index))
|
|
||||||
}
|
|
||||||
for _, header := range request.Headers {
|
for _, header := range request.Headers {
|
||||||
taskQueue.Push(header, -float32(header.Number.Uint64()))
|
taskQueue.Push(header, -float32(header.Number.Uint64()))
|
||||||
}
|
}
|
||||||
|
|
@ -640,18 +659,11 @@ func (q *queue) expire(timeout time.Duration, pendPool map[string]*fetchRequest,
|
||||||
if request.From > 0 {
|
if request.From > 0 {
|
||||||
taskQueue.Push(request.From, -float32(request.From))
|
taskQueue.Push(request.From, -float32(request.From))
|
||||||
}
|
}
|
||||||
for hash, index := range request.Hashes {
|
|
||||||
taskQueue.Push(hash, float32(index))
|
|
||||||
}
|
|
||||||
for _, header := range request.Headers {
|
for _, header := range request.Headers {
|
||||||
taskQueue.Push(header, -float32(header.Number.Uint64()))
|
taskQueue.Push(header, -float32(header.Number.Uint64()))
|
||||||
}
|
}
|
||||||
// Add the peer to the expiry report along the the number of failed requests
|
// Add the peer to the expiry report along the the number of failed requests
|
||||||
expirations := len(request.Hashes)
|
expiries[id] = len(request.Headers)
|
||||||
if expirations < len(request.Headers) {
|
|
||||||
expirations = len(request.Headers)
|
|
||||||
}
|
|
||||||
expiries[id] = expirations
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Remove the expired requests from the pending pool
|
// Remove the expired requests from the pending pool
|
||||||
|
|
@ -828,14 +840,16 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQ
|
||||||
failure = err
|
failure = err
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
donePool[header.Hash()] = struct{}{}
|
hash := header.Hash()
|
||||||
|
|
||||||
|
donePool[hash] = struct{}{}
|
||||||
q.resultCache[index].Pending--
|
q.resultCache[index].Pending--
|
||||||
useful = true
|
useful = true
|
||||||
accepted++
|
accepted++
|
||||||
|
|
||||||
// Clean up a successful fetch
|
// Clean up a successful fetch
|
||||||
request.Headers[i] = nil
|
request.Headers[i] = nil
|
||||||
delete(taskPool, header.Hash())
|
delete(taskPool, hash)
|
||||||
}
|
}
|
||||||
// Return all failed or missing fetches to the queue
|
// Return all failed or missing fetches to the queue
|
||||||
for _, header := range request.Headers {
|
for _, header := range request.Headers {
|
||||||
|
|
@ -860,7 +874,7 @@ func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQ
|
||||||
|
|
||||||
// Prepare configures the result cache to allow accepting and caching inbound
|
// Prepare configures the result cache to allow accepting and caching inbound
|
||||||
// fetch results.
|
// fetch results.
|
||||||
func (q *queue) Prepare(offset uint64, mode SyncMode, pivot uint64, head *types.Header) {
|
func (q *queue) Prepare(offset uint64, mode SyncMode) {
|
||||||
q.lock.Lock()
|
q.lock.Lock()
|
||||||
defer q.lock.Unlock()
|
defer q.lock.Unlock()
|
||||||
|
|
||||||
|
|
@ -868,6 +882,5 @@ func (q *queue) Prepare(offset uint64, mode SyncMode, pivot uint64, head *types.
|
||||||
if q.resultOffset < offset {
|
if q.resultOffset < offset {
|
||||||
q.resultOffset = offset
|
q.resultOffset = offset
|
||||||
}
|
}
|
||||||
q.fastSyncPivot = pivot
|
|
||||||
q.mode = mode
|
q.mode = mode
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/EthereumCommonwealth/go-callisto/common"
|
"github.com/EthereumCommonwealth/go-callisto/common"
|
||||||
|
|
@ -294,6 +293,9 @@ func (s *stateSync) loop() error {
|
||||||
case <-s.cancel:
|
case <-s.cancel:
|
||||||
return errCancelStateFetch
|
return errCancelStateFetch
|
||||||
|
|
||||||
|
case <-s.d.cancelCh:
|
||||||
|
return errCancelStateFetch
|
||||||
|
|
||||||
case req := <-s.deliver:
|
case req := <-s.deliver:
|
||||||
// Response, disconnect or timeout triggered, drop the peer if stalling
|
// Response, disconnect or timeout triggered, drop the peer if stalling
|
||||||
log.Trace("Received node data response", "peer", req.peer.id, "count", len(req.response), "dropped", req.dropped, "timeout", !req.dropped && req.timedOut())
|
log.Trace("Received node data response", "peer", req.peer.id, "count", len(req.response), "dropped", req.dropped, "timeout", !req.dropped && req.timedOut())
|
||||||
|
|
@ -304,15 +306,11 @@ func (s *stateSync) loop() error {
|
||||||
s.d.dropPeer(req.peer.id)
|
s.d.dropPeer(req.peer.id)
|
||||||
}
|
}
|
||||||
// Process all the received blobs and check for stale delivery
|
// Process all the received blobs and check for stale delivery
|
||||||
stale, err := s.process(req)
|
if err := s.process(req); err != nil {
|
||||||
if err != nil {
|
|
||||||
log.Warn("Node data write error", "err", err)
|
log.Warn("Node data write error", "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// The the delivery contains requested data, mark the node idle (otherwise it's a timed out delivery)
|
req.peer.SetNodeDataIdle(len(req.response))
|
||||||
if !stale {
|
|
||||||
req.peer.SetNodeDataIdle(len(req.response))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return s.commit(true)
|
return s.commit(true)
|
||||||
|
|
@ -352,6 +350,7 @@ func (s *stateSync) assignTasks() {
|
||||||
case s.d.trackStateReq <- req:
|
case s.d.trackStateReq <- req:
|
||||||
req.peer.FetchNodeData(req.items)
|
req.peer.FetchNodeData(req.items)
|
||||||
case <-s.cancel:
|
case <-s.cancel:
|
||||||
|
case <-s.d.cancelCh:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -390,7 +389,7 @@ func (s *stateSync) fillTasks(n int, req *stateReq) {
|
||||||
// process iterates over a batch of delivered state data, injecting each item
|
// process iterates over a batch of delivered state data, injecting each item
|
||||||
// into a running state sync, re-queuing any items that were requested but not
|
// into a running state sync, re-queuing any items that were requested but not
|
||||||
// delivered.
|
// delivered.
|
||||||
func (s *stateSync) process(req *stateReq) (bool, error) {
|
func (s *stateSync) process(req *stateReq) error {
|
||||||
// Collect processing stats and update progress if valid data was received
|
// Collect processing stats and update progress if valid data was received
|
||||||
duplicate, unexpected := 0, 0
|
duplicate, unexpected := 0, 0
|
||||||
|
|
||||||
|
|
@ -401,7 +400,7 @@ func (s *stateSync) process(req *stateReq) (bool, error) {
|
||||||
}(time.Now())
|
}(time.Now())
|
||||||
|
|
||||||
// Iterate over all the delivered data and inject one-by-one into the trie
|
// Iterate over all the delivered data and inject one-by-one into the trie
|
||||||
progress, stale := false, len(req.response) > 0
|
progress := false
|
||||||
|
|
||||||
for _, blob := range req.response {
|
for _, blob := range req.response {
|
||||||
prog, hash, err := s.processNodeData(blob)
|
prog, hash, err := s.processNodeData(blob)
|
||||||
|
|
@ -415,20 +414,12 @@ func (s *stateSync) process(req *stateReq) (bool, error) {
|
||||||
case trie.ErrAlreadyProcessed:
|
case trie.ErrAlreadyProcessed:
|
||||||
duplicate++
|
duplicate++
|
||||||
default:
|
default:
|
||||||
return stale, fmt.Errorf("invalid state node %s: %v", hash.TerminalString(), err)
|
return fmt.Errorf("invalid state node %s: %v", hash.TerminalString(), err)
|
||||||
}
|
}
|
||||||
// If the node delivered a requested item, mark the delivery non-stale
|
|
||||||
if _, ok := req.tasks[hash]; ok {
|
if _, ok := req.tasks[hash]; ok {
|
||||||
delete(req.tasks, hash)
|
delete(req.tasks, hash)
|
||||||
stale = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If we're inside the critical section, reset fail counter since we progressed.
|
|
||||||
if progress && atomic.LoadUint32(&s.d.fsPivotFails) > 1 {
|
|
||||||
log.Trace("Fast-sync progressed, resetting fail counter", "previous", atomic.LoadUint32(&s.d.fsPivotFails))
|
|
||||||
atomic.StoreUint32(&s.d.fsPivotFails, 1) // Don't ever reset to 0, as that will unlock the pivot block
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put unfulfilled tasks back into the retry queue
|
// Put unfulfilled tasks back into the retry queue
|
||||||
npeers := s.d.peers.Len()
|
npeers := s.d.peers.Len()
|
||||||
for hash, task := range req.tasks {
|
for hash, task := range req.tasks {
|
||||||
|
|
@ -441,12 +432,12 @@ func (s *stateSync) process(req *stateReq) (bool, error) {
|
||||||
// If we've requested the node too many times already, it may be a malicious
|
// If we've requested the node too many times already, it may be a malicious
|
||||||
// sync where nobody has the right data. Abort.
|
// sync where nobody has the right data. Abort.
|
||||||
if len(task.attempts) >= npeers {
|
if len(task.attempts) >= npeers {
|
||||||
return stale, fmt.Errorf("state node %s failed with all peers (%d tries, %d peers)", hash.TerminalString(), len(task.attempts), npeers)
|
return fmt.Errorf("state node %s failed with all peers (%d tries, %d peers)", hash.TerminalString(), len(task.attempts), npeers)
|
||||||
}
|
}
|
||||||
// Missing item, place into the retry queue.
|
// Missing item, place into the retry queue.
|
||||||
s.tasks[hash] = task
|
s.tasks[hash] = task
|
||||||
}
|
}
|
||||||
return stale, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// processNodeData tries to inject a trie node data blob delivered from a remote
|
// processNodeData tries to inject a trie node data blob delivered from a remote
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,6 @@ type ProtocolManager struct {
|
||||||
|
|
||||||
txpool txPool
|
txpool txPool
|
||||||
blockchain *core.BlockChain
|
blockchain *core.BlockChain
|
||||||
chaindb ethdb.Database
|
|
||||||
chainconfig *params.ChainConfig
|
chainconfig *params.ChainConfig
|
||||||
maxPeers int
|
maxPeers int
|
||||||
|
|
||||||
|
|
@ -106,7 +105,6 @@ func NewProtocolManager(config *params.ChainConfig, mode downloader.SyncMode, ne
|
||||||
eventMux: mux,
|
eventMux: mux,
|
||||||
txpool: txpool,
|
txpool: txpool,
|
||||||
blockchain: blockchain,
|
blockchain: blockchain,
|
||||||
chaindb: chaindb,
|
|
||||||
chainconfig: config,
|
chainconfig: config,
|
||||||
peers: newPeerSet(),
|
peers: newPeerSet(),
|
||||||
newPeerCh: make(chan *peer),
|
newPeerCh: make(chan *peer),
|
||||||
|
|
@ -257,8 +255,14 @@ func (pm *ProtocolManager) handle(p *peer) error {
|
||||||
p.Log().Debug("Ethereum peer connected", "name", p.Name())
|
p.Log().Debug("Ethereum peer connected", "name", p.Name())
|
||||||
|
|
||||||
// Execute the Ethereum handshake
|
// Execute the Ethereum handshake
|
||||||
td, head, genesis := pm.blockchain.Status()
|
var (
|
||||||
if err := p.Handshake(pm.networkId, td, head, genesis); err != nil {
|
genesis = pm.blockchain.Genesis()
|
||||||
|
head = pm.blockchain.CurrentHeader()
|
||||||
|
hash = head.Hash()
|
||||||
|
number = head.Number.Uint64()
|
||||||
|
td = pm.blockchain.GetTd(hash, number)
|
||||||
|
)
|
||||||
|
if err := p.Handshake(pm.networkId, td, hash, genesis.Hash()); err != nil {
|
||||||
p.Log().Debug("Ethereum handshake failed", "err", err)
|
p.Log().Debug("Ethereum handshake failed", "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -532,7 +536,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
||||||
}
|
}
|
||||||
// Retrieve the requested state entry, stopping if enough was found
|
// Retrieve the requested state entry, stopping if enough was found
|
||||||
if entry, err := pm.chaindb.Get(hash.Bytes()); err == nil {
|
if entry, err := pm.blockchain.TrieNode(hash); err == nil {
|
||||||
data = append(data, entry)
|
data = append(data, entry)
|
||||||
bytes += len(entry)
|
bytes += len(entry)
|
||||||
}
|
}
|
||||||
|
|
@ -570,7 +574,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
||||||
}
|
}
|
||||||
// Retrieve the requested block's receipts, skipping if unknown to us
|
// Retrieve the requested block's receipts, skipping if unknown to us
|
||||||
results := core.GetBlockReceipts(pm.chaindb, hash, core.GetBlockNumber(pm.chaindb, hash))
|
results := pm.blockchain.GetReceiptsByHash(hash)
|
||||||
if results == nil {
|
if results == nil {
|
||||||
if header := pm.blockchain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash {
|
if header := pm.blockchain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash {
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ func TestProtocolCompatibility(t *testing.T) {
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
ProtocolVersions = []uint{tt.version}
|
ProtocolVersions = []uint{tt.version}
|
||||||
|
|
||||||
pm, err := newTestProtocolManager(tt.mode, 0, nil, nil)
|
pm, _, err := newTestProtocolManager(tt.mode, 0, nil, nil)
|
||||||
if pm != nil {
|
if pm != nil {
|
||||||
defer pm.Stop()
|
defer pm.Stop()
|
||||||
}
|
}
|
||||||
|
|
@ -71,7 +71,7 @@ func TestGetBlockHeaders62(t *testing.T) { testGetBlockHeaders(t, 62) }
|
||||||
func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) }
|
func TestGetBlockHeaders63(t *testing.T) { testGetBlockHeaders(t, 63) }
|
||||||
|
|
||||||
func testGetBlockHeaders(t *testing.T, protocol int) {
|
func testGetBlockHeaders(t *testing.T, protocol int) {
|
||||||
pm := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil)
|
pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxHashFetch+15, nil, nil)
|
||||||
peer, _ := newTestPeer("peer", protocol, pm, true)
|
peer, _ := newTestPeer("peer", protocol, pm, true)
|
||||||
defer peer.close()
|
defer peer.close()
|
||||||
|
|
||||||
|
|
@ -230,7 +230,7 @@ func TestGetBlockBodies62(t *testing.T) { testGetBlockBodies(t, 62) }
|
||||||
func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) }
|
func TestGetBlockBodies63(t *testing.T) { testGetBlockBodies(t, 63) }
|
||||||
|
|
||||||
func testGetBlockBodies(t *testing.T, protocol int) {
|
func testGetBlockBodies(t *testing.T, protocol int) {
|
||||||
pm := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxBlockFetch+15, nil, nil)
|
pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, downloader.MaxBlockFetch+15, nil, nil)
|
||||||
peer, _ := newTestPeer("peer", protocol, pm, true)
|
peer, _ := newTestPeer("peer", protocol, pm, true)
|
||||||
defer peer.close()
|
defer peer.close()
|
||||||
|
|
||||||
|
|
@ -337,13 +337,13 @@ func testGetNodeData(t *testing.T, protocol int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Assemble the test environment
|
// Assemble the test environment
|
||||||
pm := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
|
pm, db := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
|
||||||
peer, _ := newTestPeer("peer", protocol, pm, true)
|
peer, _ := newTestPeer("peer", protocol, pm, true)
|
||||||
defer peer.close()
|
defer peer.close()
|
||||||
|
|
||||||
// Fetch for now the entire chain db
|
// Fetch for now the entire chain db
|
||||||
hashes := []common.Hash{}
|
hashes := []common.Hash{}
|
||||||
for _, key := range pm.chaindb.(*ethdb.MemDatabase).Keys() {
|
for _, key := range db.Keys() {
|
||||||
if len(key) == len(common.Hash{}) {
|
if len(key) == len(common.Hash{}) {
|
||||||
hashes = append(hashes, common.BytesToHash(key))
|
hashes = append(hashes, common.BytesToHash(key))
|
||||||
}
|
}
|
||||||
|
|
@ -429,7 +429,7 @@ func testGetReceipt(t *testing.T, protocol int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Assemble the test environment
|
// Assemble the test environment
|
||||||
pm := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
|
pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 4, generator, nil)
|
||||||
peer, _ := newTestPeer("peer", protocol, pm, true)
|
peer, _ := newTestPeer("peer", protocol, pm, true)
|
||||||
defer peer.close()
|
defer peer.close()
|
||||||
|
|
||||||
|
|
@ -439,7 +439,7 @@ func testGetReceipt(t *testing.T, protocol int) {
|
||||||
block := pm.blockchain.GetBlockByNumber(i)
|
block := pm.blockchain.GetBlockByNumber(i)
|
||||||
|
|
||||||
hashes = append(hashes, block.Hash())
|
hashes = append(hashes, block.Hash())
|
||||||
receipts = append(receipts, core.GetBlockReceipts(pm.chaindb, block.Hash(), block.NumberU64()))
|
receipts = append(receipts, pm.blockchain.GetReceiptsByHash(block.Hash()))
|
||||||
}
|
}
|
||||||
// Send the hash request and verify the response
|
// Send the hash request and verify the response
|
||||||
p2p.Send(peer.app, 0x0f, hashes)
|
p2p.Send(peer.app, 0x0f, hashes)
|
||||||
|
|
@ -472,7 +472,7 @@ func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool
|
||||||
config = ¶ms.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked}
|
config = ¶ms.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked}
|
||||||
gspec = &core.Genesis{Config: config}
|
gspec = &core.Genesis{Config: config}
|
||||||
genesis = gspec.MustCommit(db)
|
genesis = gspec.MustCommit(db)
|
||||||
blockchain, _ = core.NewBlockChain(db, config, pow, vm.Config{})
|
blockchain, _ = core.NewBlockChain(db, nil, config, pow, vm.Config{})
|
||||||
)
|
)
|
||||||
pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db)
|
pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ var (
|
||||||
// newTestProtocolManager creates a new protocol manager for testing purposes,
|
// newTestProtocolManager creates a new protocol manager for testing purposes,
|
||||||
// with the given number of blocks already known, and potential notification
|
// with the given number of blocks already known, and potential notification
|
||||||
// channels for different events.
|
// channels for different events.
|
||||||
func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, error) {
|
func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, *ethdb.MemDatabase, error) {
|
||||||
var (
|
var (
|
||||||
evmux = new(event.TypeMux)
|
evmux = new(event.TypeMux)
|
||||||
engine = ethash.NewFaker()
|
engine = ethash.NewFaker()
|
||||||
|
|
@ -59,7 +59,7 @@ func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func
|
||||||
Alloc: core.GenesisAlloc{testBank: {Balance: big.NewInt(1000000)}},
|
Alloc: core.GenesisAlloc{testBank: {Balance: big.NewInt(1000000)}},
|
||||||
}
|
}
|
||||||
genesis = gspec.MustCommit(db)
|
genesis = gspec.MustCommit(db)
|
||||||
blockchain, _ = core.NewBlockChain(db, gspec.Config, engine, vm.Config{})
|
blockchain, _ = core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{})
|
||||||
)
|
)
|
||||||
chain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator)
|
chain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator)
|
||||||
if _, err := blockchain.InsertChain(chain); err != nil {
|
if _, err := blockchain.InsertChain(chain); err != nil {
|
||||||
|
|
@ -68,22 +68,22 @@ func newTestProtocolManager(mode downloader.SyncMode, blocks int, generator func
|
||||||
|
|
||||||
pm, err := NewProtocolManager(gspec.Config, mode, DefaultConfig.NetworkId, evmux, &testTxPool{added: newtx}, engine, blockchain, db)
|
pm, err := NewProtocolManager(gspec.Config, mode, DefaultConfig.NetworkId, evmux, &testTxPool{added: newtx}, engine, blockchain, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
pm.Start(1000)
|
pm.Start(1000)
|
||||||
return pm, nil
|
return pm, db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// newTestProtocolManagerMust creates a new protocol manager for testing purposes,
|
// newTestProtocolManagerMust creates a new protocol manager for testing purposes,
|
||||||
// with the given number of blocks already known, and potential notification
|
// with the given number of blocks already known, and potential notification
|
||||||
// channels for different events. In case of an error, the constructor force-
|
// channels for different events. In case of an error, the constructor force-
|
||||||
// fails the test.
|
// fails the test.
|
||||||
func newTestProtocolManagerMust(t *testing.T, mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) *ProtocolManager {
|
func newTestProtocolManagerMust(t *testing.T, mode downloader.SyncMode, blocks int, generator func(int, *core.BlockGen), newtx chan<- []*types.Transaction) (*ProtocolManager, *ethdb.MemDatabase) {
|
||||||
pm, err := newTestProtocolManager(mode, blocks, generator, newtx)
|
pm, db, err := newTestProtocolManager(mode, blocks, generator, newtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create protocol manager: %v", err)
|
t.Fatalf("Failed to create protocol manager: %v", err)
|
||||||
}
|
}
|
||||||
return pm
|
return pm, db
|
||||||
}
|
}
|
||||||
|
|
||||||
// testTxPool is a fake, helper transaction pool for testing purposes
|
// testTxPool is a fake, helper transaction pool for testing purposes
|
||||||
|
|
@ -166,8 +166,12 @@ func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*te
|
||||||
tp := &testPeer{app: app, net: net, peer: peer}
|
tp := &testPeer{app: app, net: net, peer: peer}
|
||||||
// Execute any implicitly requested handshakes and return
|
// Execute any implicitly requested handshakes and return
|
||||||
if shake {
|
if shake {
|
||||||
td, head, genesis := pm.blockchain.Status()
|
var (
|
||||||
tp.handshake(nil, td, head, genesis)
|
genesis = pm.blockchain.Genesis()
|
||||||
|
head = pm.blockchain.CurrentHeader()
|
||||||
|
td = pm.blockchain.GetTd(head.Hash(), head.Number.Uint64())
|
||||||
|
)
|
||||||
|
tp.handshake(nil, td, head.Hash(), genesis.Hash())
|
||||||
}
|
}
|
||||||
return tp, errc
|
return tp, errc
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,12 @@ func TestStatusMsgErrors62(t *testing.T) { testStatusMsgErrors(t, 62) }
|
||||||
func TestStatusMsgErrors63(t *testing.T) { testStatusMsgErrors(t, 63) }
|
func TestStatusMsgErrors63(t *testing.T) { testStatusMsgErrors(t, 63) }
|
||||||
|
|
||||||
func testStatusMsgErrors(t *testing.T, protocol int) {
|
func testStatusMsgErrors(t *testing.T, protocol int) {
|
||||||
pm := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil)
|
pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil)
|
||||||
td, currentBlock, genesis := pm.blockchain.Status()
|
var (
|
||||||
|
genesis = pm.blockchain.Genesis()
|
||||||
|
head = pm.blockchain.CurrentHeader()
|
||||||
|
td = pm.blockchain.GetTd(head.Hash(), head.Number.Uint64())
|
||||||
|
)
|
||||||
defer pm.Stop()
|
defer pm.Stop()
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
|
@ -55,16 +59,16 @@ func testStatusMsgErrors(t *testing.T, protocol int) {
|
||||||
wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"),
|
wantError: errResp(ErrNoStatusMsg, "first msg has code 2 (!= 0)"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: StatusMsg, data: statusData{10, DefaultConfig.NetworkId, td, currentBlock, genesis},
|
code: StatusMsg, data: statusData{10, DefaultConfig.NetworkId, td, head.Hash(), genesis.Hash()},
|
||||||
wantError: errResp(ErrProtocolVersionMismatch, "10 (!= %d)", protocol),
|
wantError: errResp(ErrProtocolVersionMismatch, "10 (!= %d)", protocol),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: StatusMsg, data: statusData{uint32(protocol), 999, td, currentBlock, genesis},
|
code: StatusMsg, data: statusData{uint32(protocol), 999, td, head.Hash(), genesis.Hash()},
|
||||||
wantError: errResp(ErrNetworkIdMismatch, "999 (!= 1)"),
|
wantError: errResp(ErrNetworkIdMismatch, "999 (!= 1)"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: StatusMsg, data: statusData{uint32(protocol), DefaultConfig.NetworkId, td, currentBlock, common.Hash{3}},
|
code: StatusMsg, data: statusData{uint32(protocol), DefaultConfig.NetworkId, td, head.Hash(), common.Hash{3}},
|
||||||
wantError: errResp(ErrGenesisBlockMismatch, "0300000000000000 (!= %x)", genesis[:8]),
|
wantError: errResp(ErrGenesisBlockMismatch, "0300000000000000 (!= %x)", genesis.Hash().Bytes()[:8]),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,7 +98,7 @@ func TestRecvTransactions63(t *testing.T) { testRecvTransactions(t, 63) }
|
||||||
|
|
||||||
func testRecvTransactions(t *testing.T, protocol int) {
|
func testRecvTransactions(t *testing.T, protocol int) {
|
||||||
txAdded := make(chan []*types.Transaction)
|
txAdded := make(chan []*types.Transaction)
|
||||||
pm := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, txAdded)
|
pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, txAdded)
|
||||||
pm.acceptTxs = 1 // mark synced to accept transactions
|
pm.acceptTxs = 1 // mark synced to accept transactions
|
||||||
p, _ := newTestPeer("peer", protocol, pm, true)
|
p, _ := newTestPeer("peer", protocol, pm, true)
|
||||||
defer pm.Stop()
|
defer pm.Stop()
|
||||||
|
|
@ -121,7 +125,7 @@ func TestSendTransactions62(t *testing.T) { testSendTransactions(t, 62) }
|
||||||
func TestSendTransactions63(t *testing.T) { testSendTransactions(t, 63) }
|
func TestSendTransactions63(t *testing.T) { testSendTransactions(t, 63) }
|
||||||
|
|
||||||
func testSendTransactions(t *testing.T, protocol int) {
|
func testSendTransactions(t *testing.T, protocol int) {
|
||||||
pm := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil)
|
pm, _ := newTestProtocolManagerMust(t, downloader.FullSync, 0, nil, nil)
|
||||||
defer pm.Stop()
|
defer pm.Stop()
|
||||||
|
|
||||||
// Fill the pool with big transactions.
|
// Fill the pool with big transactions.
|
||||||
|
|
|
||||||
15
eth/sync.go
15
eth/sync.go
|
|
@ -189,18 +189,13 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
|
||||||
mode = downloader.FastSync
|
mode = downloader.FastSync
|
||||||
}
|
}
|
||||||
// Run the sync cycle, and disable fast sync if we've went past the pivot block
|
// Run the sync cycle, and disable fast sync if we've went past the pivot block
|
||||||
err := pm.downloader.Synchronise(peer.id, pHead, pTd, mode)
|
if err := pm.downloader.Synchronise(peer.id, pHead, pTd, mode); err != nil {
|
||||||
|
|
||||||
if atomic.LoadUint32(&pm.fastSync) == 1 {
|
|
||||||
// Disable fast sync if we indeed have something in our chain
|
|
||||||
if pm.blockchain.CurrentBlock().NumberU64() > 0 {
|
|
||||||
log.Info("Fast sync complete, auto disabling")
|
|
||||||
atomic.StoreUint32(&pm.fastSync, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if atomic.LoadUint32(&pm.fastSync) == 1 {
|
||||||
|
log.Info("Fast sync complete, auto disabling")
|
||||||
|
atomic.StoreUint32(&pm.fastSync, 0)
|
||||||
|
}
|
||||||
atomic.StoreUint32(&pm.acceptTxs, 1) // Mark initial sync done
|
atomic.StoreUint32(&pm.acceptTxs, 1) // Mark initial sync done
|
||||||
if head := pm.blockchain.CurrentBlock(); head.NumberU64() > 0 {
|
if head := pm.blockchain.CurrentBlock(); head.NumberU64() > 0 {
|
||||||
// We've completed a sync cycle, notify all peers of new state. This path is
|
// We've completed a sync cycle, notify all peers of new state. This path is
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,12 @@ import (
|
||||||
// imported into the blockchain.
|
// imported into the blockchain.
|
||||||
func TestFastSyncDisabling(t *testing.T) {
|
func TestFastSyncDisabling(t *testing.T) {
|
||||||
// Create a pristine protocol manager, check that fast sync is left enabled
|
// Create a pristine protocol manager, check that fast sync is left enabled
|
||||||
pmEmpty := newTestProtocolManagerMust(t, downloader.FastSync, 0, nil, nil)
|
pmEmpty, _ := newTestProtocolManagerMust(t, downloader.FastSync, 0, nil, nil)
|
||||||
if atomic.LoadUint32(&pmEmpty.fastSync) == 0 {
|
if atomic.LoadUint32(&pmEmpty.fastSync) == 0 {
|
||||||
t.Fatalf("fast sync disabled on pristine blockchain")
|
t.Fatalf("fast sync disabled on pristine blockchain")
|
||||||
}
|
}
|
||||||
// Create a full protocol manager, check that fast sync gets disabled
|
// Create a full protocol manager, check that fast sync gets disabled
|
||||||
pmFull := newTestProtocolManagerMust(t, downloader.FastSync, 1024, nil, nil)
|
pmFull, _ := newTestProtocolManagerMust(t, downloader.FastSync, 1024, nil, nil)
|
||||||
if atomic.LoadUint32(&pmFull.fastSync) == 1 {
|
if atomic.LoadUint32(&pmFull.fastSync) == 1 {
|
||||||
t.Fatalf("fast sync not disabled on non-empty blockchain")
|
t.Fatalf("fast sync not disabled on non-empty blockchain")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2016 The go-ethereum Authors
|
// Copyright 2017 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2016 The go-ethereum Authors
|
// Copyright 2017 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of the go-ethereum library.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
|
|
||||||
|
|
@ -299,6 +299,11 @@ func (b *ldbBatch) ValueSize() int {
|
||||||
return b.size
|
return b.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *ldbBatch) Reset() {
|
||||||
|
b.b.Reset()
|
||||||
|
b.size = 0
|
||||||
|
}
|
||||||
|
|
||||||
type table struct {
|
type table struct {
|
||||||
db Database
|
db Database
|
||||||
prefix string
|
prefix string
|
||||||
|
|
@ -358,3 +363,7 @@ func (tb *tableBatch) Write() error {
|
||||||
func (tb *tableBatch) ValueSize() int {
|
func (tb *tableBatch) ValueSize() int {
|
||||||
return tb.batch.ValueSize()
|
return tb.batch.ValueSize()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tb *tableBatch) Reset() {
|
||||||
|
tb.batch.Reset()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,4 +41,6 @@ type Batch interface {
|
||||||
Putter
|
Putter
|
||||||
ValueSize() int // amount of data in the batch
|
ValueSize() int // amount of data in the batch
|
||||||
Write() error
|
Write() error
|
||||||
|
// Reset resets the batch for reuse
|
||||||
|
Reset()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,3 +123,8 @@ func (b *memBatch) Write() error {
|
||||||
func (b *memBatch) ValueSize() int {
|
func (b *memBatch) ValueSize() int {
|
||||||
return b.size
|
return b.size
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *memBatch) Reset() {
|
||||||
|
b.writes = b.writes[:0]
|
||||||
|
b.size = 0
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,7 @@ func LocalEnv() Environment {
|
||||||
}
|
}
|
||||||
if env.Branch == "" {
|
if env.Branch == "" {
|
||||||
if head != "HEAD" {
|
if head != "HEAD" {
|
||||||
env.Branch = strings.TrimLeft(head, "refs/heads/")
|
env.Branch = strings.TrimPrefix(head, "refs/heads/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if info, err := os.Stat(".git/objects"); err == nil && info.IsDir() && env.Tag == "" {
|
if info, err := os.Stat(".git/objects"); err == nil && info.IsDir() && env.Tag == "" {
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
// Copyright 2016 The go-ethereum Authors
|
// Copyright 2017 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 cmdtest
|
package cmdtest
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -333,7 +333,7 @@ func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool {
|
||||||
return fetchKeystore(s.am).Lock(addr) == nil
|
return fetchKeystore(s.am).Lock(addr) == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// signTransactions sets defaults and signs the given transation
|
// signTransactions sets defaults and signs the given transaction
|
||||||
// NOTE: the caller needs to ensure that the nonceLock is held, if applicable,
|
// NOTE: the caller needs to ensure that the nonceLock is held, if applicable,
|
||||||
// and release it after the transaction has been submitted to the tx pool
|
// and release it after the transaction has been submitted to the tx pool
|
||||||
func (s *PrivateAccountAPI) signTransaction(ctx context.Context, args SendTxArgs, passwd string) (*types.Transaction, error) {
|
func (s *PrivateAccountAPI) signTransaction(ctx context.Context, args SendTxArgs, passwd string) (*types.Transaction, error) {
|
||||||
|
|
@ -808,7 +808,7 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx
|
||||||
"difficulty": (*hexutil.Big)(head.Difficulty),
|
"difficulty": (*hexutil.Big)(head.Difficulty),
|
||||||
"totalDifficulty": (*hexutil.Big)(s.b.GetTd(b.Hash())),
|
"totalDifficulty": (*hexutil.Big)(s.b.GetTd(b.Hash())),
|
||||||
"extraData": hexutil.Bytes(head.Extra),
|
"extraData": hexutil.Bytes(head.Extra),
|
||||||
"size": hexutil.Uint64(uint64(b.Size().Int64())),
|
"size": hexutil.Uint64(b.Size()),
|
||||||
"gasLimit": hexutil.Uint64(head.GasLimit),
|
"gasLimit": hexutil.Uint64(head.GasLimit),
|
||||||
"gasUsed": hexutil.Uint64(head.GasUsed),
|
"gasUsed": hexutil.Uint64(head.GasUsed),
|
||||||
"timestamp": (*hexutil.Big)(head.Time),
|
"timestamp": (*hexutil.Big)(head.Time),
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type LightEthereum struct {
|
type LightEthereum struct {
|
||||||
|
config *eth.Config
|
||||||
|
|
||||||
odr *LesOdr
|
odr *LesOdr
|
||||||
relay *LesTxRelay
|
relay *LesTxRelay
|
||||||
chainConfig *params.ChainConfig
|
chainConfig *params.ChainConfig
|
||||||
|
|
@ -92,6 +94,7 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
|
||||||
quitSync := make(chan struct{})
|
quitSync := make(chan struct{})
|
||||||
|
|
||||||
leth := &LightEthereum{
|
leth := &LightEthereum{
|
||||||
|
config: config,
|
||||||
chainConfig: chainConfig,
|
chainConfig: chainConfig,
|
||||||
chainDb: chainDb,
|
chainDb: chainDb,
|
||||||
eventMux: ctx.EventMux,
|
eventMux: ctx.EventMux,
|
||||||
|
|
@ -224,7 +227,7 @@ func (s *LightEthereum) Start(srvr *p2p.Server) error {
|
||||||
// clients are searching for the first advertised protocol in the list
|
// clients are searching for the first advertised protocol in the list
|
||||||
protocolVersion := AdvertiseProtocolVersions[0]
|
protocolVersion := AdvertiseProtocolVersions[0]
|
||||||
s.serverPool.start(srvr, lesTopic(s.blockchain.Genesis().Hash(), protocolVersion))
|
s.serverPool.start(srvr, lesTopic(s.blockchain.Genesis().Hash(), protocolVersion))
|
||||||
s.protocolManager.Start()
|
s.protocolManager.Start(s.config.LightPeers)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -425,6 +425,9 @@ func (f *lightFetcher) nextRequest() (*distReq, uint64) {
|
||||||
},
|
},
|
||||||
canSend: func(dp distPeer) bool {
|
canSend: func(dp distPeer) bool {
|
||||||
p := dp.(*peer)
|
p := dp.(*peer)
|
||||||
|
f.lock.Lock()
|
||||||
|
defer f.lock.Unlock()
|
||||||
|
|
||||||
fp := f.peers[p]
|
fp := f.peers[p]
|
||||||
return fp != nil && fp.nodeByHash[bestHash] != nil
|
return fp != nil && fp.nodeByHash[bestHash] != nil
|
||||||
},
|
},
|
||||||
|
|
|
||||||
240
les/handler.go
240
les/handler.go
|
|
@ -18,7 +18,6 @@
|
||||||
package les
|
package les
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -77,13 +76,12 @@ type BlockChain interface {
|
||||||
GetHeader(hash common.Hash, number uint64) *types.Header
|
GetHeader(hash common.Hash, number uint64) *types.Header
|
||||||
GetHeaderByHash(hash common.Hash) *types.Header
|
GetHeaderByHash(hash common.Hash) *types.Header
|
||||||
CurrentHeader() *types.Header
|
CurrentHeader() *types.Header
|
||||||
GetTdByHash(hash common.Hash) *big.Int
|
GetTd(hash common.Hash, number uint64) *big.Int
|
||||||
|
State() (*state.StateDB, error)
|
||||||
InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error)
|
InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error)
|
||||||
Rollback(chain []common.Hash)
|
Rollback(chain []common.Hash)
|
||||||
Status() (td *big.Int, currentBlock common.Hash, genesisBlock common.Hash)
|
|
||||||
GetHeaderByNumber(number uint64) *types.Header
|
GetHeaderByNumber(number uint64) *types.Header
|
||||||
GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash
|
GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash
|
||||||
LastBlockHash() common.Hash
|
|
||||||
Genesis() *types.Block
|
Genesis() *types.Block
|
||||||
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription
|
||||||
}
|
}
|
||||||
|
|
@ -111,6 +109,7 @@ type ProtocolManager struct {
|
||||||
downloader *downloader.Downloader
|
downloader *downloader.Downloader
|
||||||
fetcher *lightFetcher
|
fetcher *lightFetcher
|
||||||
peers *peerSet
|
peers *peerSet
|
||||||
|
maxPeers int
|
||||||
|
|
||||||
SubProtocols []p2p.Protocol
|
SubProtocols []p2p.Protocol
|
||||||
|
|
||||||
|
|
@ -218,7 +217,9 @@ func (pm *ProtocolManager) removePeer(id string) {
|
||||||
pm.peers.Unregister(id)
|
pm.peers.Unregister(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *ProtocolManager) Start() {
|
func (pm *ProtocolManager) Start(maxPeers int) {
|
||||||
|
pm.maxPeers = maxPeers
|
||||||
|
|
||||||
if pm.lightSync {
|
if pm.lightSync {
|
||||||
go pm.syncer()
|
go pm.syncer()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -259,12 +260,21 @@ func (pm *ProtocolManager) newPeer(pv int, nv uint64, p *p2p.Peer, rw p2p.MsgRea
|
||||||
// handle is the callback invoked to manage the life cycle of a les peer. When
|
// handle is the callback invoked to manage the life cycle of a les peer. When
|
||||||
// this function terminates, the peer is disconnected.
|
// this function terminates, the peer is disconnected.
|
||||||
func (pm *ProtocolManager) handle(p *peer) error {
|
func (pm *ProtocolManager) handle(p *peer) error {
|
||||||
|
if pm.peers.Len() >= pm.maxPeers {
|
||||||
|
return p2p.DiscTooManyPeers
|
||||||
|
}
|
||||||
|
|
||||||
p.Log().Debug("Light Ethereum peer connected", "name", p.Name())
|
p.Log().Debug("Light Ethereum peer connected", "name", p.Name())
|
||||||
|
|
||||||
// Execute the LES handshake
|
// Execute the LES handshake
|
||||||
td, head, genesis := pm.blockchain.Status()
|
var (
|
||||||
headNum := core.GetBlockNumber(pm.chainDb, head)
|
genesis = pm.blockchain.Genesis()
|
||||||
if err := p.Handshake(td, head, headNum, genesis, pm.server); err != nil {
|
head = pm.blockchain.CurrentHeader()
|
||||||
|
hash = head.Hash()
|
||||||
|
number = head.Number.Uint64()
|
||||||
|
td = pm.blockchain.GetTd(hash, number)
|
||||||
|
)
|
||||||
|
if err := p.Handshake(td, hash, number, genesis.Hash(), pm.server); err != nil {
|
||||||
p.Log().Debug("Light Ethereum handshake failed", "err", err)
|
p.Log().Debug("Light Ethereum handshake failed", "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -569,17 +579,19 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
for _, req := range req.Reqs {
|
for _, req := range req.Reqs {
|
||||||
// Retrieve the requested state entry, stopping if enough was found
|
// Retrieve the requested state entry, stopping if enough was found
|
||||||
if header := core.GetHeader(pm.chainDb, req.BHash, core.GetBlockNumber(pm.chainDb, req.BHash)); header != nil {
|
if header := core.GetHeader(pm.chainDb, req.BHash, core.GetBlockNumber(pm.chainDb, req.BHash)); header != nil {
|
||||||
if trie, _ := trie.New(header.Root, pm.chainDb); trie != nil {
|
statedb, err := pm.blockchain.State()
|
||||||
sdata := trie.Get(req.AccKey)
|
if err != nil {
|
||||||
var acc state.Account
|
continue
|
||||||
if err := rlp.DecodeBytes(sdata, &acc); err == nil {
|
}
|
||||||
entry, _ := pm.chainDb.Get(acc.CodeHash)
|
account, err := pm.getAccount(statedb, header.Root, common.BytesToHash(req.AccKey))
|
||||||
if bytes+len(entry) >= softResponseLimit {
|
if err != nil {
|
||||||
break
|
continue
|
||||||
}
|
}
|
||||||
data = append(data, entry)
|
code, _ := statedb.Database().TrieDB().Node(common.BytesToHash(account.CodeHash))
|
||||||
bytes += len(entry)
|
|
||||||
}
|
data = append(data, code)
|
||||||
|
if bytes += len(code); bytes >= softResponseLimit {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -691,25 +703,29 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
return errResp(ErrRequestRejected, "")
|
return errResp(ErrRequestRejected, "")
|
||||||
}
|
}
|
||||||
for _, req := range req.Reqs {
|
for _, req := range req.Reqs {
|
||||||
if bytes >= softResponseLimit {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// Retrieve the requested state entry, stopping if enough was found
|
// Retrieve the requested state entry, stopping if enough was found
|
||||||
if header := core.GetHeader(pm.chainDb, req.BHash, core.GetBlockNumber(pm.chainDb, req.BHash)); header != nil {
|
if header := core.GetHeader(pm.chainDb, req.BHash, core.GetBlockNumber(pm.chainDb, req.BHash)); header != nil {
|
||||||
if tr, _ := trie.New(header.Root, pm.chainDb); tr != nil {
|
statedb, err := pm.blockchain.State()
|
||||||
if len(req.AccKey) > 0 {
|
if err != nil {
|
||||||
sdata := tr.Get(req.AccKey)
|
continue
|
||||||
tr = nil
|
}
|
||||||
var acc state.Account
|
var trie state.Trie
|
||||||
if err := rlp.DecodeBytes(sdata, &acc); err == nil {
|
if len(req.AccKey) > 0 {
|
||||||
tr, _ = trie.New(acc.Root, pm.chainDb)
|
account, err := pm.getAccount(statedb, header.Root, common.BytesToHash(req.AccKey))
|
||||||
}
|
if err != nil {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
if tr != nil {
|
trie, _ = statedb.Database().OpenStorageTrie(common.BytesToHash(req.AccKey), account.Root)
|
||||||
var proof light.NodeList
|
} else {
|
||||||
tr.Prove(req.Key, 0, &proof)
|
trie, _ = statedb.Database().OpenTrie(header.Root)
|
||||||
proofs = append(proofs, proof)
|
}
|
||||||
bytes += proof.DataSize()
|
if trie != nil {
|
||||||
|
var proof light.NodeList
|
||||||
|
trie.Prove(req.Key, 0, &proof)
|
||||||
|
|
||||||
|
proofs = append(proofs, proof)
|
||||||
|
if bytes += proof.DataSize(); bytes >= softResponseLimit {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -730,9 +746,9 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
}
|
}
|
||||||
// Gather state data until the fetch or network limits is reached
|
// Gather state data until the fetch or network limits is reached
|
||||||
var (
|
var (
|
||||||
lastBHash common.Hash
|
lastBHash common.Hash
|
||||||
lastAccKey []byte
|
statedb *state.StateDB
|
||||||
tr, str *trie.Trie
|
root common.Hash
|
||||||
)
|
)
|
||||||
reqCnt := len(req.Reqs)
|
reqCnt := len(req.Reqs)
|
||||||
if reject(uint64(reqCnt), MaxProofsFetch) {
|
if reject(uint64(reqCnt), MaxProofsFetch) {
|
||||||
|
|
@ -742,41 +758,41 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
nodes := light.NewNodeSet()
|
nodes := light.NewNodeSet()
|
||||||
|
|
||||||
for _, req := range req.Reqs {
|
for _, req := range req.Reqs {
|
||||||
|
// Look up the state belonging to the request
|
||||||
|
if statedb == nil || req.BHash != lastBHash {
|
||||||
|
statedb, root, lastBHash = nil, common.Hash{}, req.BHash
|
||||||
|
|
||||||
|
if header := core.GetHeader(pm.chainDb, req.BHash, core.GetBlockNumber(pm.chainDb, req.BHash)); header != nil {
|
||||||
|
statedb, _ = pm.blockchain.State()
|
||||||
|
root = header.Root
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if statedb == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Pull the account or storage trie of the request
|
||||||
|
var trie state.Trie
|
||||||
|
if len(req.AccKey) > 0 {
|
||||||
|
account, err := pm.getAccount(statedb, root, common.BytesToHash(req.AccKey))
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
trie, _ = statedb.Database().OpenStorageTrie(common.BytesToHash(req.AccKey), account.Root)
|
||||||
|
} else {
|
||||||
|
trie, _ = statedb.Database().OpenTrie(root)
|
||||||
|
}
|
||||||
|
if trie == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Prove the user's request from the account or stroage trie
|
||||||
|
trie.Prove(req.Key, req.FromLevel, nodes)
|
||||||
if nodes.DataSize() >= softResponseLimit {
|
if nodes.DataSize() >= softResponseLimit {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if tr == nil || req.BHash != lastBHash {
|
|
||||||
if header := core.GetHeader(pm.chainDb, req.BHash, core.GetBlockNumber(pm.chainDb, req.BHash)); header != nil {
|
|
||||||
tr, _ = trie.New(header.Root, pm.chainDb)
|
|
||||||
} else {
|
|
||||||
tr = nil
|
|
||||||
}
|
|
||||||
lastBHash = req.BHash
|
|
||||||
str = nil
|
|
||||||
}
|
|
||||||
if tr != nil {
|
|
||||||
if len(req.AccKey) > 0 {
|
|
||||||
if str == nil || !bytes.Equal(req.AccKey, lastAccKey) {
|
|
||||||
sdata := tr.Get(req.AccKey)
|
|
||||||
str = nil
|
|
||||||
var acc state.Account
|
|
||||||
if err := rlp.DecodeBytes(sdata, &acc); err == nil {
|
|
||||||
str, _ = trie.New(acc.Root, pm.chainDb)
|
|
||||||
}
|
|
||||||
lastAccKey = common.CopyBytes(req.AccKey)
|
|
||||||
}
|
|
||||||
if str != nil {
|
|
||||||
str.Prove(req.Key, req.FromLevel, nodes)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tr.Prove(req.Key, req.FromLevel, nodes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
proofs := nodes.NodeList()
|
|
||||||
bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost)
|
bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost)
|
||||||
pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost)
|
pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost)
|
||||||
return p.SendProofsV2(req.ReqID, bv, proofs)
|
return p.SendProofsV2(req.ReqID, bv, nodes.NodeList())
|
||||||
|
|
||||||
case ProofsV1Msg:
|
case ProofsV1Msg:
|
||||||
if pm.odr == nil {
|
if pm.odr == nil {
|
||||||
|
|
@ -839,22 +855,24 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
if reject(uint64(reqCnt), MaxHelperTrieProofsFetch) {
|
if reject(uint64(reqCnt), MaxHelperTrieProofsFetch) {
|
||||||
return errResp(ErrRequestRejected, "")
|
return errResp(ErrRequestRejected, "")
|
||||||
}
|
}
|
||||||
trieDb := ethdb.NewTable(pm.chainDb, light.ChtTablePrefix)
|
trieDb := trie.NewDatabase(ethdb.NewTable(pm.chainDb, light.ChtTablePrefix))
|
||||||
for _, req := range req.Reqs {
|
for _, req := range req.Reqs {
|
||||||
if bytes >= softResponseLimit {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if header := pm.blockchain.GetHeaderByNumber(req.BlockNum); header != nil {
|
if header := pm.blockchain.GetHeaderByNumber(req.BlockNum); header != nil {
|
||||||
sectionHead := core.GetCanonicalHash(pm.chainDb, req.ChtNum*light.ChtV1Frequency-1)
|
sectionHead := core.GetCanonicalHash(pm.chainDb, req.ChtNum*light.CHTFrequencyServer-1)
|
||||||
if root := light.GetChtRoot(pm.chainDb, req.ChtNum-1, sectionHead); root != (common.Hash{}) {
|
if root := light.GetChtRoot(pm.chainDb, req.ChtNum-1, sectionHead); root != (common.Hash{}) {
|
||||||
if tr, _ := trie.New(root, trieDb); tr != nil {
|
trie, err := trie.New(root, trieDb)
|
||||||
var encNumber [8]byte
|
if err != nil {
|
||||||
binary.BigEndian.PutUint64(encNumber[:], req.BlockNum)
|
continue
|
||||||
var proof light.NodeList
|
}
|
||||||
tr.Prove(encNumber[:], 0, &proof)
|
var encNumber [8]byte
|
||||||
proofs = append(proofs, ChtResp{Header: header, Proof: proof})
|
binary.BigEndian.PutUint64(encNumber[:], req.BlockNum)
|
||||||
bytes += proof.DataSize() + estHeaderRlpSize
|
|
||||||
|
var proof light.NodeList
|
||||||
|
trie.Prove(encNumber[:], 0, &proof)
|
||||||
|
|
||||||
|
proofs = append(proofs, ChtResp{Header: header, Proof: proof})
|
||||||
|
if bytes += proof.DataSize() + estHeaderRlpSize; bytes >= softResponseLimit {
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -887,25 +905,17 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
lastIdx uint64
|
lastIdx uint64
|
||||||
lastType uint
|
lastType uint
|
||||||
root common.Hash
|
root common.Hash
|
||||||
tr *trie.Trie
|
auxTrie *trie.Trie
|
||||||
)
|
)
|
||||||
|
|
||||||
nodes := light.NewNodeSet()
|
nodes := light.NewNodeSet()
|
||||||
|
|
||||||
for _, req := range req.Reqs {
|
for _, req := range req.Reqs {
|
||||||
if nodes.DataSize()+auxBytes >= softResponseLimit {
|
if auxTrie == nil || req.Type != lastType || req.TrieIdx != lastIdx {
|
||||||
break
|
auxTrie, lastType, lastIdx = nil, req.Type, req.TrieIdx
|
||||||
}
|
|
||||||
if tr == nil || req.HelperTrieType != lastType || req.TrieIdx != lastIdx {
|
|
||||||
var prefix string
|
var prefix string
|
||||||
root, prefix = pm.getHelperTrie(req.HelperTrieType, req.TrieIdx)
|
if root, prefix = pm.getHelperTrie(req.Type, req.TrieIdx); root != (common.Hash{}) {
|
||||||
if root != (common.Hash{}) {
|
auxTrie, _ = trie.New(root, trie.NewDatabase(ethdb.NewTable(pm.chainDb, prefix)))
|
||||||
if t, err := trie.New(root, ethdb.NewTable(pm.chainDb, prefix)); err == nil {
|
|
||||||
tr = t
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
lastType = req.HelperTrieType
|
|
||||||
lastIdx = req.TrieIdx
|
|
||||||
}
|
}
|
||||||
if req.AuxReq == auxRoot {
|
if req.AuxReq == auxRoot {
|
||||||
var data []byte
|
var data []byte
|
||||||
|
|
@ -915,8 +925,8 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
auxData = append(auxData, data)
|
auxData = append(auxData, data)
|
||||||
auxBytes += len(data)
|
auxBytes += len(data)
|
||||||
} else {
|
} else {
|
||||||
if tr != nil {
|
if auxTrie != nil {
|
||||||
tr.Prove(req.Key, req.FromLevel, nodes)
|
auxTrie.Prove(req.Key, req.FromLevel, nodes)
|
||||||
}
|
}
|
||||||
if req.AuxReq != 0 {
|
if req.AuxReq != 0 {
|
||||||
data := pm.getHelperTrieAuxData(req)
|
data := pm.getHelperTrieAuxData(req)
|
||||||
|
|
@ -924,11 +934,13 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
auxBytes += len(data)
|
auxBytes += len(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if nodes.DataSize()+auxBytes >= softResponseLimit {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
proofs := nodes.NodeList()
|
|
||||||
bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost)
|
bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost)
|
||||||
pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost)
|
pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost)
|
||||||
return p.SendHelperTrieProofs(req.ReqID, bv, HelperTrieResps{Proofs: proofs, AuxData: auxData})
|
return p.SendHelperTrieProofs(req.ReqID, bv, HelperTrieResps{Proofs: nodes.NodeList(), AuxData: auxData})
|
||||||
|
|
||||||
case HeaderProofsMsg:
|
case HeaderProofsMsg:
|
||||||
if pm.odr == nil {
|
if pm.odr == nil {
|
||||||
|
|
@ -1080,11 +1092,28 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getAccount retrieves an account from the state based at root.
|
||||||
|
func (pm *ProtocolManager) getAccount(statedb *state.StateDB, root, hash common.Hash) (state.Account, error) {
|
||||||
|
trie, err := trie.New(root, statedb.Database().TrieDB())
|
||||||
|
if err != nil {
|
||||||
|
return state.Account{}, err
|
||||||
|
}
|
||||||
|
blob, err := trie.TryGet(hash[:])
|
||||||
|
if err != nil {
|
||||||
|
return state.Account{}, err
|
||||||
|
}
|
||||||
|
var account state.Account
|
||||||
|
if err = rlp.DecodeBytes(blob, &account); err != nil {
|
||||||
|
return state.Account{}, err
|
||||||
|
}
|
||||||
|
return account, nil
|
||||||
|
}
|
||||||
|
|
||||||
// getHelperTrie returns the post-processed trie root for the given trie ID and section index
|
// getHelperTrie returns the post-processed trie root for the given trie ID and section index
|
||||||
func (pm *ProtocolManager) getHelperTrie(id uint, idx uint64) (common.Hash, string) {
|
func (pm *ProtocolManager) getHelperTrie(id uint, idx uint64) (common.Hash, string) {
|
||||||
switch id {
|
switch id {
|
||||||
case htCanonical:
|
case htCanonical:
|
||||||
sectionHead := core.GetCanonicalHash(pm.chainDb, (idx+1)*light.ChtFrequency-1)
|
sectionHead := core.GetCanonicalHash(pm.chainDb, (idx+1)*light.CHTFrequencyClient-1)
|
||||||
return light.GetChtV2Root(pm.chainDb, idx, sectionHead), light.ChtTablePrefix
|
return light.GetChtV2Root(pm.chainDb, idx, sectionHead), light.ChtTablePrefix
|
||||||
case htBloomBits:
|
case htBloomBits:
|
||||||
sectionHead := core.GetCanonicalHash(pm.chainDb, (idx+1)*light.BloomTrieFrequency-1)
|
sectionHead := core.GetCanonicalHash(pm.chainDb, (idx+1)*light.BloomTrieFrequency-1)
|
||||||
|
|
@ -1095,10 +1124,8 @@ func (pm *ProtocolManager) getHelperTrie(id uint, idx uint64) (common.Hash, stri
|
||||||
|
|
||||||
// getHelperTrieAuxData returns requested auxiliary data for the given HelperTrie request
|
// getHelperTrieAuxData returns requested auxiliary data for the given HelperTrie request
|
||||||
func (pm *ProtocolManager) getHelperTrieAuxData(req HelperTrieReq) []byte {
|
func (pm *ProtocolManager) getHelperTrieAuxData(req HelperTrieReq) []byte {
|
||||||
if req.HelperTrieType == htCanonical && req.AuxReq == auxHeader {
|
switch {
|
||||||
if len(req.Key) != 8 {
|
case req.Type == htCanonical && req.AuxReq == auxHeader && len(req.Key) == 8:
|
||||||
return nil
|
|
||||||
}
|
|
||||||
blockNum := binary.BigEndian.Uint64(req.Key)
|
blockNum := binary.BigEndian.Uint64(req.Key)
|
||||||
hash := core.GetCanonicalHash(pm.chainDb, blockNum)
|
hash := core.GetCanonicalHash(pm.chainDb, blockNum)
|
||||||
return core.GetHeaderRLP(pm.chainDb, hash, blockNum)
|
return core.GetHeaderRLP(pm.chainDb, hash, blockNum)
|
||||||
|
|
@ -1135,12 +1162,15 @@ type NodeInfo struct {
|
||||||
|
|
||||||
// NodeInfo retrieves some protocol metadata about the running host node.
|
// NodeInfo retrieves some protocol metadata about the running host node.
|
||||||
func (self *ProtocolManager) NodeInfo() *NodeInfo {
|
func (self *ProtocolManager) NodeInfo() *NodeInfo {
|
||||||
|
head := self.blockchain.CurrentHeader()
|
||||||
|
hash := head.Hash()
|
||||||
|
|
||||||
return &NodeInfo{
|
return &NodeInfo{
|
||||||
Network: self.networkId,
|
Network: self.networkId,
|
||||||
Difficulty: self.blockchain.GetTdByHash(self.blockchain.LastBlockHash()),
|
Difficulty: self.blockchain.GetTd(hash, head.Number.Uint64()),
|
||||||
Genesis: self.blockchain.Genesis().Hash(),
|
Genesis: self.blockchain.Genesis().Hash(),
|
||||||
Config: self.blockchain.Config(),
|
Config: self.blockchain.Config(),
|
||||||
Head: self.blockchain.LastBlockHash(),
|
Head: hash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
package les
|
package les
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"encoding/binary"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
@ -45,27 +45,8 @@ func expectResponse(r p2p.MsgReader, msgcode, reqID, bv uint64, data interface{}
|
||||||
return p2p.ExpectMsg(r, msgcode, resp{reqID, bv, data})
|
return p2p.ExpectMsg(r, msgcode, resp{reqID, bv, data})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testCheckProof(t *testing.T, exp *light.NodeSet, got light.NodeList) {
|
|
||||||
if exp.KeyCount() > len(got) {
|
|
||||||
t.Errorf("proof has fewer nodes than expected")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if exp.KeyCount() < len(got) {
|
|
||||||
t.Errorf("proof has more nodes than expected")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, node := range got {
|
|
||||||
n, _ := exp.Get(crypto.Keccak256(node))
|
|
||||||
if !bytes.Equal(n, node) {
|
|
||||||
t.Errorf("proof contents mismatch")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that block headers can be retrieved from a remote chain based on user queries.
|
// Tests that block headers can be retrieved from a remote chain based on user queries.
|
||||||
func TestGetBlockHeadersLes1(t *testing.T) { testGetBlockHeaders(t, 1) }
|
func TestGetBlockHeadersLes1(t *testing.T) { testGetBlockHeaders(t, 1) }
|
||||||
|
|
||||||
func TestGetBlockHeadersLes2(t *testing.T) { testGetBlockHeaders(t, 2) }
|
func TestGetBlockHeadersLes2(t *testing.T) { testGetBlockHeaders(t, 2) }
|
||||||
|
|
||||||
func testGetBlockHeaders(t *testing.T, protocol int) {
|
func testGetBlockHeaders(t *testing.T, protocol int) {
|
||||||
|
|
@ -196,7 +177,6 @@ func testGetBlockHeaders(t *testing.T, protocol int) {
|
||||||
|
|
||||||
// Tests that block contents can be retrieved from a remote chain based on their hashes.
|
// Tests that block contents can be retrieved from a remote chain based on their hashes.
|
||||||
func TestGetBlockBodiesLes1(t *testing.T) { testGetBlockBodies(t, 1) }
|
func TestGetBlockBodiesLes1(t *testing.T) { testGetBlockBodies(t, 1) }
|
||||||
|
|
||||||
func TestGetBlockBodiesLes2(t *testing.T) { testGetBlockBodies(t, 2) }
|
func TestGetBlockBodiesLes2(t *testing.T) { testGetBlockBodies(t, 2) }
|
||||||
|
|
||||||
func testGetBlockBodies(t *testing.T, protocol int) {
|
func testGetBlockBodies(t *testing.T, protocol int) {
|
||||||
|
|
@ -274,7 +254,6 @@ func testGetBlockBodies(t *testing.T, protocol int) {
|
||||||
|
|
||||||
// Tests that the contract codes can be retrieved based on account addresses.
|
// Tests that the contract codes can be retrieved based on account addresses.
|
||||||
func TestGetCodeLes1(t *testing.T) { testGetCode(t, 1) }
|
func TestGetCodeLes1(t *testing.T) { testGetCode(t, 1) }
|
||||||
|
|
||||||
func TestGetCodeLes2(t *testing.T) { testGetCode(t, 2) }
|
func TestGetCodeLes2(t *testing.T) { testGetCode(t, 2) }
|
||||||
|
|
||||||
func testGetCode(t *testing.T, protocol int) {
|
func testGetCode(t *testing.T, protocol int) {
|
||||||
|
|
@ -309,7 +288,6 @@ func testGetCode(t *testing.T, protocol int) {
|
||||||
|
|
||||||
// Tests that the transaction receipts can be retrieved based on hashes.
|
// Tests that the transaction receipts can be retrieved based on hashes.
|
||||||
func TestGetReceiptLes1(t *testing.T) { testGetReceipt(t, 1) }
|
func TestGetReceiptLes1(t *testing.T) { testGetReceipt(t, 1) }
|
||||||
|
|
||||||
func TestGetReceiptLes2(t *testing.T) { testGetReceipt(t, 2) }
|
func TestGetReceiptLes2(t *testing.T) { testGetReceipt(t, 2) }
|
||||||
|
|
||||||
func testGetReceipt(t *testing.T, protocol int) {
|
func testGetReceipt(t *testing.T, protocol int) {
|
||||||
|
|
@ -338,7 +316,6 @@ func testGetReceipt(t *testing.T, protocol int) {
|
||||||
|
|
||||||
// Tests that trie merkle proofs can be retrieved
|
// Tests that trie merkle proofs can be retrieved
|
||||||
func TestGetProofsLes1(t *testing.T) { testGetProofs(t, 1) }
|
func TestGetProofsLes1(t *testing.T) { testGetProofs(t, 1) }
|
||||||
|
|
||||||
func TestGetProofsLes2(t *testing.T) { testGetProofs(t, 2) }
|
func TestGetProofsLes2(t *testing.T) { testGetProofs(t, 2) }
|
||||||
|
|
||||||
func testGetProofs(t *testing.T, protocol int) {
|
func testGetProofs(t *testing.T, protocol int) {
|
||||||
|
|
@ -359,7 +336,7 @@ func testGetProofs(t *testing.T, protocol int) {
|
||||||
for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ {
|
for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ {
|
||||||
header := bc.GetHeaderByNumber(i)
|
header := bc.GetHeaderByNumber(i)
|
||||||
root := header.Root
|
root := header.Root
|
||||||
trie, _ := trie.New(root, db)
|
trie, _ := trie.New(root, trie.NewDatabase(db))
|
||||||
|
|
||||||
for _, acc := range accounts {
|
for _, acc := range accounts {
|
||||||
req := ProofReq{
|
req := ProofReq{
|
||||||
|
|
@ -389,27 +366,126 @@ func testGetProofs(t *testing.T, protocol int) {
|
||||||
case 2:
|
case 2:
|
||||||
cost := peer.GetRequestCost(GetProofsV2Msg, len(proofreqs))
|
cost := peer.GetRequestCost(GetProofsV2Msg, len(proofreqs))
|
||||||
sendRequest(peer.app, GetProofsV2Msg, 42, cost, proofreqs)
|
sendRequest(peer.app, GetProofsV2Msg, 42, cost, proofreqs)
|
||||||
msg, err := peer.app.ReadMsg()
|
if err := expectResponse(peer.app, ProofsV2Msg, 42, testBufLimit, proofsV2.NodeList()); err != nil {
|
||||||
if err != nil {
|
t.Errorf("proofs mismatch: %v", err)
|
||||||
t.Errorf("Message read error: %v", err)
|
|
||||||
}
|
}
|
||||||
var resp struct {
|
}
|
||||||
ReqID, BV uint64
|
}
|
||||||
Data light.NodeList
|
|
||||||
|
// Tests that CHT proofs can be correctly retrieved.
|
||||||
|
func TestGetCHTProofsLes1(t *testing.T) { testGetCHTProofs(t, 1) }
|
||||||
|
func TestGetCHTProofsLes2(t *testing.T) { testGetCHTProofs(t, 2) }
|
||||||
|
|
||||||
|
func testGetCHTProofs(t *testing.T, protocol int) {
|
||||||
|
// Figure out the client's CHT frequency
|
||||||
|
frequency := uint64(light.CHTFrequencyClient)
|
||||||
|
if protocol == 1 {
|
||||||
|
frequency = uint64(light.CHTFrequencyServer)
|
||||||
|
}
|
||||||
|
// Assemble the test environment
|
||||||
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
pm := newTestProtocolManagerMust(t, false, int(frequency)+light.HelperTrieProcessConfirmations, testChainGen, nil, nil, db)
|
||||||
|
bc := pm.blockchain.(*core.BlockChain)
|
||||||
|
peer, _ := newTestPeer(t, "peer", protocol, pm, true)
|
||||||
|
defer peer.close()
|
||||||
|
|
||||||
|
// Wait a while for the CHT indexer to process the new headers
|
||||||
|
time.Sleep(100 * time.Millisecond * time.Duration(frequency/light.CHTFrequencyServer)) // Chain indexer throttling
|
||||||
|
time.Sleep(250 * time.Millisecond) // CI tester slack
|
||||||
|
|
||||||
|
// Assemble the proofs from the different protocols
|
||||||
|
header := bc.GetHeaderByNumber(frequency)
|
||||||
|
rlp, _ := rlp.EncodeToBytes(header)
|
||||||
|
|
||||||
|
key := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(key, frequency)
|
||||||
|
|
||||||
|
proofsV1 := []ChtResp{{
|
||||||
|
Header: header,
|
||||||
|
}}
|
||||||
|
proofsV2 := HelperTrieResps{
|
||||||
|
AuxData: [][]byte{rlp},
|
||||||
|
}
|
||||||
|
switch protocol {
|
||||||
|
case 1:
|
||||||
|
root := light.GetChtRoot(db, 0, bc.GetHeaderByNumber(frequency-1).Hash())
|
||||||
|
trie, _ := trie.New(root, trie.NewDatabase(ethdb.NewTable(db, light.ChtTablePrefix)))
|
||||||
|
|
||||||
|
var proof light.NodeList
|
||||||
|
trie.Prove(key, 0, &proof)
|
||||||
|
proofsV1[0].Proof = proof
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
root := light.GetChtV2Root(db, 0, bc.GetHeaderByNumber(frequency-1).Hash())
|
||||||
|
trie, _ := trie.New(root, trie.NewDatabase(ethdb.NewTable(db, light.ChtTablePrefix)))
|
||||||
|
trie.Prove(key, 0, &proofsV2.Proofs)
|
||||||
|
}
|
||||||
|
// Assemble the requests for the different protocols
|
||||||
|
requestsV1 := []ChtReq{{
|
||||||
|
ChtNum: 1,
|
||||||
|
BlockNum: frequency,
|
||||||
|
}}
|
||||||
|
requestsV2 := []HelperTrieReq{{
|
||||||
|
Type: htCanonical,
|
||||||
|
TrieIdx: 0,
|
||||||
|
Key: key,
|
||||||
|
AuxReq: auxHeader,
|
||||||
|
}}
|
||||||
|
// Send the proof request and verify the response
|
||||||
|
switch protocol {
|
||||||
|
case 1:
|
||||||
|
cost := peer.GetRequestCost(GetHeaderProofsMsg, len(requestsV1))
|
||||||
|
sendRequest(peer.app, GetHeaderProofsMsg, 42, cost, requestsV1)
|
||||||
|
if err := expectResponse(peer.app, HeaderProofsMsg, 42, testBufLimit, proofsV1); err != nil {
|
||||||
|
t.Errorf("proofs mismatch: %v", err)
|
||||||
}
|
}
|
||||||
if err := msg.Decode(&resp); err != nil {
|
case 2:
|
||||||
t.Errorf("reply decode error: %v", err)
|
cost := peer.GetRequestCost(GetHelperTrieProofsMsg, len(requestsV2))
|
||||||
|
sendRequest(peer.app, GetHelperTrieProofsMsg, 42, cost, requestsV2)
|
||||||
|
if err := expectResponse(peer.app, HelperTrieProofsMsg, 42, testBufLimit, proofsV2); err != nil {
|
||||||
|
t.Errorf("proofs mismatch: %v", err)
|
||||||
}
|
}
|
||||||
if msg.Code != ProofsV2Msg {
|
}
|
||||||
t.Errorf("Message code mismatch")
|
}
|
||||||
|
|
||||||
|
// Tests that bloombits proofs can be correctly retrieved.
|
||||||
|
func TestGetBloombitsProofs(t *testing.T) {
|
||||||
|
// Assemble the test environment
|
||||||
|
db, _ := ethdb.NewMemDatabase()
|
||||||
|
pm := newTestProtocolManagerMust(t, false, light.BloomTrieFrequency+256, testChainGen, nil, nil, db)
|
||||||
|
bc := pm.blockchain.(*core.BlockChain)
|
||||||
|
peer, _ := newTestPeer(t, "peer", 2, pm, true)
|
||||||
|
defer peer.close()
|
||||||
|
|
||||||
|
// Wait a while for the bloombits indexer to process the new headers
|
||||||
|
time.Sleep(100 * time.Millisecond * time.Duration(light.BloomTrieFrequency/4096)) // Chain indexer throttling
|
||||||
|
time.Sleep(250 * time.Millisecond) // CI tester slack
|
||||||
|
|
||||||
|
// Request and verify each bit of the bloom bits proofs
|
||||||
|
for bit := 0; bit < 2048; bit++ {
|
||||||
|
// Assemble therequest and proofs for the bloombits
|
||||||
|
key := make([]byte, 10)
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint16(key[:2], uint16(bit))
|
||||||
|
binary.BigEndian.PutUint64(key[2:], uint64(light.BloomTrieFrequency))
|
||||||
|
|
||||||
|
requests := []HelperTrieReq{{
|
||||||
|
Type: htBloomBits,
|
||||||
|
TrieIdx: 0,
|
||||||
|
Key: key,
|
||||||
|
}}
|
||||||
|
var proofs HelperTrieResps
|
||||||
|
|
||||||
|
root := light.GetBloomTrieRoot(db, 0, bc.GetHeaderByNumber(light.BloomTrieFrequency-1).Hash())
|
||||||
|
trie, _ := trie.New(root, trie.NewDatabase(ethdb.NewTable(db, light.BloomTrieTablePrefix)))
|
||||||
|
trie.Prove(key, 0, &proofs.Proofs)
|
||||||
|
|
||||||
|
// Send the proof request and verify the response
|
||||||
|
cost := peer.GetRequestCost(GetHelperTrieProofsMsg, len(requests))
|
||||||
|
sendRequest(peer.app, GetHelperTrieProofsMsg, 42, cost, requests)
|
||||||
|
if err := expectResponse(peer.app, HelperTrieProofsMsg, 42, testBufLimit, proofs); err != nil {
|
||||||
|
t.Errorf("bit %d: proofs mismatch: %v", bit, err)
|
||||||
}
|
}
|
||||||
if resp.ReqID != 42 {
|
|
||||||
t.Errorf("ReqID mismatch")
|
|
||||||
}
|
|
||||||
if resp.BV != testBufLimit {
|
|
||||||
t.Errorf("BV mismatch")
|
|
||||||
}
|
|
||||||
testCheckProof(t, proofsV2, resp.Data)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import (
|
||||||
"github.com/EthereumCommonwealth/go-callisto/core/types"
|
"github.com/EthereumCommonwealth/go-callisto/core/types"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/core/vm"
|
"github.com/EthereumCommonwealth/go-callisto/core/vm"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/crypto"
|
"github.com/EthereumCommonwealth/go-callisto/crypto"
|
||||||
|
"github.com/EthereumCommonwealth/go-callisto/eth"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/ethdb"
|
"github.com/EthereumCommonwealth/go-callisto/ethdb"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/event"
|
"github.com/EthereumCommonwealth/go-callisto/event"
|
||||||
"github.com/EthereumCommonwealth/go-callisto/les/flowcontrol"
|
"github.com/EthereumCommonwealth/go-callisto/les/flowcontrol"
|
||||||
|
|
@ -55,6 +56,9 @@ var (
|
||||||
testContractCodeDeployed = testContractCode[16:]
|
testContractCodeDeployed = testContractCode[16:]
|
||||||
testContractDeployed = uint64(2)
|
testContractDeployed = uint64(2)
|
||||||
|
|
||||||
|
testEventEmitterCode = common.Hex2Bytes("60606040523415600e57600080fd5b7f57050ab73f6b9ebdd9f76b8d4997793f48cf956e965ee070551b9ca0bb71584e60405160405180910390a160358060476000396000f3006060604052600080fd00a165627a7a723058203f727efcad8b5811f8cb1fc2620ce5e8c63570d697aef968172de296ea3994140029")
|
||||||
|
testEventEmitterAddr common.Address
|
||||||
|
|
||||||
testBufLimit = uint64(100)
|
testBufLimit = uint64(100)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -85,15 +89,19 @@ func testChainGen(i int, block *core.BlockGen) {
|
||||||
// In block 2, the test bank sends some more ether to account #1.
|
// In block 2, the test bank sends some more ether to account #1.
|
||||||
// acc1Addr passes it on to account #2.
|
// acc1Addr passes it on to account #2.
|
||||||
// acc1Addr creates a test contract.
|
// acc1Addr creates a test contract.
|
||||||
tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
|
// acc1Addr creates a test event.
|
||||||
nonce := block.TxNonce(acc1Addr)
|
nonce := block.TxNonce(acc1Addr)
|
||||||
|
|
||||||
|
tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey)
|
||||||
tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
|
tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key)
|
||||||
nonce++
|
tx3, _ := types.SignTx(types.NewContractCreation(nonce+1, big.NewInt(0), 200000, big.NewInt(0), testContractCode), signer, acc1Key)
|
||||||
tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 200000, big.NewInt(0), testContractCode), signer, acc1Key)
|
testContractAddr = crypto.CreateAddress(acc1Addr, nonce+1)
|
||||||
testContractAddr = crypto.CreateAddress(acc1Addr, nonce)
|
tx4, _ := types.SignTx(types.NewContractCreation(nonce+2, big.NewInt(0), 200000, big.NewInt(0), testEventEmitterCode), signer, acc1Key)
|
||||||
|
testEventEmitterAddr = crypto.CreateAddress(acc1Addr, nonce+2)
|
||||||
block.AddTx(tx1)
|
block.AddTx(tx1)
|
||||||
block.AddTx(tx2)
|
block.AddTx(tx2)
|
||||||
block.AddTx(tx3)
|
block.AddTx(tx3)
|
||||||
|
block.AddTx(tx4)
|
||||||
case 2:
|
case 2:
|
||||||
// Block 3 is empty but was mined by account #2.
|
// Block 3 is empty but was mined by account #2.
|
||||||
block.SetCoinbase(acc2Addr)
|
block.SetCoinbase(acc2Addr)
|
||||||
|
|
@ -146,7 +154,17 @@ func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *cor
|
||||||
if lightSync {
|
if lightSync {
|
||||||
chain, _ = light.NewLightChain(odr, gspec.Config, engine)
|
chain, _ = light.NewLightChain(odr, gspec.Config, engine)
|
||||||
} else {
|
} else {
|
||||||
blockchain, _ := core.NewBlockChain(db, gspec.Config, engine, vm.Config{})
|
blockchain, _ := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{})
|
||||||
|
|
||||||
|
chtIndexer := light.NewChtIndexer(db, false)
|
||||||
|
chtIndexer.Start(blockchain)
|
||||||
|
|
||||||
|
bbtIndexer := light.NewBloomTrieIndexer(db, false)
|
||||||
|
|
||||||
|
bloomIndexer := eth.NewBloomIndexer(db, params.BloomBitsBlocks)
|
||||||
|
bloomIndexer.AddChildIndexer(bbtIndexer)
|
||||||
|
bloomIndexer.Start(blockchain)
|
||||||
|
|
||||||
gchain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator)
|
gchain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator)
|
||||||
if _, err := blockchain.InsertChain(gchain); err != nil {
|
if _, err := blockchain.InsertChain(gchain); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
@ -176,7 +194,7 @@ func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *cor
|
||||||
srv.fcManager = flowcontrol.NewClientManager(50, 10, 1000000000)
|
srv.fcManager = flowcontrol.NewClientManager(50, 10, 1000000000)
|
||||||
srv.fcCostStats = newCostStats(nil)
|
srv.fcCostStats = newCostStats(nil)
|
||||||
}
|
}
|
||||||
pm.Start()
|
pm.Start(1000)
|
||||||
return pm, nil
|
return pm, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,9 +245,12 @@ func newTestPeer(t *testing.T, name string, version int, pm *ProtocolManager, sh
|
||||||
}
|
}
|
||||||
// Execute any implicitly requested handshakes and return
|
// Execute any implicitly requested handshakes and return
|
||||||
if shake {
|
if shake {
|
||||||
td, head, genesis := pm.blockchain.Status()
|
var (
|
||||||
headNum := pm.blockchain.CurrentHeader().Number.Uint64()
|
genesis = pm.blockchain.Genesis()
|
||||||
tp.handshake(t, td, head, headNum, genesis)
|
head = pm.blockchain.CurrentHeader()
|
||||||
|
td = pm.blockchain.GetTd(head.Hash(), head.Number.Uint64())
|
||||||
|
)
|
||||||
|
tp.handshake(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash())
|
||||||
}
|
}
|
||||||
return tp, errc
|
return tp, errc
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -321,7 +321,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type HelperTrieReq struct {
|
type HelperTrieReq struct {
|
||||||
HelperTrieType uint
|
Type uint
|
||||||
TrieIdx uint64
|
TrieIdx uint64
|
||||||
Key []byte
|
Key []byte
|
||||||
FromLevel, AuxReq uint
|
FromLevel, AuxReq uint
|
||||||
|
|
@ -365,7 +365,7 @@ func (r *ChtRequest) CanSend(peer *peer) bool {
|
||||||
peer.lock.RLock()
|
peer.lock.RLock()
|
||||||
defer peer.lock.RUnlock()
|
defer peer.lock.RUnlock()
|
||||||
|
|
||||||
return peer.headInfo.Number >= light.HelperTrieConfirmations && r.ChtNum <= (peer.headInfo.Number-light.HelperTrieConfirmations)/light.ChtFrequency
|
return peer.headInfo.Number >= light.HelperTrieConfirmations && r.ChtNum <= (peer.headInfo.Number-light.HelperTrieConfirmations)/light.CHTFrequencyClient
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
|
// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
|
||||||
|
|
@ -374,10 +374,10 @@ func (r *ChtRequest) Request(reqID uint64, peer *peer) error {
|
||||||
var encNum [8]byte
|
var encNum [8]byte
|
||||||
binary.BigEndian.PutUint64(encNum[:], r.BlockNum)
|
binary.BigEndian.PutUint64(encNum[:], r.BlockNum)
|
||||||
req := HelperTrieReq{
|
req := HelperTrieReq{
|
||||||
HelperTrieType: htCanonical,
|
Type: htCanonical,
|
||||||
TrieIdx: r.ChtNum,
|
TrieIdx: r.ChtNum,
|
||||||
Key: encNum[:],
|
Key: encNum[:],
|
||||||
AuxReq: auxHeader,
|
AuxReq: auxHeader,
|
||||||
}
|
}
|
||||||
return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), []HelperTrieReq{req})
|
return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), []HelperTrieReq{req})
|
||||||
}
|
}
|
||||||
|
|
@ -493,14 +493,14 @@ func (r *BloomRequest) Request(reqID uint64, peer *peer) error {
|
||||||
reqs := make([]HelperTrieReq, len(r.SectionIdxList))
|
reqs := make([]HelperTrieReq, len(r.SectionIdxList))
|
||||||
|
|
||||||
var encNumber [10]byte
|
var encNumber [10]byte
|
||||||
binary.BigEndian.PutUint16(encNumber[0:2], uint16(r.BitIdx))
|
binary.BigEndian.PutUint16(encNumber[:2], uint16(r.BitIdx))
|
||||||
|
|
||||||
for i, sectionIdx := range r.SectionIdxList {
|
for i, sectionIdx := range r.SectionIdxList {
|
||||||
binary.BigEndian.PutUint64(encNumber[2:10], sectionIdx)
|
binary.BigEndian.PutUint64(encNumber[2:], sectionIdx)
|
||||||
reqs[i] = HelperTrieReq{
|
reqs[i] = HelperTrieReq{
|
||||||
HelperTrieType: htBloomBits,
|
Type: htBloomBits,
|
||||||
TrieIdx: r.BloomTrieNum,
|
TrieIdx: r.BloomTrieNum,
|
||||||
Key: common.CopyBytes(encNumber[:]),
|
Key: common.CopyBytes(encNumber[:]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), reqs)
|
return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), reqs)
|
||||||
|
|
@ -525,10 +525,10 @@ func (r *BloomRequest) Validate(db ethdb.Database, msg *Msg) error {
|
||||||
|
|
||||||
// Verify the proofs
|
// Verify the proofs
|
||||||
var encNumber [10]byte
|
var encNumber [10]byte
|
||||||
binary.BigEndian.PutUint16(encNumber[0:2], uint16(r.BitIdx))
|
binary.BigEndian.PutUint16(encNumber[:2], uint16(r.BitIdx))
|
||||||
|
|
||||||
for i, idx := range r.SectionIdxList {
|
for i, idx := range r.SectionIdxList {
|
||||||
binary.BigEndian.PutUint64(encNumber[2:10], idx)
|
binary.BigEndian.PutUint64(encNumber[2:], idx)
|
||||||
value, err, _ := trie.VerifyProof(r.BloomTrieRoot, encNumber[:], reads)
|
value, err, _ := trie.VerifyProof(r.BloomTrieRoot, encNumber[:], reads)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue