Merge pull request #1 from ethereum/master

my build
This commit is contained in:
Abes 2019-11-11 18:30:09 +01:00 committed by GitHub
commit db26bfd37c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
584 changed files with 365262 additions and 9049 deletions

View file

@ -1,6 +1,6 @@
Hi there, Hi there,
please note that this is an issue tracker reserved for bug reports and feature requests. Please note that this is an issue tracker reserved for bug reports and feature requests.
For general questions please use the gitter channel or the Ethereum stack exchange at https://ethereum.stackexchange.com. For general questions please use the gitter channel or the Ethereum stack exchange at https://ethereum.stackexchange.com.

View file

@ -1,32 +1,58 @@
language: go language: go
go_import_path: github.com/ethereum/go-ethereum go_import_path: github.com/ethereum/go-ethereum
sudo: false sudo: false
matrix: jobs:
include: include:
- os: linux # This builder only tests code linters on latest version of Go
- stage: lint
os: linux
dist: xenial dist: xenial
go: 1.10.x go: 1.13.x
env:
- lint
git:
submodules: false # avoid cloning ethereum/tests
script: script:
- go run build/ci.go install - go run build/ci.go lint
- go run build/ci.go test -coverage $TEST_PACKAGES
- os: linux - stage: build
os: linux
dist: xenial dist: xenial
go: 1.11.x go: 1.11.x
script: script:
- go run build/ci.go install - go run build/ci.go install
- go run build/ci.go test -coverage $TEST_PACKAGES - go run build/ci.go test -coverage $TEST_PACKAGES
# These are the latest Go versions. - stage: build
- os: linux os: linux
dist: xenial dist: xenial
go: 1.12.x go: 1.12.x
script: script:
- go run build/ci.go install - go run build/ci.go install
- go run build/ci.go test -coverage $TEST_PACKAGES - go run build/ci.go test -coverage $TEST_PACKAGES
- os: osx # These are the latest Go versions.
go: 1.12.x - stage: build
os: linux
arch: amd64
dist: xenial
go: 1.13.x
script:
- go run build/ci.go install
- go run build/ci.go test -coverage $TEST_PACKAGES
- stage: build
os: linux
arch: arm64
dist: xenial
go: 1.13.x
script:
- go run build/ci.go install
- go run build/ci.go test -coverage $TEST_PACKAGES
- stage: build
os: osx
go: 1.13.x
script: script:
- echo "Increase the maximum number of open file descriptors on macOS" - echo "Increase the maximum number of open file descriptors on macOS"
- NOFILE=20480 - NOFILE=20480
@ -40,22 +66,12 @@ matrix:
- go run build/ci.go install - go run build/ci.go install
- go run build/ci.go test -coverage $TEST_PACKAGES - go run build/ci.go test -coverage $TEST_PACKAGES
# This builder only tests code linters on latest version of Go
- os: linux
dist: xenial
go: 1.12.x
env:
- lint
git:
submodules: false # avoid cloning ethereum/tests
script:
- go run build/ci.go lint
# This builder does the Ubuntu PPA upload # This builder does the Ubuntu PPA upload
- if: type = push - stage: build
if: type = push
os: linux os: linux
dist: xenial dist: xenial
go: 1.12.x go: 1.13.x
env: env:
- ubuntu-ppa - ubuntu-ppa
git: git:
@ -69,16 +85,20 @@ matrix:
- fakeroot - fakeroot
- python-bzrlib - python-bzrlib
- python-paramiko - python-paramiko
cache:
directories:
- $HOME/.gobundle
script: script:
- echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts - echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts
- go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>" - go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>" -goversion 1.13.4 -gohash 95dbeab442ee2746b9acf0934c8e2fc26414a0565c008631b04addb8c02e7624 -gobundle $HOME/.gobundle/go.tar.gz
# This builder does the Linux Azure uploads # This builder does the Linux Azure uploads
- if: type = push - stage: build
if: type = push
os: linux os: linux
dist: xenial dist: xenial
sudo: required sudo: required
go: 1.12.x go: 1.13.x
env: env:
- azure-linux - azure-linux
git: git:
@ -108,12 +128,13 @@ matrix:
- go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
# This builder does the Linux Azure MIPS xgo uploads # This builder does the Linux Azure MIPS xgo uploads
- if: type = push - stage: build
if: type = push
os: linux os: linux
dist: xenial dist: xenial
services: services:
- docker - docker
go: 1.12.x go: 1.13.x
env: env:
- azure-linux-mips - azure-linux-mips
git: git:
@ -136,7 +157,8 @@ matrix:
- go run build/ci.go archive -arch mips64le -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds - go run build/ci.go archive -arch mips64le -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
# This builder does the Android Maven and Azure uploads # This builder does the Android Maven and Azure uploads
- if: type = push - stage: build
if: type = push
os: linux os: linux
dist: xenial dist: xenial
addons: addons:
@ -158,7 +180,7 @@ matrix:
git: git:
submodules: false # avoid cloning ethereum/tests submodules: false # avoid cloning ethereum/tests
before_install: before_install:
- curl https://dl.google.com/go/go1.12.linux-amd64.tar.gz | tar -xz - curl https://dl.google.com/go/go1.13.linux-amd64.tar.gz | tar -xz
- export PATH=`pwd`/go/bin:$PATH - export PATH=`pwd`/go/bin:$PATH
- export GOROOT=`pwd`/go - export GOROOT=`pwd`/go
- export GOPATH=$HOME/go - export GOPATH=$HOME/go
@ -173,9 +195,10 @@ matrix:
- go run build/ci.go aar -signer ANDROID_SIGNING_KEY -deploy https://oss.sonatype.org -upload gethstore/builds - go run build/ci.go aar -signer ANDROID_SIGNING_KEY -deploy https://oss.sonatype.org -upload gethstore/builds
# This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads # This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads
- if: type = push - stage: build
if: type = push
os: osx os: osx
go: 1.12.x go: 1.13.x
env: env:
- azure-osx - azure-osx
- azure-ios - azure-ios
@ -202,10 +225,11 @@ matrix:
- 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
- if: type = cron - stage: build
if: type = cron
os: linux os: linux
dist: xenial dist: xenial
go: 1.12.x go: 1.13.x
env: env:
- azure-purge - azure-purge
git: git:

247
AUTHORS
View file

@ -1,5 +1,11 @@
# This is the official list of go-ethereum authors for copyright purposes. # This is the official list of go-ethereum authors for copyright purposes.
a e r t h <aerth@users.noreply.github.com>
Abel Nieto <abel.nieto90@gmail.com>
Abel Nieto <anietoro@uwaterloo.ca>
Adam Babik <a.babik@designfortress.com>
Aditya <adityasripal@gmail.com>
Adrià Cidre <adria.cidre@gmail.com>
Afri Schoedon <5chdn@users.noreply.github.com> Afri Schoedon <5chdn@users.noreply.github.com>
Agustin Armellini Fischer <armellini13@gmail.com> Agustin Armellini Fischer <armellini13@gmail.com>
Airead <fgh1987168@gmail.com> Airead <fgh1987168@gmail.com>
@ -10,165 +16,354 @@ Alex Leverington <alex@ethdev.com>
Alex Wu <wuyiding@gmail.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> Ali Hajimirza <Ali92hm@users.noreply.github.com>
am2rican5 <am2rican5@gmail.com>
Andrea Franz <andrea@gravityblast.com>
Andrey Petrov <andrey.petrov@shazow.net>
Andrey Petrov <shazow@gmail.com>
ANOTHEL <anothel1@naver.com>
Antoine Rondelet <rondelet.antoine@gmail.com>
Anton Evangelatov <anton.evangelatov@gmail.com> Anton Evangelatov <anton.evangelatov@gmail.com>
Antonio Salazar Cardozo <savedfastcool@gmail.com>
Arba Sasmoyo <arba.sasmoyo@gmail.com> Arba Sasmoyo <arba.sasmoyo@gmail.com>
Armani Ferrante <armaniferrante@berkeley.edu> Armani Ferrante <armaniferrante@berkeley.edu>
Armin Braun <me@obrown.io> Armin Braun <me@obrown.io>
Aron Fischer <github@aron.guru> Aron Fischer <github@aron.guru>
atsushi-ishibashi <atsushi.ishibashi@finatext.com>
ayeowch <ayeowch@gmail.com>
b00ris <b00ris@mail.ru>
bailantaotao <Edwin@maicoin.com>
baizhenxuan <nkbai@163.com>
Balint Gabor <balint.g@gmail.com>
Bas van Kervel <bas@ethdev.com> Bas van Kervel <bas@ethdev.com>
Benjamin Brent <benjamin@benjaminbrent.com> Benjamin Brent <benjamin@benjaminbrent.com>
benma <mbencun@gmail.com>
Benoit Verkindt <benoit.verkindt@gmail.com> Benoit Verkindt <benoit.verkindt@gmail.com>
bloonfield <bloonfield@163.com>
Bo <bohende@gmail.com> Bo <bohende@gmail.com>
Bo Ye <boy.e.computer.1982@outlook.com> Bo Ye <boy.e.computer.1982@outlook.com>
Bob Glickstein <bobg@users.noreply.github.com> Bob Glickstein <bobg@users.noreply.github.com>
Brent <bmperrea@gmail.com>
Brian Schroeder <bts@gmail.com> Brian Schroeder <bts@gmail.com>
Bruno Škvorc <bruno@skvorc.me>
C. Brown <hackdom@majoolr.io>
Caesar Chad <BLUE.WEB.GEEK@gmail.com>
Casey Detrio <cdetrio@gmail.com> Casey Detrio <cdetrio@gmail.com>
CDsigma <cdsigma271@gmail.com>
changhong <changhong.yu@shanbay.com>
Chase Wright <mysticryuujin@gmail.com> Chase Wright <mysticryuujin@gmail.com>
Chen Quan <terasum@163.com>
chenyufeng <yufengcode@gmail.com>
Christian Muehlhaeuser <muesli@gmail.com>
Christoph Jentzsch <jentzsch.software@gmail.com> Christoph Jentzsch <jentzsch.software@gmail.com>
cong <ackratos@users.noreply.github.com>
Corey Lin <514971757@qq.com>
cpusoft <cpusoft@live.com>
Crispin Flowerday <crispin@bitso.com>
croath <croathliu@gmail.com>
cui <523516579@qq.com>
Dan Kinsley <dan@joincivil.com>
Daniel A. Nagy <nagy.da@gmail.com> Daniel A. Nagy <nagy.da@gmail.com>
Daniel Sloof <goapsychadelic@gmail.com> Daniel Sloof <goapsychadelic@gmail.com>
Darrel Herbst <dherbst@gmail.com> Darrel Herbst <dherbst@gmail.com>
Dave Appleton <calistralabs@gmail.com> Dave Appleton <calistralabs@gmail.com>
Dave McGregor <dave.s.mcgregor@gmail.com>
David Huie <dahuie@gmail.com>
Derek Gottfrid <derek@codecubed.com>
Diego Siqueira <DiSiqueira@users.noreply.github.com> Diego Siqueira <DiSiqueira@users.noreply.github.com>
Diep Pham <mrfavadi@gmail.com>
dipingxian2 <39109351+dipingxian2@users.noreply.github.com>
dm4 <sunrisedm4@gmail.com>
Dmitrij Koniajev <dimchansky@gmail.com>
Dmitry Shulyak <yashulyak@gmail.com> Dmitry Shulyak <yashulyak@gmail.com>
Domino Valdano <dominoplural@gmail.com>
Domino Valdano <jeff@okcupid.com>
Dragan Milic <dragan@netice9.com>
dragonvslinux <35779158+dragononcrypto@users.noreply.github.com>
Egon Elbre <egonelbre@gmail.com> Egon Elbre <egonelbre@gmail.com>
Elad <theman@elad.im>
Eli <elihanover@yahoo.com>
Elias Naur <elias.naur@gmail.com> Elias Naur <elias.naur@gmail.com>
Elliot Shepherd <elliot@identitii.com> Elliot Shepherd <elliot@identitii.com>
Emil <mursalimovemeel@gmail.com>
emile <emile@users.noreply.github.com>
Enrique Fynn <enriquefynn@gmail.com> Enrique Fynn <enriquefynn@gmail.com>
Enrique Fynn <me@enriquefynn.com>
EOS Classic <info@eos-classic.io>
Erichin <erichinbato@gmail.com>
Ernesto del Toro <ernesto.deltoro@gmail.com> Ernesto del Toro <ernesto.deltoro@gmail.com>
Ethan Buchman <ethan@coinculture.info> Ethan Buchman <ethan@coinculture.info>
ethersphere <thesw@rm.eth>
Eugene Valeyev <evgen.povt@gmail.com> Eugene Valeyev <evgen.povt@gmail.com>
Evangelos Pappas <epappas@evalonlabs.com> Evangelos Pappas <epappas@evalonlabs.com>
Evgeny <awesome.observer@yandex.com>
Evgeny Danilenko <6655321@bk.ru> Evgeny Danilenko <6655321@bk.ru>
evgk <evgeniy.kamyshev@gmail.com>
Fabian Vogelsteller <fabian@frozeman.de> Fabian Vogelsteller <fabian@frozeman.de>
Fabio Barone <fabio.barone.co@gmail.com> Fabio Barone <fabio.barone.co@gmail.com>
Fabio Berger <fabioberger1991@gmail.com> Fabio Berger <fabioberger1991@gmail.com>
FaceHo <facehoshi@gmail.com> FaceHo <facehoshi@gmail.com>
Felix Lange <fjl@twurst.com> Felix Lange <fjl@twurst.com>
Ferenc Szabo <frncmx@gmail.com>
ferhat elmas <elmas.ferhat@gmail.com>
Fiisio <liangcszzu@163.com> Fiisio <liangcszzu@163.com>
Frank Szendzielarz <33515470+FrankSzendzielarz@users.noreply.github.com>
Frank Wang <eternnoir@gmail.com> Frank Wang <eternnoir@gmail.com>
Franklin <mr_franklin@126.com>
Furkan KAMACI <furkankamaci@gmail.com> Furkan KAMACI <furkankamaci@gmail.com>
GagziW <leon.stanko@rwth-aachen.de>
Gary Rong <garyrong0905@gmail.com> Gary Rong <garyrong0905@gmail.com>
George Ornbo <george@shapeshed.com> George Ornbo <george@shapeshed.com>
Gregg Dourgarian <greggd@tempworks.com> Gregg Dourgarian <greggd@tempworks.com>
Guilherme Salgado <gsalgado@gmail.com>
Guillaume Ballet <gballet@gmail.com> Guillaume Ballet <gballet@gmail.com>
Guillaume Nicolas <guin56@gmail.com> Guillaume Nicolas <guin56@gmail.com>
GuiltyMorishita <morilliantblue@gmail.com>
Gus <yo@soygus.com>
Gustav Simonsson <gustav.simonsson@gmail.com> Gustav Simonsson <gustav.simonsson@gmail.com>
Gísli Kristjánsson <gislik@hamstur.is>
Ha ĐANG <dvietha@gmail.com>
HackyMiner <hackyminer@gmail.com>
hadv <dvietha@gmail.com>
Hao Bryan Cheng <haobcheng@gmail.com> Hao Bryan Cheng <haobcheng@gmail.com>
HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Henning Diedrich <hd@eonblast.com> Henning Diedrich <hd@eonblast.com>
holisticode <holistic.computing@gmail.com>
Hongbin Mao <hello2mao@gmail.com>
Hsien-Tang Kao <htkao@pm.me>
Husam Ibrahim <39692071+HusamIbrahim@users.noreply.github.com>
hydai <z54981220@gmail.com>
Hyung-Kyu Hqueue Choi <hyungkyu.choi@gmail.com>
Ian Macalinao <me@ian.pw>
Ian Norden <iannordenn@gmail.com>
Isidoro Ghezzi <isidoro.ghezzi@icloud.com> Isidoro Ghezzi <isidoro.ghezzi@icloud.com>
Iskander (Alex) Sharipov <quasilyte@gmail.com>
Ivan Daniluk <ivan.daniluk@gmail.com> Ivan Daniluk <ivan.daniluk@gmail.com>
Ivo Georgiev <ivo@strem.io>
Jae Kwon <jkwon.work@gmail.com> Jae Kwon <jkwon.work@gmail.com>
Jamie Pitts <james.pitts@gmail.com> Jamie Pitts <james.pitts@gmail.com>
Janos Guljas <janos@resenje.org>
Janoš Guljaš <janos@users.noreply.github.com> Janoš Guljaš <janos@users.noreply.github.com>
Jason Carver <jacarver@linkedin.com> Jason Carver <jacarver@linkedin.com>
Javier Peletier <jm@epiclabs.io>
Javier Peletier <jpeletier@users.noreply.github.com>
Javier Sagredo <jasataco@gmail.com>
Jay <codeholic.arena@gmail.com>
Jay Guo <guojiannan1101@gmail.com> Jay Guo <guojiannan1101@gmail.com>
Jaynti Kanani <jdkanani@gmail.com>
Jeff Prestes <jeffprestes@gmail.com>
Jeff R. Allen <jra@nella.org> Jeff R. Allen <jra@nella.org>
Jeffery Robert Walsh <rlxrlps@gmail.com>
Jeffrey Wilcke <jeffrey@ethereum.org> Jeffrey Wilcke <jeffrey@ethereum.org>
Jens Agerberg <github@agerberg.me> Jens Agerberg <github@agerberg.me>
Jeremy McNevin <jeremy.mcnevin@optum.com>
Jeremy Schlatter <jeremy.schlatter@gmail.com>
Jerzy Lasyk <jerzylasyk@gmail.com>
Jia Chenhui <jiachenhui1989@gmail.com> Jia Chenhui <jiachenhui1989@gmail.com>
Jim McDonald <Jim@mcdee.net> Jim McDonald <Jim@mcdee.net>
jkcomment <jkcomment@gmail.com>
Joel Burget <joelburget@gmail.com> Joel Burget <joelburget@gmail.com>
John C. Vernaleo <john@netpurgatory.com>
Johns Beharry <johns@peakshift.com>
Jonas <felberj@users.noreply.github.com>
Jonathan Brown <jbrown@bluedroplet.com> Jonathan Brown <jbrown@bluedroplet.com>
JoranHonig <JoranHonig@users.noreply.github.com>
Jordan Krage <jmank88@gmail.com>
Joseph Chow <ethereum@outlook.com> Joseph Chow <ethereum@outlook.com>
jtakalai <juuso.takalainen@streamr.com>
JU HYEONG PARK <dkdkajej@gmail.com>
Justin Clark-Casey <justincc@justincc.org> Justin Clark-Casey <justincc@justincc.org>
Justin Drake <drakefjustin@gmail.com> Justin Drake <drakefjustin@gmail.com>
jwasinger <j-wasinger@hotmail.com>
ken10100147 <sunhongping@kanjian.com>
Kenji Siu <kenji@isuntv.com> Kenji Siu <kenji@isuntv.com>
Kenso Trabing <kenso.trabing@bloomwebsite.com>
Kenso Trabing <ktrabing@acm.org>
Kevin <denk.kevin@web.de>
kevin.xu <cming.xu@gmail.com>
kiel barry <kiel.j.barry@gmail.com>
kimmylin <30611210+kimmylin@users.noreply.github.com>
Kitten King <53072918+kittenking@users.noreply.github.com>
knarfeh <hejun1874@gmail.com>
Kobi Gurkan <kobigurk@gmail.com> Kobi Gurkan <kobigurk@gmail.com>
Konrad Feldmeier <konrad@brainbot.com> Konrad Feldmeier <konrad@brainbot.com>
Kris Shinn <raggamuffin.music@gmail.com>
Kurkó Mihály <kurkomisi@users.noreply.github.com> Kurkó Mihály <kurkomisi@users.noreply.github.com>
Kushagra Sharma <ksharm01@gmail.com>
Kwuaint <34888408+kwuaint@users.noreply.github.com>
Kyuntae Ethan Kim <ethan.kyuntae.kim@gmail.com> Kyuntae Ethan Kim <ethan.kyuntae.kim@gmail.com>
ledgerwatch <akhounov@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> Leo Shklovskii <leo@thermopylae.net>
LeoLiao <leofantast@gmail.com>
Lewis Marshall <lewis@lmars.net> Lewis Marshall <lewis@lmars.net>
lhendre <lhendre2@gmail.com>
Liang Ma <liangma.ul@gmail.com>
Liang Ma <liangma@liangbit.com>
Liang ZOU <liang.d.zou@gmail.com>
libotony <liboliqi@gmail.com>
ligi <ligi@ligi.de>
Lio李欧 <lionello@users.noreply.github.com> Lio李欧 <lionello@users.noreply.github.com>
Lorenzo Manacorda <lorenzo@kinvolk.io>
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> Magicking <s@6120.eu>
manlio <manlio.poltronieri@gmail.com>
Maran Hidskes <maran.hidskes@gmail.com> Maran Hidskes <maran.hidskes@gmail.com>
Marek Kotewicz <marek.kotewicz@gmail.com> Marek Kotewicz <marek.kotewicz@gmail.com>
Marius van der Wijden <m.vanderwijden@live.de>
Mark <markya0616@gmail.com> Mark <markya0616@gmail.com>
Mark Rushakoff <mark.rushakoff@gmail.com>
mark.lin <mark@maicoin.com>
Martin Alex Philip Dawson <u1356770@gmail.com>
Martin Holst Swende <martin@swende.se> Martin Holst Swende <martin@swende.se>
Martin Klepsch <martinklepsch@googlemail.com>
Mats Julian Olsen <mats@plysjbyen.net>
Matt K <1036969+mkrump@users.noreply.github.com>
Matthew Di Ferrante <mattdf@users.noreply.github.com> Matthew Di Ferrante <mattdf@users.noreply.github.com>
Matthew Halpern <matthalp@gmail.com>
Matthew Halpern <matthalp@google.com>
Matthew Wampler-Doty <matthew.wampler.doty@gmail.com> Matthew Wampler-Doty <matthew.wampler.doty@gmail.com>
Max Sistemich <mafrasi2@googlemail.com>
Maximilian Meister <mmeister@suse.de> Maximilian Meister <mmeister@suse.de>
Micah Zoltu <micah@zoltu.net> Micah Zoltu <micah@zoltu.net>
Michael Ruminer <michael.ruminer+github@gmail.com> Michael Ruminer <michael.ruminer+github@gmail.com>
Miguel Mota <miguelmota2@gmail.com> Miguel Mota <miguelmota2@gmail.com>
Miya Chen <miyatlchen@gmail.com> Miya Chen <miyatlchen@gmail.com>
Mohanson <mohanson@outlook.com>
mr_franklin <mr_franklin@126.com>
Mymskmkt <1847234666@qq.com>
Nalin Bhardwaj <nalinbhardwaj@nibnalin.me>
Nchinda Nchinda <nchinda2@gmail.com> Nchinda Nchinda <nchinda2@gmail.com>
necaremus <necaremus@gmail.com>
needkane <604476380@qq.com>
Nguyen Kien Trung <trung.n.k@gmail.com>
Nguyen Sy Thanh Son <thanhson1085@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> Nicolas Guillaume <gunicolas@sqli.com>
Nilesh Trivedi <nilesh@hypertrack.io>
Nimrod Gutman <nimrod.gutman@gmail.com>
njupt-moon <1015041018@njupt.edu.cn>
nkbai <nkbai@163.com>
nobody <ddean2009@163.com>
Noman <noman@noman.land> Noman <noman@noman.land>
Oleg Kovalov <iamolegkovalov@gmail.com>
Oli Bye <olibye@users.noreply.github.com> Oli Bye <olibye@users.noreply.github.com>
Osuke <arget-fee.free.dgm@hotmail.co.jp>
Paul Berg <hello@paulrberg.com>
Paul Litvak <litvakpol@012.net.il> 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> Paweł Bylica <chfast@gmail.com>
Pedro Pombeiro <PombeirP@users.noreply.github.com>
Peter Broadhurst <peter@themumbles.net>
Peter Pratscher <pratscher@gmail.com> Peter Pratscher <pratscher@gmail.com>
Petr Mikusek <petr@mikusek.info> Petr Mikusek <petr@mikusek.info>
Philip Schlump <pschlump@gmail.com>
Pierre Neter <pierreneter@gmail.com>
PilkyuJung <anothel1@naver.com>
protolambda <proto@protolambda.com>
Péter Szilágyi <peterke@gmail.com> Péter Szilágyi <peterke@gmail.com>
RJ Catalano <catalanor0220@gmail.com> qd-ethan <31876119+qdgogogo@users.noreply.github.com>
Raghav Sood <raghavsood@gmail.com>
Ralph Caraveo <deckarep@gmail.com>
Ralph Caraveo III <deckarep@gmail.com>
Ramesh Nair <ram@hiddentao.com> Ramesh Nair <ram@hiddentao.com>
reinerRubin <tolstov.georgij@gmail.com>
rhaps107 <dod-source@yandex.ru>
Ricardo Catalinas Jiménez <r@untroubled.be> Ricardo Catalinas Jiménez <r@untroubled.be>
Ricardo Domingos <ricardohsd@gmail.com> Ricardo Domingos <ricardohsd@gmail.com>
Richard Hart <richardhart92@gmail.com> Richard Hart <richardhart92@gmail.com>
RJ Catalano <catalanor0220@gmail.com>
Rob <robert@rojotek.com> Rob <robert@rojotek.com>
Rob Mulholand <rmulholand@8thlight.com>
Robert Zaremba <robert.zaremba@scale-it.pl> Robert Zaremba <robert.zaremba@scale-it.pl>
Roc Yu <rociiu0112@gmail.com>
Runchao Han <elvisage941102@gmail.com>
Russ Cox <rsc@golang.org> Russ Cox <rsc@golang.org>
Ryan Schneider <ryanleeschneider@gmail.com>
Rémy Roy <remyroy@remyroy.com> Rémy Roy <remyroy@remyroy.com>
S. Matthew English <s-matthew-english@users.noreply.github.com> S. Matthew English <s-matthew-english@users.noreply.github.com>
salanfe <salanfe@users.noreply.github.com>
Samuel Marks <samuelmarks@gmail.com>
Sarlor <kinsleer@outlook.com>
Sasuke1964 <neilperry1964@gmail.com>
Saulius Grigaitis <saulius@necolt.com>
Sean <darcys22@gmail.com>
Sheldon <11510383@mail.sustc.edu.cn>
Sheldon <374662347@qq.com>
Shintaro Kaneko <kaneshin0120@gmail.com> Shintaro Kaneko <kaneshin0120@gmail.com>
Shuai Qi <qishuai231@gmail.com>
Shunsuke Watanabe <ww.shunsuke@gmail.com>
silence <wangsai.silence@qq.com>
Simon Jentzsch <simon@slock.it>
slumber1122 <slumber1122@gmail.com>
Smilenator <yurivanenko@yandex.ru>
Sorin Neacsu <sorin.neacsu@gmail.com> Sorin Neacsu <sorin.neacsu@gmail.com>
Stein Dekker <dekker.stein@gmail.com> Stein Dekker <dekker.stein@gmail.com>
Steve Gattuso <steve@stevegattuso.me>
Steve Ruckdashel <steve.ruckdashel@gmail.com>
Steve Waldman <swaldman@mchange.com> Steve Waldman <swaldman@mchange.com>
Steven Roose <stevenroose@gmail.com> Steven Roose <stevenroose@gmail.com>
stompesi <stompesi@gmail.com>
stormpang <jialinpeng@vip.qq.com>
sunxiaojun2014 <sunxiaojun-xy@360.cn>
tamirms <tamir@trello.com>
Taylor Gerring <taylor.gerring@gmail.com> Taylor Gerring <taylor.gerring@gmail.com>
TColl <38299499+TColl@users.noreply.github.com>
terasum <terasum@163.com>
Thomas Bocek <tom@tomp2p.net> Thomas Bocek <tom@tomp2p.net>
thomasmodeneis <thomas.modeneis@gmail.com>
thumb8432 <thumb8432@gmail.com>
Ti Zhou <tizhou1986@gmail.com> Ti Zhou <tizhou1986@gmail.com>
Tosh Camille <tochecamille@gmail.com> Tosh Camille <tochecamille@gmail.com>
tsarpaul <Litvakpol@012.net.il>
tzapu <alex@tzapu.com>
ult-bobonovski <alex@ultiledger.io>
Valentin Wüstholz <wuestholz@gmail.com> Valentin Wüstholz <wuestholz@gmail.com>
Vedhavyas Singareddi <vedhavyas.singareddi@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>
Vie <yangchenzhong@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>
vim88 <vim88vim88@gmail.com>
Vincent G <caktux@gmail.com> Vincent G <caktux@gmail.com>
Vincent Serpoul <vincent@serpoul.com>
Vitalik Buterin <v@buterin.com> Vitalik Buterin <v@buterin.com>
Vitaly Bogdanov <vsbogd@gmail.com>
Vitaly V <vvelikodny@gmail.com> Vitaly V <vvelikodny@gmail.com>
Vivek Anand <vivekanand1101@users.noreply.github.com> Vivek Anand <vivekanand1101@users.noreply.github.com>
Vlad <gluk256@gmail.com>
Vlad Bokov <razum2um@mail.ru>
Vlad Gluhovsky <gluk256@users.noreply.github.com> Vlad Gluhovsky <gluk256@users.noreply.github.com>
weimumu <934657014@qq.com>
Wenbiao Zheng <delweng@gmail.com>
William Setzer <bootstrapsetzer@gmail.com>
williambannas <wrschwartz@wpi.edu>
Wuxiang <wuxiangzhou2010@gmail.com>
xiekeyang <xiekeyang@users.noreply.github.com>
xincaosu <xincaosu@126.com>
yahtoo <yahtoo.ma@gmail.com>
YaoZengzeng <yaozengzeng@zju.edu.cn>
YH-Zhou <yanhong.zhou05@gmail.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> Yondon Fu <yondon.fu@gmail.com>
YOSHIDA Masanori <masanori.yoshida@gmail.com>
yoza <yoza.is12s@gmail.com>
Yusup <awklsgrep@gmail.com>
Zach <zach.ramsay@gmail.com> Zach <zach.ramsay@gmail.com>
zah <zahary@gmail.com>
Zahoor Mohamed <zahoor@zahoor.in> Zahoor Mohamed <zahoor@zahoor.in>
Zak Cole <zak@beattiecole.com>
zer0to0ne <36526113+zer0to0ne@users.noreply.github.com>
Zhenguo Niu <Niu.ZGlinux@gmail.com>
Zoe Nolan <github@zoenolan.org> Zoe Nolan <github@zoenolan.org>
Zsolt Felföldi <zsfelfoldi@gmail.com> Zsolt Felföldi <zsfelfoldi@gmail.com>
am2rican5 <am2rican5@gmail.com> Łukasz Kurowski <crackcomm@users.noreply.github.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>
jtakalai <juuso.takalainen@streamr.com>
ken10100147 <sunhongping@kanjian.com>
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>
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>
Ralph Caraveo <deckarep@gmail.com> 大彬 <hz_stb@163.com>
贺鹏飞 <hpf@hackerful.cn>
유용환 <33824408+eric-yoo@users.noreply.github.com>

View file

@ -1,5 +1,5 @@
# Build Geth in a stock Go builder container # Build Geth in a stock Go builder container
FROM golang:1.12-alpine as builder FROM golang:1.13-alpine as builder
RUN apk add --no-cache make gcc musl-dev linux-headers git RUN apk add --no-cache make gcc musl-dev linux-headers git
@ -12,5 +12,5 @@ FROM alpine:latest
RUN apk add --no-cache ca-certificates RUN apk add --no-cache ca-certificates
COPY --from=builder /go-ethereum/build/bin/geth /usr/local/bin/ COPY --from=builder /go-ethereum/build/bin/geth /usr/local/bin/
EXPOSE 8545 8546 30303 30303/udp EXPOSE 8545 8546 8547 30303 30303/udp
ENTRYPOINT ["geth"] ENTRYPOINT ["geth"]

View file

@ -1,5 +1,5 @@
# Build Geth in a stock Go builder container # Build Geth in a stock Go builder container
FROM golang:1.12-alpine as builder FROM golang:1.13-alpine as builder
RUN apk add --no-cache make gcc musl-dev linux-headers git RUN apk add --no-cache make gcc musl-dev linux-headers git
@ -12,4 +12,4 @@ FROM alpine:latest
RUN apk add --no-cache ca-certificates RUN apk add --no-cache ca-certificates
COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/ COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/
EXPOSE 8545 8546 30303 30303/udp EXPOSE 8545 8546 8547 30303 30303/udp

View file

@ -8,7 +8,7 @@
.PHONY: geth-darwin geth-darwin-386 geth-darwin-amd64 .PHONY: geth-darwin geth-darwin-386 geth-darwin-amd64
.PHONY: geth-windows geth-windows-386 geth-windows-amd64 .PHONY: geth-windows geth-windows-386 geth-windows-amd64
GOBIN = $(shell pwd)/build/bin GOBIN = ./build/bin
GO ?= latest GO ?= latest
geth: geth:

View file

@ -98,7 +98,7 @@ Specifying the `--testnet` flag, however, will reconfigure your `geth` instance
this. this.
* Instead of connecting the main Ethereum network, the client will connect to the test * 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. network, which uses different P2P bootnodes, different network IDs and genesis states.
*Note: Although there are some internal protective measures to prevent transactions from *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 crossing 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 use separate accounts for play-money and real-money. Unless you manually move
@ -210,10 +210,14 @@ aware of and agree upon. This consists of a small JSON file (e.g. call it `genes
```json ```json
{ {
"config": { "config": {
"chainId": 0, "chainId": <arbitrary positive integer>,
"homesteadBlock": 0, "homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0, "eip155Block": 0,
"eip158Block": 0 "eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0
}, },
"alloc": {}, "alloc": {},
"coinbase": "0x0000000000000000000000000000000000000000", "coinbase": "0x0000000000000000000000000000000000000000",
@ -229,8 +233,8 @@ aware of and agree upon. This consists of a small JSON file (e.g. call it `genes
The above fields should be fine for most purposes, although we'd recommend changing The above fields should be fine for most purposes, although we'd recommend changing
the `nonce` to some random value so you prevent unknown remote nodes from being able the `nonce` to some random value so you prevent unknown remote nodes from being able
to connect to you. If you'd like to pre-fund some accounts for easier testing, you can to connect to you. If you'd like to pre-fund some accounts for easier testing, create
populate the `alloc` field with account configs: the accounts and populate the `alloc` field with their addresses.
```json ```json
"alloc": { "alloc": {
@ -299,7 +303,7 @@ ones either). To start a `geth` instance for mining, run it with all your usual
by: by:
```shell ```shell
$ geth <usual-flags> --mine --minerthreads=1 --etherbase=0x0000000000000000000000000000000000000000 $ geth <usual-flags> --mine --miner.threads=1 --etherbase=0x0000000000000000000000000000000000000000
``` ```
Which will start mining blocks and transactions on a single CPU thread, crediting all Which will start mining blocks and transactions on a single CPU thread, crediting all

View file

@ -70,14 +70,11 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
return nil, err return nil, err
} }
// Pack up the method ID too if not a constructor and return // Pack up the method ID too if not a constructor and return
return append(method.Id(), arguments...), nil return append(method.ID(), arguments...), nil
} }
// Unpack output in v according to the abi specification // Unpack output in v according to the abi specification
func (abi ABI) Unpack(v interface{}, name string, data []byte) (err error) { func (abi ABI) Unpack(v interface{}, name string, data []byte) (err error) {
if len(data) == 0 {
return fmt.Errorf("abi: unmarshalling empty output")
}
// since there can't be naming collisions with contracts and events, // since there can't be naming collisions with contracts and events,
// we need to decide whether we're calling a method or an event // we need to decide whether we're calling a method or an event
if method, ok := abi.Methods[name]; ok { if method, ok := abi.Methods[name]; ok {
@ -94,9 +91,6 @@ func (abi ABI) Unpack(v interface{}, name string, data []byte) (err error) {
// UnpackIntoMap unpacks a log into the provided map[string]interface{} // UnpackIntoMap unpacks a log into the provided map[string]interface{}
func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) { func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) {
if len(data) == 0 {
return fmt.Errorf("abi: unmarshalling empty output")
}
// since there can't be naming collisions with contracts and events, // since there can't be naming collisions with contracts and events,
// we need to decide whether we're calling a method or an event // we need to decide whether we're calling a method or an event
if method, ok := abi.Methods[name]; ok { if method, ok := abi.Methods[name]; ok {
@ -121,11 +115,9 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
Inputs []Argument Inputs []Argument
Outputs []Argument Outputs []Argument
} }
if err := json.Unmarshal(data, &fields); err != nil { if err := json.Unmarshal(data, &fields); err != nil {
return err return err
} }
abi.Methods = make(map[string]Method) abi.Methods = make(map[string]Method)
abi.Events = make(map[string]Event) abi.Events = make(map[string]Event)
for _, field := range fields { for _, field := range fields {
@ -144,6 +136,7 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
} }
abi.Methods[name] = Method{ abi.Methods[name] = Method{
Name: name, Name: name,
RawName: field.Name,
Const: field.Constant, Const: field.Constant,
Inputs: field.Inputs, Inputs: field.Inputs,
Outputs: field.Outputs, Outputs: field.Outputs,
@ -157,6 +150,7 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
} }
abi.Events[name] = Event{ abi.Events[name] = Event{
Name: name, Name: name,
RawName: field.Name,
Anonymous: field.Anonymous, Anonymous: field.Anonymous,
Inputs: field.Inputs, Inputs: field.Inputs,
} }
@ -173,7 +167,7 @@ func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata)) return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
} }
for _, method := range abi.Methods { for _, method := range abi.Methods {
if bytes.Equal(method.Id(), sigdata[:4]) { if bytes.Equal(method.ID(), sigdata[:4]) {
return &method, nil return &method, nil
} }
} }
@ -184,7 +178,7 @@ func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
// ABI and returns nil if none found. // ABI and returns nil if none found.
func (abi *ABI) EventByID(topic common.Hash) (*Event, error) { func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
for _, event := range abi.Events { for _, event := range abi.Events {
if bytes.Equal(event.Id().Bytes(), topic.Bytes()) { if bytes.Equal(event.ID().Bytes(), topic.Bytes()) {
return &event, nil return &event, nil
} }
} }

View file

@ -20,7 +20,6 @@ import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"log"
"math/big" "math/big"
"reflect" "reflect"
"strings" "strings"
@ -58,14 +57,14 @@ const jsondata2 = `
]` ]`
func TestReader(t *testing.T) { func TestReader(t *testing.T) {
Uint256, _ := NewType("uint256", nil) Uint256, _ := NewType("uint256", "", nil)
exp := ABI{ exp := ABI{
Methods: map[string]Method{ Methods: map[string]Method{
"balance": { "balance": {
"balance", true, nil, nil, "balance", "balance", true, nil, nil,
}, },
"send": { "send": {
"send", false, []Argument{ "send", "send", false, []Argument{
{"amount", Uint256, false}, {"amount", Uint256, false},
}, nil, }, nil,
}, },
@ -102,8 +101,7 @@ func TestReader(t *testing.T) {
func TestTestNumbers(t *testing.T) { func TestTestNumbers(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2)) abi, err := JSON(strings.NewReader(jsondata2))
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
t.FailNow()
} }
if _, err := abi.Pack("balance"); err != nil { if _, err := abi.Pack("balance"); err != nil {
@ -140,8 +138,7 @@ func TestTestNumbers(t *testing.T) {
func TestTestString(t *testing.T) { func TestTestString(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2)) abi, err := JSON(strings.NewReader(jsondata2))
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
t.FailNow()
} }
if _, err := abi.Pack("string", "hello world"); err != nil { if _, err := abi.Pack("string", "hello world"); err != nil {
@ -152,8 +149,7 @@ func TestTestString(t *testing.T) {
func TestTestBool(t *testing.T) { func TestTestBool(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2)) abi, err := JSON(strings.NewReader(jsondata2))
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
t.FailNow()
} }
if _, err := abi.Pack("bool", true); err != nil { if _, err := abi.Pack("bool", true); err != nil {
@ -164,42 +160,39 @@ func TestTestBool(t *testing.T) {
func TestTestSlice(t *testing.T) { func TestTestSlice(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2)) abi, err := JSON(strings.NewReader(jsondata2))
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
t.FailNow()
} }
slice := make([]uint64, 2) slice := make([]uint64, 2)
if _, err := abi.Pack("uint64[2]", slice); err != nil { if _, err := abi.Pack("uint64[2]", slice); err != nil {
t.Error(err) t.Error(err)
} }
if _, err := abi.Pack("uint64[]", slice); err != nil { if _, err := abi.Pack("uint64[]", slice); err != nil {
t.Error(err) t.Error(err)
} }
} }
func TestMethodSignature(t *testing.T) { func TestMethodSignature(t *testing.T) {
String, _ := NewType("string", nil) String, _ := NewType("string", "", nil)
m := Method{"foo", false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil} m := Method{"foo", "foo", false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil}
exp := "foo(string,string)" exp := "foo(string,string)"
if m.Sig() != exp { if m.Sig() != exp {
t.Error("signature mismatch", exp, "!=", m.Sig()) t.Error("signature mismatch", exp, "!=", m.Sig())
} }
idexp := crypto.Keccak256([]byte(exp))[:4] idexp := crypto.Keccak256([]byte(exp))[:4]
if !bytes.Equal(m.Id(), idexp) { if !bytes.Equal(m.ID(), idexp) {
t.Errorf("expected ids to match %x != %x", m.Id(), idexp) t.Errorf("expected ids to match %x != %x", m.ID(), idexp)
} }
uintt, _ := NewType("uint256", nil) uintt, _ := NewType("uint256", "", nil)
m = Method{"foo", false, []Argument{{"bar", uintt, false}}, nil} m = Method{"foo", "foo", false, []Argument{{"bar", uintt, false}}, nil}
exp = "foo(uint256)" exp = "foo(uint256)"
if m.Sig() != exp { if m.Sig() != exp {
t.Error("signature mismatch", exp, "!=", m.Sig()) t.Error("signature mismatch", exp, "!=", m.Sig())
} }
// Method with tuple arguments // Method with tuple arguments
s, _ := NewType("tuple", []ArgumentMarshaling{ s, _ := NewType("tuple", "", []ArgumentMarshaling{
{Name: "a", Type: "int256"}, {Name: "a", Type: "int256"},
{Name: "b", Type: "int256[]"}, {Name: "b", Type: "int256[]"},
{Name: "c", Type: "tuple[]", Components: []ArgumentMarshaling{ {Name: "c", Type: "tuple[]", Components: []ArgumentMarshaling{
@ -211,18 +204,40 @@ func TestMethodSignature(t *testing.T) {
{Name: "y", Type: "int256"}, {Name: "y", Type: "int256"},
}}, }},
}) })
m = Method{"foo", false, []Argument{{"s", s, false}, {"bar", String, false}}, nil} m = Method{"foo", "foo", false, []Argument{{"s", s, false}, {"bar", String, false}}, nil}
exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)" exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)"
if m.Sig() != exp { if m.Sig() != exp {
t.Error("signature mismatch", exp, "!=", m.Sig()) t.Error("signature mismatch", exp, "!=", m.Sig())
} }
} }
func TestOverloadedMethodSignature(t *testing.T) {
json := `[{"constant":true,"inputs":[{"name":"i","type":"uint256"},{"name":"j","type":"uint256"}],"name":"foo","outputs":[],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[{"name":"i","type":"uint256"}],"name":"foo","outputs":[],"payable":false,"stateMutability":"pure","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"i","type":"uint256"}],"name":"bar","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"i","type":"uint256"},{"indexed":false,"name":"j","type":"uint256"}],"name":"bar","type":"event"}]`
abi, err := JSON(strings.NewReader(json))
if err != nil {
t.Fatal(err)
}
check := func(name string, expect string, method bool) {
if method {
if abi.Methods[name].Sig() != expect {
t.Fatalf("The signature of overloaded method mismatch, want %s, have %s", expect, abi.Methods[name].Sig())
}
} else {
if abi.Events[name].Sig() != expect {
t.Fatalf("The signature of overloaded event mismatch, want %s, have %s", expect, abi.Events[name].Sig())
}
}
}
check("foo", "foo(uint256,uint256)", true)
check("foo0", "foo(uint256)", true)
check("bar", "bar(uint256)", false)
check("bar0", "bar(uint256,uint256)", false)
}
func TestMultiPack(t *testing.T) { func TestMultiPack(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2)) abi, err := JSON(strings.NewReader(jsondata2))
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
t.FailNow()
} }
sig := crypto.Keccak256([]byte("bar(uint32,uint16)"))[:4] sig := crypto.Keccak256([]byte("bar(uint32,uint16)"))[:4]
@ -232,10 +247,8 @@ func TestMultiPack(t *testing.T) {
packed, err := abi.Pack("bar", uint32(10), uint16(11)) packed, err := abi.Pack("bar", uint32(10), uint16(11))
if err != nil { if err != nil {
t.Error(err) t.Fatal(err)
t.FailNow()
} }
if !bytes.Equal(packed, sig) { if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed) t.Errorf("expected %x got %x", sig, packed)
} }
@ -246,11 +259,11 @@ func ExampleJSON() {
abi, err := JSON(strings.NewReader(definition)) abi, err := JSON(strings.NewReader(definition))
if err != nil { if err != nil {
log.Fatalln(err) panic(err)
} }
out, err := abi.Pack("isBar", common.HexToAddress("01")) out, err := abi.Pack("isBar", common.HexToAddress("01"))
if err != nil { if err != nil {
log.Fatalln(err) panic(err)
} }
fmt.Printf("%x\n", out) fmt.Printf("%x\n", out)
@ -589,9 +602,9 @@ func TestBareEvents(t *testing.T) {
{ "type" : "event", "name" : "tuple", "inputs" : [{ "indexed":false, "name":"t", "type":"tuple", "components":[{"name":"a", "type":"uint256"}] }, { "indexed":true, "name":"arg1", "type":"address" }] } { "type" : "event", "name" : "tuple", "inputs" : [{ "indexed":false, "name":"t", "type":"tuple", "components":[{"name":"a", "type":"uint256"}] }, { "indexed":true, "name":"arg1", "type":"address" }] }
]` ]`
arg0, _ := NewType("uint256", nil) arg0, _ := NewType("uint256", "", nil)
arg1, _ := NewType("address", nil) arg1, _ := NewType("address", "", nil)
tuple, _ := NewType("tuple", []ArgumentMarshaling{{Name: "a", Type: "uint256"}}) tuple, _ := NewType("tuple", "", []ArgumentMarshaling{{Name: "a", Type: "uint256"}})
expectedEvents := map[string]struct { expectedEvents := map[string]struct {
Anonymous bool Anonymous bool
@ -908,13 +921,13 @@ func TestABI_MethodById(t *testing.T) {
} }
for name, m := range abi.Methods { for name, m := range abi.Methods {
a := fmt.Sprintf("%v", m) a := fmt.Sprintf("%v", m)
m2, err := abi.MethodById(m.Id()) m2, err := abi.MethodById(m.ID())
if err != nil { if err != nil {
t.Fatalf("Failed to look up ABI method: %v", err) t.Fatalf("Failed to look up ABI method: %v", err)
} }
b := fmt.Sprintf("%v", m2) b := fmt.Sprintf("%v", m2)
if a != b { if a != b {
t.Errorf("Method %v (id %v) not 'findable' by id in ABI", name, common.ToHex(m.Id())) t.Errorf("Method %v (id %v) not 'findable' by id in ABI", name, common.ToHex(m.ID()))
} }
} }
// Also test empty // Also test empty
@ -982,8 +995,8 @@ func TestABI_EventById(t *testing.T) {
t.Errorf("We should find a event for topic %s, test #%d", topicID.Hex(), testnum) t.Errorf("We should find a event for topic %s, test #%d", topicID.Hex(), testnum)
} }
if event.Id() != topicID { if event.ID() != topicID {
t.Errorf("Event id %s does not match topic %s, test #%d", event.Id().Hex(), topicID.Hex(), testnum) t.Errorf("Event id %s does not match topic %s, test #%d", event.ID().Hex(), topicID.Hex(), testnum)
} }
unknowntopicID := crypto.Keccak256Hash([]byte("unknownEvent")) unknowntopicID := crypto.Keccak256Hash([]byte("unknownEvent"))

View file

@ -34,10 +34,11 @@ type Argument struct {
type Arguments []Argument type Arguments []Argument
type ArgumentMarshaling struct { type ArgumentMarshaling struct {
Name string Name string
Type string Type string
Components []ArgumentMarshaling InternalType string
Indexed bool Components []ArgumentMarshaling
Indexed bool
} }
// UnmarshalJSON implements json.Unmarshaler interface // UnmarshalJSON implements json.Unmarshaler interface
@ -48,7 +49,7 @@ func (argument *Argument) UnmarshalJSON(data []byte) error {
return fmt.Errorf("argument json err: %v", err) return fmt.Errorf("argument json err: %v", err)
} }
argument.Type, err = NewType(arg.Type, arg.Components) argument.Type, err = NewType(arg.Type, arg.InternalType, arg.Components)
if err != nil { if err != nil {
return err return err
} }
@ -88,6 +89,13 @@ func (arguments Arguments) isTuple() bool {
// Unpack performs the operation hexdata -> Go format // Unpack performs the operation hexdata -> Go format
func (arguments Arguments) Unpack(v interface{}, data []byte) error { func (arguments Arguments) Unpack(v interface{}, data []byte) error {
if len(data) == 0 {
if len(arguments) != 0 {
return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
} else {
return nil // Nothing to unmarshal, return
}
}
// make sure the passed value is arguments pointer // make sure the passed value is arguments pointer
if reflect.Ptr != reflect.ValueOf(v).Kind() { if reflect.Ptr != reflect.ValueOf(v).Kind() {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v) return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
@ -104,11 +112,17 @@ func (arguments Arguments) Unpack(v interface{}, data []byte) error {
// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value // UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value
func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error { func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
if len(data) == 0 {
if len(arguments) != 0 {
return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
} else {
return nil // Nothing to unmarshal, return
}
}
marshalledValues, err := arguments.UnpackValues(data) marshalledValues, err := arguments.UnpackValues(data)
if err != nil { if err != nil {
return err return err
} }
return arguments.unpackIntoMap(v, marshalledValues) return arguments.unpackIntoMap(v, marshalledValues)
} }

View file

@ -88,6 +88,12 @@ func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBac
return NewSimulatedBackendWithDatabase(rawdb.NewMemoryDatabase(), alloc, gasLimit) return NewSimulatedBackendWithDatabase(rawdb.NewMemoryDatabase(), alloc, gasLimit)
} }
// Close terminates the underlying blockchain's update loop.
func (b *SimulatedBackend) Close() error {
b.blockchain.Stop()
return nil
}
// Commit imports all the pending transactions as a single block and starts a // Commit imports all the pending transactions as a single block and starts a
// fresh new state. // fresh new state.
func (b *SimulatedBackend) Commit() { func (b *SimulatedBackend) Commit() {

View file

@ -38,6 +38,7 @@ func TestSimulatedBackend(t *testing.T) {
genAlloc[auth.From] = core.GenesisAccount{Balance: big.NewInt(9223372036854775807)} genAlloc[auth.From] = core.GenesisAccount{Balance: big.NewInt(9223372036854775807)}
sim := backends.NewSimulatedBackend(genAlloc, gasLimit) sim := backends.NewSimulatedBackend(genAlloc, gasLimit)
defer sim.Close()
// should return an error if the tx is not found // should return an error if the tx is not found
txHash := common.HexToHash("2") txHash := common.HexToHash("2")

View file

@ -218,7 +218,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
} }
} }
// If the contract surely has code (or code is not needed), estimate the transaction // If the contract surely has code (or code is not needed), estimate the transaction
msg := ethereum.CallMsg{From: opts.From, To: contract, Value: value, Data: input} msg := ethereum.CallMsg{From: opts.From, To: contract, GasPrice: gasPrice, Value: value, Data: input}
gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg) gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to estimate gas needed: %v", err) return nil, fmt.Errorf("failed to estimate gas needed: %v", err)
@ -252,7 +252,7 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
opts = new(FilterOpts) opts = new(FilterOpts)
} }
// Append the event selector to the query parameters and construct the topic set // Append the event selector to the query parameters and construct the topic set
query = append([][]interface{}{{c.abi.Events[name].Id()}}, query...) query = append([][]interface{}{{c.abi.Events[name].ID()}}, query...)
topics, err := makeTopics(query...) topics, err := makeTopics(query...)
if err != nil { if err != nil {
@ -301,7 +301,7 @@ func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]inter
opts = new(WatchOpts) opts = new(WatchOpts)
} }
// Append the event selector to the query parameters and construct the topic set // Append the event selector to the query parameters and construct the topic set
query = append([][]interface{}{{c.abi.Events[name].Id()}}, query...) query = append([][]interface{}{{c.abi.Events[name].ID()}}, query...)
topics, err := makeTopics(query...) topics, err := makeTopics(query...)
if err != nil { if err != nil {

View file

@ -31,6 +31,7 @@ import (
"unicode" "unicode"
"github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/log"
) )
// Lang is a target programming language selector to generate bindings for. // Lang is a target programming language selector to generate bindings for.
@ -46,10 +47,13 @@ const (
// to be used as is in client code, but rather as an intermediate struct which // to be used as is in client code, but rather as an intermediate struct which
// enforces compile time type safety and naming convention opposed to having to // enforces compile time type safety and naming convention opposed to having to
// manually maintain hard coded strings that break on runtime. // manually maintain hard coded strings that break on runtime.
func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang) (string, error) { func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string) (string, error) {
// Process each individual contract requested binding // Process each individual contract requested binding
contracts := make(map[string]*tmplContract) contracts := make(map[string]*tmplContract)
// Map used to flag each encountered library as such
isLib := make(map[string]struct{})
for i := 0; i < len(types); i++ { for i := 0; i < len(types); i++ {
// Parse the actual ABI to generate the binding for // Parse the actual ABI to generate the binding for
evmABI, err := abi.JSON(strings.NewReader(abis[i])) evmABI, err := abi.JSON(strings.NewReader(abis[i]))
@ -82,7 +86,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
if input.Name == "" { if input.Name == "" {
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
} }
if _, exist := structs[input.Type.String()]; input.Type.T == abi.TupleTy && !exist { if hasStruct(input.Type) {
bindStructType[lang](input.Type, structs) bindStructType[lang](input.Type, structs)
} }
} }
@ -92,7 +96,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
if output.Name != "" { if output.Name != "" {
normalized.Outputs[j].Name = capitalise(output.Name) normalized.Outputs[j].Name = capitalise(output.Name)
} }
if _, exist := structs[output.Type.String()]; output.Type.T == abi.TupleTy && !exist { if hasStruct(output.Type) {
bindStructType[lang](output.Type, structs) bindStructType[lang](output.Type, structs)
} }
} }
@ -115,14 +119,11 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
normalized.Inputs = make([]abi.Argument, len(original.Inputs)) normalized.Inputs = make([]abi.Argument, len(original.Inputs))
copy(normalized.Inputs, original.Inputs) copy(normalized.Inputs, original.Inputs)
for j, input := range normalized.Inputs { for j, input := range normalized.Inputs {
// Indexed fields are input, non-indexed ones are outputs if input.Name == "" {
if input.Indexed { normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
if input.Name == "" { }
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j) if hasStruct(input.Type) {
} bindStructType[lang](input.Type, structs)
if _, exist := structs[input.Type.String()]; input.Type.T == abi.TupleTy && !exist {
bindStructType[lang](input.Type, structs)
}
} }
} }
// Append the event to the accumulator list // Append the event to the accumulator list
@ -137,21 +138,44 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
contracts[types[i]] = &tmplContract{ contracts[types[i]] = &tmplContract{
Type: capitalise(types[i]), Type: capitalise(types[i]),
InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1), InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1),
InputBin: strings.TrimSpace(bytecodes[i]), InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
Constructor: evmABI.Constructor, Constructor: evmABI.Constructor,
Calls: calls, Calls: calls,
Transacts: transacts, Transacts: transacts,
Events: events, Events: events,
Libraries: make(map[string]string),
Structs: structs, Structs: structs,
} }
// Function 4-byte signatures are stored in the same sequence
// as types, if available.
if len(fsigs) > i { if len(fsigs) > i {
contracts[types[i]].FuncSigs = fsigs[i] contracts[types[i]].FuncSigs = fsigs[i]
} }
// Parse library references.
for pattern, name := range libs {
matched, err := regexp.Match("__\\$"+pattern+"\\$__", []byte(contracts[types[i]].InputBin))
if err != nil {
log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err)
}
if matched {
contracts[types[i]].Libraries[pattern] = name
// keep track that this type is a library
if _, ok := isLib[name]; !ok {
isLib[name] = struct{}{}
}
}
}
}
// Check if that type has already been identified as a library
for i := 0; i < len(types); i++ {
_, ok := isLib[types[i]]
contracts[types[i]].Library = ok
} }
// Generate the contract template data content and render it // Generate the contract template data content and render it
data := &tmplData{ data := &tmplData{
Package: pkg, Package: pkg,
Contracts: contracts, Contracts: contracts,
Libraries: libs,
} }
buffer := new(bytes.Buffer) buffer := new(bytes.Buffer)
@ -217,7 +241,7 @@ func bindBasicTypeGo(kind abi.Type) string {
func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
switch kind.T { switch kind.T {
case abi.TupleTy: case abi.TupleTy:
return structs[kind.String()].Name return structs[kind.TupleRawName+kind.String()].Name
case abi.ArrayTy: case abi.ArrayTy:
return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs) return fmt.Sprintf("[%d]", kind.Size) + bindTypeGo(*kind.Elem, structs)
case abi.SliceTy: case abi.SliceTy:
@ -294,7 +318,7 @@ func pluralizeJavaType(typ string) string {
func bindTypeJava(kind abi.Type, structs map[string]*tmplStruct) string { func bindTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
switch kind.T { switch kind.T {
case abi.TupleTy: case abi.TupleTy:
return structs[kind.String()].Name return structs[kind.TupleRawName+kind.String()].Name
case abi.ArrayTy, abi.SliceTy: case abi.ArrayTy, abi.SliceTy:
return pluralizeJavaType(bindTypeJava(*kind.Elem, structs)) return pluralizeJavaType(bindTypeJava(*kind.Elem, structs))
default: default:
@ -313,6 +337,13 @@ var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct)
// funcionality as for simple types, but dynamic types get converted to hashes. // funcionality as for simple types, but dynamic types get converted to hashes.
func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
bound := bindTypeGo(kind, structs) bound := bindTypeGo(kind, structs)
// todo(rjl493456442) according solidity documentation, indexed event
// parameters that are not value types i.e. arrays and structs are not
// stored directly but instead a keccak256-hash of an encoding is stored.
//
// We only convert stringS and bytes to hash, still need to deal with
// array(both fixed-size and dynamic-size) and struct.
if bound == "string" || bound == "[]byte" { if bound == "string" || bound == "[]byte" {
bound = "common.Hash" bound = "common.Hash"
} }
@ -323,6 +354,13 @@ func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
// funcionality as for simple types, but dynamic types get converted to hashes. // funcionality as for simple types, but dynamic types get converted to hashes.
func bindTopicTypeJava(kind abi.Type, structs map[string]*tmplStruct) string { func bindTopicTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
bound := bindTypeJava(kind, structs) bound := bindTypeJava(kind, structs)
// todo(rjl493456442) according solidity documentation, indexed event
// parameters that are not value types i.e. arrays and structs are not
// stored directly but instead a keccak256-hash of an encoding is stored.
//
// We only convert stringS and bytes to hash, still need to deal with
// array(both fixed-size and dynamic-size) and struct.
if bound == "String" || bound == "byte[]" { if bound == "String" || bound == "byte[]" {
bound = "Hash" bound = "Hash"
} }
@ -342,7 +380,14 @@ var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct
func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string { func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
switch kind.T { switch kind.T {
case abi.TupleTy: case abi.TupleTy:
if s, exist := structs[kind.String()]; exist { // We compose raw struct name and canonical parameter expression
// together here. The reason is before solidity v0.5.11, kind.TupleRawName
// is empty, so we use canonical parameter expression to distinguish
// different struct definition. From the consideration of backward
// compatibility, we concat these two together so that if kind.TupleRawName
// is not empty, it can have unique id.
id := kind.TupleRawName + kind.String()
if s, exist := structs[id]; exist {
return s.Name return s.Name
} }
var fields []*tmplField var fields []*tmplField
@ -350,8 +395,11 @@ func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
field := bindStructTypeGo(*elem, structs) field := bindStructTypeGo(*elem, structs)
fields = append(fields, &tmplField{Type: field, Name: capitalise(kind.TupleRawNames[i]), SolKind: *elem}) fields = append(fields, &tmplField{Type: field, Name: capitalise(kind.TupleRawNames[i]), SolKind: *elem})
} }
name := fmt.Sprintf("Struct%d", len(structs)) name := kind.TupleRawName
structs[kind.String()] = &tmplStruct{ if name == "" {
name = fmt.Sprintf("Struct%d", len(structs))
}
structs[id] = &tmplStruct{
Name: name, Name: name,
Fields: fields, Fields: fields,
} }
@ -371,7 +419,14 @@ func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
func bindStructTypeJava(kind abi.Type, structs map[string]*tmplStruct) string { func bindStructTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
switch kind.T { switch kind.T {
case abi.TupleTy: case abi.TupleTy:
if s, exist := structs[kind.String()]; exist { // We compose raw struct name and canonical parameter expression
// together here. The reason is before solidity v0.5.11, kind.TupleRawName
// is empty, so we use canonical parameter expression to distinguish
// different struct definition. From the consideration of backward
// compatibility, we concat these two together so that if kind.TupleRawName
// is not empty, it can have unique id.
id := kind.TupleRawName + kind.String()
if s, exist := structs[id]; exist {
return s.Name return s.Name
} }
var fields []*tmplField var fields []*tmplField
@ -379,8 +434,11 @@ func bindStructTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
field := bindStructTypeJava(*elem, structs) field := bindStructTypeJava(*elem, structs)
fields = append(fields, &tmplField{Type: field, Name: decapitalise(kind.TupleRawNames[i]), SolKind: *elem}) fields = append(fields, &tmplField{Type: field, Name: decapitalise(kind.TupleRawNames[i]), SolKind: *elem})
} }
name := fmt.Sprintf("Class%d", len(structs)) name := kind.TupleRawName
structs[kind.String()] = &tmplStruct{ if name == "" {
name = fmt.Sprintf("Class%d", len(structs))
}
structs[id] = &tmplStruct{
Name: name, Name: name,
Fields: fields, Fields: fields,
} }
@ -470,6 +528,21 @@ func structured(args abi.Arguments) bool {
return true return true
} }
// hasStruct returns an indicator whether the given type is struct, struct slice
// or struct array.
func hasStruct(t abi.Type) bool {
switch t.T {
case abi.SliceTy:
return hasStruct(*t.Elem)
case abi.ArrayTy:
return hasStruct(*t.Elem)
case abi.TupleTy:
return true
default:
return false
}
}
// resolveArgName converts a raw argument representation into a user friendly format. // resolveArgName converts a raw argument representation into a user friendly format.
func resolveArgName(arg abi.Argument, structs map[string]*tmplStruct) string { func resolveArgName(arg abi.Argument, structs map[string]*tmplStruct) string {
var ( var (
@ -485,7 +558,7 @@ loop:
case abi.ArrayTy: case abi.ArrayTy:
prefix += fmt.Sprintf("[%d]", typ.Size) prefix += fmt.Sprintf("[%d]", typ.Size)
default: default:
embedded = typ.String() embedded = typ.TupleRawName + typ.String()
break loop break loop
} }
typ = typ.Elem typ = typ.Elem
@ -514,7 +587,7 @@ func formatMethod(method abi.Method, structs map[string]*tmplStruct) string {
if method.Const { if method.Const {
constant = "constant " constant = "constant "
} }
return fmt.Sprintf("function %v(%v) %sreturns(%v)", method.Name, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", ")) return fmt.Sprintf("function %v(%v) %sreturns(%v)", method.RawName, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", "))
} }
// formatEvent transforms raw event representation into a user friendly one. // formatEvent transforms raw event representation into a user friendly one.
@ -527,5 +600,5 @@ func formatEvent(event abi.Event, structs map[string]*tmplStruct) string {
inputs[i] = fmt.Sprintf("%v %v", resolveArgName(input, structs), input.Name) inputs[i] = fmt.Sprintf("%v %v", resolveArgName(input, structs), input.Name)
} }
} }
return fmt.Sprintf("event %v(%v)", event.Name, strings.Join(inputs, ", ")) return fmt.Sprintf("event %v(%v)", event.RawName, strings.Join(inputs, ", "))
} }

File diff suppressed because one or more lines are too long

View file

@ -22,6 +22,7 @@ import "github.com/ethereum/go-ethereum/accounts/abi"
type tmplData struct { type tmplData struct {
Package string // Name of the package to place the generated file in Package string // Name of the package to place the generated file in
Contracts map[string]*tmplContract // List of contracts to generate into this file Contracts map[string]*tmplContract // List of contracts to generate into this file
Libraries map[string]string // Map the bytecode's link pattern to the library name
} }
// tmplContract contains the data needed to generate an individual contract binding. // tmplContract contains the data needed to generate an individual contract binding.
@ -34,7 +35,9 @@ type tmplContract struct {
Calls map[string]*tmplMethod // Contract calls that only read state data Calls map[string]*tmplMethod // Contract calls that only read state data
Transacts map[string]*tmplMethod // Contract calls that write state data Transacts map[string]*tmplMethod // Contract calls that write state data
Events map[string]*tmplEvent // Contract events accessors Events map[string]*tmplEvent // Contract events accessors
Libraries map[string]string // Same as tmplData, but filtered to only keep what the contract needs
Structs map[string]*tmplStruct // Contract struct type definitions Structs map[string]*tmplStruct // Contract struct type definitions
Library bool
} }
// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed // tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
@ -62,7 +65,7 @@ type tmplField struct {
// tmplStruct is a wrapper around an abi.tuple contains a auto-generated // tmplStruct is a wrapper around an abi.tuple contains a auto-generated
// struct name. // struct name.
type tmplStruct struct { type tmplStruct struct {
Name string // Auto-generated struct name(We can't obtain the raw struct name through abi) Name string // Auto-generated struct name(before solidity v0.5.11) or raw name.
Fields []*tmplField // Struct fields definition depends on the binding language. Fields []*tmplField // Struct fields definition depends on the binding language.
} }
@ -113,15 +116,14 @@ var (
{{if $contract.FuncSigs}} {{if $contract.FuncSigs}}
// {{.Type}}FuncSigs maps the 4-byte function signature to its string representation. // {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
var {{.Type}}FuncSigs = map[string]string{ var {{.Type}}FuncSigs = map[string]string{
{{range $strsig, $binsig := .FuncSigs}} {{range $strsig, $binsig := .FuncSigs}}"{{$binsig}}": "{{$strsig}}",
"{{$binsig}}": "{{$strsig}}",
{{end}} {{end}}
} }
{{end}} {{end}}
{{if .InputBin}} {{if .InputBin}}
// {{.Type}}Bin is the compiled bytecode used for deploying new contracts. // {{.Type}}Bin is the compiled bytecode used for deploying new contracts.
const {{.Type}}Bin = ` + "`" + `{{.InputBin}}` + "`" + ` var {{.Type}}Bin = "0x{{.InputBin}}"
// Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it. // Deploy{{.Type}} deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type $structs}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) { func Deploy{{.Type}}(auth *bind.TransactOpts, backend bind.ContractBackend {{range .Constructor.Inputs}}, {{.Name}} {{bindtype .Type $structs}}{{end}}) (common.Address, *types.Transaction, *{{.Type}}, error) {
@ -129,6 +131,10 @@ var (
if err != nil { if err != nil {
return common.Address{}, nil, nil, err return common.Address{}, nil, nil, err
} }
{{range $pattern, $name := .Libraries}}
{{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend)
{{$contract.Type}}Bin = strings.Replace({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:], -1)
{{end}}
address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}}) address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
if err != nil { if err != nil {
return common.Address{}, nil, nil, err return common.Address{}, nil, nil, err
@ -288,7 +294,7 @@ var (
{{end}} {{end}}
{{range .Calls}} {{range .Calls}}
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}. // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
// //
// Solidity: {{formatmethod .Original $structs}} // Solidity: {{formatmethod .Original $structs}}
func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}(opts *bind.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}}{{end}} error) { func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}(opts *bind.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}}{{end}} error) {
@ -307,14 +313,14 @@ var (
return {{if .Structured}}*ret,{{else}}{{range $i, $_ := .Normalized.Outputs}}*ret{{$i}},{{end}}{{end}} err return {{if .Structured}}*ret,{{else}}{{range $i, $_ := .Normalized.Outputs}}*ret{{$i}},{{end}}{{end}} err
} }
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}. // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
// //
// Solidity: {{formatmethod .Original $structs}} // Solidity: {{formatmethod .Original $structs}}
func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) { func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}}) return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.CallOpts {{range .Normalized.Inputs}}, {{.Name}}{{end}})
} }
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}. // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
// //
// Solidity: {{formatmethod .Original $structs}} // Solidity: {{formatmethod .Original $structs}}
func (_{{$contract.Type}} *{{$contract.Type}}CallerSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) { func (_{{$contract.Type}} *{{$contract.Type}}CallerSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} }, {{else}} {{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}} {{end}} error) {
@ -323,21 +329,21 @@ var (
{{end}} {{end}}
{{range .Transacts}} {{range .Transacts}}
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}. // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
// //
// Solidity: {{formatmethod .Original $structs}} // Solidity: {{formatmethod .Original $structs}}
func (_{{$contract.Type}} *{{$contract.Type}}Transactor) {{.Normalized.Name}}(opts *bind.TransactOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) { func (_{{$contract.Type}} *{{$contract.Type}}Transactor) {{.Normalized.Name}}(opts *bind.TransactOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
return _{{$contract.Type}}.contract.Transact(opts, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}}) return _{{$contract.Type}}.contract.Transact(opts, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}})
} }
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}. // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
// //
// Solidity: {{formatmethod .Original $structs}} // Solidity: {{formatmethod .Original $structs}}
func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) { func (_{{$contract.Type}} *{{$contract.Type}}Session) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}}) return _{{$contract.Type}}.Contract.{{.Normalized.Name}}(&_{{$contract.Type}}.TransactOpts {{range $i, $_ := .Normalized.Inputs}}, {{.Name}}{{end}})
} }
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}. // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
// //
// Solidity: {{formatmethod .Original $structs}} // Solidity: {{formatmethod .Original $structs}}
func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) { func (_{{$contract.Type}} *{{$contract.Type}}TransactorSession) {{.Normalized.Name}}({{range $i, $_ := .Normalized.Inputs}}{{if ne $i 0}},{{end}} {{.Name}} {{bindtype .Type $structs}} {{end}}) (*types.Transaction, error) {
@ -416,7 +422,7 @@ var (
Raw types.Log // Blockchain specific contextual infos Raw types.Log // Blockchain specific contextual infos
} }
// Filter{{.Normalized.Name}} is a free log retrieval operation binding the contract event 0x{{printf "%x" .Original.Id}}. // Filter{{.Normalized.Name}} is a free log retrieval operation binding the contract event 0x{{printf "%x" .Original.ID}}.
// //
// Solidity: {{formatevent .Original $structs}} // Solidity: {{formatevent .Original $structs}}
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Filter{{.Normalized.Name}}(opts *bind.FilterOpts{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (*{{$contract.Type}}{{.Normalized.Name}}Iterator, error) { func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Filter{{.Normalized.Name}}(opts *bind.FilterOpts{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (*{{$contract.Type}}{{.Normalized.Name}}Iterator, error) {
@ -433,7 +439,7 @@ var (
return &{{$contract.Type}}{{.Normalized.Name}}Iterator{contract: _{{$contract.Type}}.contract, event: "{{.Original.Name}}", logs: logs, sub: sub}, nil return &{{$contract.Type}}{{.Normalized.Name}}Iterator{contract: _{{$contract.Type}}.contract, event: "{{.Original.Name}}", logs: logs, sub: sub}, nil
} }
// Watch{{.Normalized.Name}} is a free log subscription operation binding the contract event 0x{{printf "%x" .Original.Id}}. // Watch{{.Normalized.Name}} is a free log subscription operation binding the contract event 0x{{printf "%x" .Original.ID}}.
// //
// Solidity: {{formatevent .Original $structs}} // Solidity: {{formatevent .Original $structs}}
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Watch{{.Normalized.Name}}(opts *bind.WatchOpts, sink chan<- *{{$contract.Type}}{{.Normalized.Name}}{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (event.Subscription, error) { func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Watch{{.Normalized.Name}}(opts *bind.WatchOpts, sink chan<- *{{$contract.Type}}{{.Normalized.Name}}{{range .Normalized.Inputs}}{{if .Indexed}}, {{.Name}} []{{bindtype .Type $structs}}{{end}}{{end}}) (event.Subscription, error) {
@ -475,9 +481,9 @@ var (
}), nil }), nil
} }
// Parse{{.Normalized.Name}} is a log parse operation binding the contract event 0x{{printf "%x" .Original.Id}}. // Parse{{.Normalized.Name}} is a log parse operation binding the contract event 0x{{printf "%x" .Original.ID}}.
// //
// Solidity: {{.Original.String}} // Solidity: {{formatevent .Original $structs}}
func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Parse{{.Normalized.Name}}(log types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) { func (_{{$contract.Type}} *{{$contract.Type}}Filterer) Parse{{.Normalized.Name}}(log types.Log) (*{{$contract.Type}}{{.Normalized.Name}}, error) {
event := new({{$contract.Type}}{{.Normalized.Name}}) event := new({{$contract.Type}}{{.Normalized.Name}})
if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil { if err := _{{$contract.Type}}.contract.UnpackLog(event, "{{.Original.Name}}", log); err != nil {
@ -503,7 +509,7 @@ import java.util.*;
{{range $contract := .Contracts}} {{range $contract := .Contracts}}
{{$structs := $contract.Structs}} {{$structs := $contract.Structs}}
public class {{.Type}} { {{if not .Library}}public {{end}}class {{.Type}} {
// ABI is the input ABI used to generate the binding from. // ABI is the input ABI used to generate the binding from.
public final static String ABI = "{{.InputABI}}"; public final static String ABI = "{{.InputABI}}";
{{if $contract.FuncSigs}} {{if $contract.FuncSigs}}
@ -511,8 +517,7 @@ public class {{.Type}} {
public final static Map<String, String> {{.Type}}FuncSigs; public final static Map<String, String> {{.Type}}FuncSigs;
static { static {
Hashtable<String, String> temp = new Hashtable<String, String>(); Hashtable<String, String> temp = new Hashtable<String, String>();
{{range $strsig, $binsig := .FuncSigs}} {{range $strsig, $binsig := .FuncSigs}}temp.put("{{$binsig}}", "{{$strsig}}");
temp.put("{{$binsig}}", "{{$strsig}}");
{{end}} {{end}}
{{.Type}}FuncSigs = Collections.unmodifiableMap(temp); {{.Type}}FuncSigs = Collections.unmodifiableMap(temp);
} }
@ -524,9 +529,18 @@ public class {{.Type}} {
// deploy deploys a new Ethereum contract, binding an instance of {{.Type}} to it. // deploy deploys a new Ethereum contract, binding an instance of {{.Type}} to it.
public static {{.Type}} deploy(TransactOpts auth, EthereumClient client{{range .Constructor.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception { public static {{.Type}} deploy(TransactOpts auth, EthereumClient client{{range .Constructor.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
Interfaces args = Geth.newInterfaces({{(len .Constructor.Inputs)}}); Interfaces args = Geth.newInterfaces({{(len .Constructor.Inputs)}});
String bytecode = BYTECODE;
{{if .Libraries}}
// "link" contract to dependent libraries by deploying them first.
{{range $pattern, $name := .Libraries}}
{{capitalise $name}} {{decapitalise $name}}Inst = {{capitalise $name}}.deploy(auth, client);
bytecode = bytecode.replace("__${{$pattern}}$__", {{decapitalise $name}}Inst.Address.getHex().substring(2));
{{end}}
{{end}}
{{range $index, $element := .Constructor.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}}); {{range $index, $element := .Constructor.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
{{end}} {{end}}
return new {{.Type}}(Geth.deployContract(auth, ABI, Geth.decodeFromHex(BYTECODE), client, args)); return new {{.Type}}(Geth.deployContract(auth, ABI, Geth.decodeFromHex(bytecode), client, args));
} }
// Internal constructor used by contract deployment. // Internal constructor used by contract deployment.
@ -560,7 +574,7 @@ public class {{.Type}} {
} }
{{end}} {{end}}
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.Id}}. // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
// //
// Solidity: {{.Original.String}} // Solidity: {{.Original.String}}
public {{if gt (len .Normalized.Outputs) 1}}{{capitalise .Normalized.Name}}Results{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception { public {{if gt (len .Normalized.Outputs) 1}}{{capitalise .Normalized.Name}}Results{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
@ -587,7 +601,7 @@ public class {{.Type}} {
{{end}} {{end}}
{{range .Transacts}} {{range .Transacts}}
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.Id}}. // {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
// //
// Solidity: {{.Original.String}} // Solidity: {{.Original.String}}
public Transaction {{.Normalized.Name}}(TransactOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception { public Transaction {{.Normalized.Name}}(TransactOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {

View file

@ -80,15 +80,19 @@ func makeTopics(query ...[]interface{}) ([][]common.Hash, error) {
copy(topic[:], hash[:]) copy(topic[:], hash[:])
default: default:
// todo(rjl493456442) according solidity documentation, indexed event
// parameters that are not value types i.e. arrays and structs are not
// stored directly but instead a keccak256-hash of an encoding is stored.
//
// We only convert stringS and bytes to hash, still need to deal with
// array(both fixed-size and dynamic-size) and struct.
// Attempt to generate the topic from funky types // Attempt to generate the topic from funky types
val := reflect.ValueOf(rule) val := reflect.ValueOf(rule)
switch { switch {
// static byte array // static byte array
case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8: case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8:
reflect.Copy(reflect.ValueOf(topic[:val.Len()]), val) reflect.Copy(reflect.ValueOf(topic[:val.Len()]), val)
default: default:
return nil, fmt.Errorf("unsupported indexed type: %T", rule) return nil, fmt.Errorf("unsupported indexed type: %T", rule)
} }
@ -162,6 +166,7 @@ func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) er
default: default:
// Ran out of plain primitive types, try custom types // Ran out of plain primitive types, try custom types
switch field.Type() { switch field.Type() {
case reflectHash: // Also covers all dynamic types case reflectHash: // Also covers all dynamic types
field.Set(reflect.ValueOf(topics[0])) field.Set(reflect.ValueOf(topics[0]))
@ -178,11 +183,9 @@ func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) er
default: default:
// Ran out of custom types, try the crazies // Ran out of custom types, try the crazies
switch { switch {
// static byte array // static byte array
case arg.Type.T == abi.FixedBytesTy: case arg.Type.T == abi.FixedBytesTy:
reflect.Copy(field, reflect.ValueOf(topics[0][:arg.Type.Size])) reflect.Copy(field, reflect.ValueOf(topics[0][:arg.Type.Size]))
default: default:
return fmt.Errorf("unsupported indexed type: %v", arg.Type) return fmt.Errorf("unsupported indexed type: %v", arg.Type)
} }

View file

@ -1,4 +1,4 @@
// Copyright 2018 The go-ethereum Authors // Copyright 2019 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
@ -59,7 +59,7 @@ func TestParseTopics(t *testing.T) {
type bytesStruct struct { type bytesStruct struct {
StaticBytes [5]byte StaticBytes [5]byte
} }
bytesType, _ := abi.NewType("bytes5", nil) bytesType, _ := abi.NewType("bytes5", "", nil)
type args struct { type args struct {
createObj func() interface{} createObj func() interface{}
resultObj func() interface{} resultObj func() interface{}

View file

@ -59,6 +59,7 @@ func TestWaitDeployed(t *testing.T) {
}, },
10000000, 10000000,
) )
defer backend.Close()
// Create the transaction. // Create the transaction.
tx := types.NewContractCreation(0, big.NewInt(0), test.gas, big.NewInt(1), common.FromHex(test.code)) tx := types.NewContractCreation(0, big.NewInt(0), test.gas, big.NewInt(1), common.FromHex(test.code))

View file

@ -28,7 +28,18 @@ import (
// holds type information (inputs) about the yielded output. Anonymous events // holds type information (inputs) about the yielded output. Anonymous events
// don't get the signature canonical representation as the first LOG topic. // don't get the signature canonical representation as the first LOG topic.
type Event struct { type Event struct {
Name string // Name is the event name used for internal representation. It's derived from
// the raw name and a suffix will be added in the case of a event overload.
//
// e.g.
// There are two events have same name:
// * foo(int,int)
// * foo(uint,uint)
// The event name of the first one wll be resolved as foo while the second one
// will be resolved as foo0.
Name string
// RawName is the raw event name parsed from ABI.
RawName string
Anonymous bool Anonymous bool
Inputs Arguments Inputs Arguments
} }
@ -41,17 +52,26 @@ func (e Event) String() string {
inputs[i] = fmt.Sprintf("%v indexed %v", input.Type, input.Name) inputs[i] = fmt.Sprintf("%v indexed %v", input.Type, input.Name)
} }
} }
return fmt.Sprintf("event %v(%v)", e.Name, strings.Join(inputs, ", ")) return fmt.Sprintf("event %v(%v)", e.RawName, strings.Join(inputs, ", "))
} }
// Id returns the canonical representation of the event's signature used by the // Sig returns the event string signature according to the ABI spec.
// abi definition to identify event names and types. //
func (e Event) Id() common.Hash { // Example
//
// event foo(uint32 a, int b) = "foo(uint32,int256)"
//
// Please note that "int" is substitute for its canonical representation "int256"
func (e Event) Sig() string {
types := make([]string, len(e.Inputs)) types := make([]string, len(e.Inputs))
i := 0 for i, input := range e.Inputs {
for _, input := range e.Inputs {
types[i] = input.Type.String() types[i] = input.Type.String()
i++
} }
return common.BytesToHash(crypto.Keccak256([]byte(fmt.Sprintf("%v(%v)", e.Name, strings.Join(types, ","))))) return fmt.Sprintf("%v(%v)", e.RawName, strings.Join(types, ","))
}
// ID returns the canonical representation of the event's signature used by the
// abi definition to identify event names and types.
func (e Event) ID() common.Hash {
return common.BytesToHash(crypto.Keccak256([]byte(e.Sig())))
} }

View file

@ -104,8 +104,8 @@ func TestEventId(t *testing.T) {
} }
for name, event := range abi.Events { for name, event := range abi.Events {
if event.Id() != test.expectations[name] { if event.ID() != test.expectations[name] {
t.Errorf("expected id to be %x, got %x", test.expectations[name], event.Id()) t.Errorf("expected id to be %x, got %x", test.expectations[name], event.ID())
} }
} }
} }

View file

@ -32,7 +32,18 @@ import (
// be flagged `false`. // be flagged `false`.
// Input specifies the required input parameters for this gives method. // Input specifies the required input parameters for this gives method.
type Method struct { type Method struct {
Name string // Name is the method name used for internal representation. It's derived from
// the raw name and a suffix will be added in the case of a function overload.
//
// e.g.
// There are two functions have same name:
// * foo(int,int)
// * foo(uint,uint)
// The method name of the first one will be resolved as foo while the second one
// will be resolved as foo0.
Name string
// RawName is the raw method name parsed from ABI.
RawName string
Const bool Const bool
Inputs Arguments Inputs Arguments
Outputs Arguments Outputs Arguments
@ -42,7 +53,7 @@ type Method struct {
// //
// Example // Example
// //
// function foo(uint32 a, int b) = "foo(uint32,int256)" // function foo(uint32 a, int b) = "foo(uint32,int256)"
// //
// Please note that "int" is substitute for its canonical representation "int256" // Please note that "int" is substitute for its canonical representation "int256"
func (method Method) Sig() string { func (method Method) Sig() string {
@ -50,7 +61,7 @@ func (method Method) Sig() string {
for i, input := range method.Inputs { for i, input := range method.Inputs {
types[i] = input.Type.String() types[i] = input.Type.String()
} }
return fmt.Sprintf("%v(%v)", method.Name, strings.Join(types, ",")) return fmt.Sprintf("%v(%v)", method.RawName, strings.Join(types, ","))
} }
func (method Method) String() string { func (method Method) String() string {
@ -69,9 +80,11 @@ func (method Method) String() string {
if method.Const { if method.Const {
constant = "constant " constant = "constant "
} }
return fmt.Sprintf("function %v(%v) %sreturns(%v)", method.Name, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", ")) return fmt.Sprintf("function %v(%v) %sreturns(%v)", method.RawName, strings.Join(inputs, ", "), constant, strings.Join(outputs, ", "))
} }
func (method Method) Id() []byte { // ID returns the canonical representation of the method's signature used by the
// abi definition to identify method names and types.
func (method Method) ID() []byte {
return crypto.Keccak256([]byte(method.Sig()))[:4] return crypto.Keccak256([]byte(method.Sig()))[:4]
} }

View file

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

View file

@ -613,7 +613,7 @@ func TestPack(t *testing.T) {
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), // tuple[1].A[1] "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), // tuple[1].A[1]
}, },
} { } {
typ, err := NewType(test.typ, test.components) typ, err := NewType(test.typ, "", test.components)
if err != nil { if err != nil {
t.Fatalf("%v failed. Unexpected parse error: %v", i, err) t.Fatalf("%v failed. Unexpected parse error: %v", i, err)
} }
@ -634,7 +634,7 @@ func TestMethodPack(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
sig := abi.Methods["slice"].Id() sig := abi.Methods["slice"].ID()
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
@ -648,7 +648,7 @@ func TestMethodPack(t *testing.T) {
} }
var addrA, addrB = common.Address{1}, common.Address{2} var addrA, addrB = common.Address{1}, common.Address{2}
sig = abi.Methods["sliceAddress"].Id() sig = abi.Methods["sliceAddress"].ID()
sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes(addrA[:], 32)...) sig = append(sig, common.LeftPadBytes(addrA[:], 32)...)
@ -663,7 +663,7 @@ func TestMethodPack(t *testing.T) {
} }
var addrC, addrD = common.Address{3}, common.Address{4} var addrC, addrD = common.Address{3}, common.Address{4}
sig = abi.Methods["sliceMultiAddress"].Id() sig = abi.Methods["sliceMultiAddress"].ID()
sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
@ -681,7 +681,7 @@ func TestMethodPack(t *testing.T) {
t.Errorf("expected %x got %x", sig, packed) t.Errorf("expected %x got %x", sig, packed)
} }
sig = abi.Methods["slice256"].Id() sig = abi.Methods["slice256"].ID()
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
@ -695,7 +695,7 @@ func TestMethodPack(t *testing.T) {
} }
a := [2][2]*big.Int{{big.NewInt(1), big.NewInt(1)}, {big.NewInt(2), big.NewInt(0)}} a := [2][2]*big.Int{{big.NewInt(1), big.NewInt(1)}, {big.NewInt(2), big.NewInt(0)}}
sig = abi.Methods["nestedArray"].Id() sig = abi.Methods["nestedArray"].ID()
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
@ -712,7 +712,7 @@ func TestMethodPack(t *testing.T) {
t.Errorf("expected %x got %x", sig, packed) t.Errorf("expected %x got %x", sig, packed)
} }
sig = abi.Methods["nestedArray2"].Id() sig = abi.Methods["nestedArray2"].ID()
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0x80}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{0x80}, 32)...)
@ -728,7 +728,7 @@ func TestMethodPack(t *testing.T) {
t.Errorf("expected %x got %x", sig, packed) t.Errorf("expected %x got %x", sig, packed)
} }
sig = abi.Methods["nestedSlice"].Id() sig = abi.Methods["nestedSlice"].ID()
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0x02}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{0x02}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...) sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)

View file

@ -53,6 +53,7 @@ type Type struct {
stringKind string // holds the unparsed string for deriving signatures stringKind string // holds the unparsed string for deriving signatures
// Tuple relative fields // Tuple relative fields
TupleRawName string // Raw struct name defined in source code, may be empty.
TupleElems []*Type // Type information of all tuple fields TupleElems []*Type // Type information of all tuple fields
TupleRawNames []string // Raw field name of all tuple fields TupleRawNames []string // Raw field name of all tuple fields
} }
@ -63,7 +64,7 @@ var (
) )
// NewType creates a new reflection type of abi type given in t. // NewType creates a new reflection type of abi type given in t.
func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) { func NewType(t string, internalType string, components []ArgumentMarshaling) (typ Type, err error) {
// check that array brackets are equal if they exist // check that array brackets are equal if they exist
if strings.Count(t, "[") != strings.Count(t, "]") { if strings.Count(t, "[") != strings.Count(t, "]") {
return Type{}, fmt.Errorf("invalid arg type in abi") return Type{}, fmt.Errorf("invalid arg type in abi")
@ -73,9 +74,14 @@ func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) {
// if there are brackets, get ready to go into slice/array mode and // if there are brackets, get ready to go into slice/array mode and
// recursively create the type // recursively create the type
if strings.Count(t, "[") != 0 { if strings.Count(t, "[") != 0 {
i := strings.LastIndex(t, "[") // Note internalType can be empty here.
subInternal := internalType
if i := strings.LastIndex(internalType, "["); i != -1 {
subInternal = subInternal[:i]
}
// recursively embed the type // recursively embed the type
embeddedType, err := NewType(t[:i], components) i := strings.LastIndex(t, "[")
embeddedType, err := NewType(t[:i], subInternal, components)
if err != nil { if err != nil {
return Type{}, err return Type{}, err
} }
@ -173,7 +179,7 @@ func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) {
) )
expression += "(" expression += "("
for idx, c := range components { for idx, c := range components {
cType, err := NewType(c.Type, c.Components) cType, err := NewType(c.Type, c.InternalType, c.Components)
if err != nil { if err != nil {
return Type{}, err return Type{}, err
} }
@ -199,6 +205,17 @@ func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) {
typ.TupleRawNames = names typ.TupleRawNames = names
typ.T = TupleTy typ.T = TupleTy
typ.stringKind = expression typ.stringKind = expression
const structPrefix = "struct "
// After solidity 0.5.10, a new field of abi "internalType"
// is introduced. From that we can obtain the struct name
// user defined in the source code.
if internalType != "" && strings.HasPrefix(internalType, structPrefix) {
// Foo.Bar type definition is not allowed in golang,
// convert the format to FooBar
typ.TupleRawName = strings.Replace(internalType[len(structPrefix):], ".", "", -1)
}
case "function": case "function":
typ.Kind = reflect.Array typ.Kind = reflect.Array
typ.T = FunctionTy typ.T = FunctionTy

View file

@ -106,7 +106,7 @@ func TestTypeRegexp(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
typ, err := NewType(tt.blob, tt.components) typ, err := NewType(tt.blob, "", tt.components)
if err != nil { if err != nil {
t.Errorf("type %q: failed to parse type string: %v", tt.blob, err) t.Errorf("type %q: failed to parse type string: %v", tt.blob, err)
} }
@ -281,7 +281,7 @@ func TestTypeCheck(t *testing.T) {
B *big.Int B *big.Int
}{{big.NewInt(0), big.NewInt(0)}, {big.NewInt(0), big.NewInt(0)}}, ""}, }{{big.NewInt(0), big.NewInt(0)}, {big.NewInt(0), big.NewInt(0)}}, ""},
} { } {
typ, err := NewType(test.typ, test.components) typ, err := NewType(test.typ, "", test.components)
if err != nil && len(test.err) == 0 { if err != nil && len(test.err) == 0 {
t.Fatal("unexpected parse error:", err) t.Fatal("unexpected parse error:", err)
} else if err != nil && len(test.err) != 0 { } else if err != nil && len(test.err) != 0 {

View file

@ -51,6 +51,7 @@ func (test unpackTest) checkError(err error) error {
} }
var unpackTests = []unpackTest{ var unpackTests = []unpackTest{
// Bools
{ {
def: `[{ "type": "bool" }]`, def: `[{ "type": "bool" }]`,
enc: "0000000000000000000000000000000000000000000000000000000000000001", enc: "0000000000000000000000000000000000000000000000000000000000000001",
@ -73,6 +74,7 @@ var unpackTests = []unpackTest{
want: false, want: false,
err: "abi: improperly encoded boolean value", err: "abi: improperly encoded boolean value",
}, },
// Integers
{ {
def: `[{"type": "uint32"}]`, def: `[{"type": "uint32"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000001", enc: "0000000000000000000000000000000000000000000000000000000000000001",
@ -122,11 +124,13 @@ var unpackTests = []unpackTest{
enc: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", enc: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
want: big.NewInt(-1), want: big.NewInt(-1),
}, },
// Address
{ {
def: `[{"type": "address"}]`, def: `[{"type": "address"}]`,
enc: "0000000000000000000000000100000000000000000000000000000000000000", enc: "0000000000000000000000000100000000000000000000000000000000000000",
want: common.Address{1}, want: common.Address{1},
}, },
// Bytes
{ {
def: `[{"type": "bytes32"}]`, def: `[{"type": "bytes32"}]`,
enc: "0100000000000000000000000000000000000000000000000000000000000000", enc: "0100000000000000000000000000000000000000000000000000000000000000",
@ -154,23 +158,39 @@ var unpackTests = []unpackTest{
enc: "0100000000000000000000000000000000000000000000000000000000000000", enc: "0100000000000000000000000000000000000000000000000000000000000000",
want: [32]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, want: [32]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
}, },
// Functions
{ {
def: `[{"type": "function"}]`, def: `[{"type": "function"}]`,
enc: "0100000000000000000000000000000000000000000000000000000000000000", enc: "0100000000000000000000000000000000000000000000000000000000000000",
want: [24]byte{1}, want: [24]byte{1},
}, },
// slices // Slice and Array
{ {
def: `[{"type": "uint8[]"}]`, def: `[{"type": "uint8[]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: []uint8{1, 2}, want: []uint8{1, 2},
}, },
{
def: `[{"type": "uint8[]"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000",
want: []uint8{},
},
{
def: `[{"type": "uint256[]"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000",
want: []*big.Int{},
},
{ {
def: `[{"type": "uint8[2]"}]`, def: `[{"type": "uint8[2]"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: [2]uint8{1, 2}, want: [2]uint8{1, 2},
}, },
// multi dimensional, if these pass, all types that don't require length prefix should pass // multi dimensional, if these pass, all types that don't require length prefix should pass
{
def: `[{"type": "uint8[][]"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000",
want: [][]uint8{},
},
{ {
def: `[{"type": "uint8[][]"}]`, def: `[{"type": "uint8[][]"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
@ -186,11 +206,21 @@ var unpackTests = []unpackTest{
enc: "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", enc: "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: [2][2]uint8{{1, 2}, {1, 2}}, want: [2][2]uint8{{1, 2}, {1, 2}},
}, },
{
def: `[{"type": "uint8[][2]"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
want: [2][]uint8{{}, {}},
},
{ {
def: `[{"type": "uint8[][2]"}]`, def: `[{"type": "uint8[][2]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001", enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001",
want: [2][]uint8{{1}, {1}}, want: [2][]uint8{{1}, {1}},
}, },
{
def: `[{"type": "uint8[2][]"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000",
want: [][2]uint8{},
},
{ {
def: `[{"type": "uint8[2][]"}]`, def: `[{"type": "uint8[2][]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
@ -420,7 +450,7 @@ func TestUnpack(t *testing.T) {
} }
encb, err := hex.DecodeString(test.enc) encb, err := hex.DecodeString(test.enc)
if err != nil { if err != nil {
t.Fatalf("invalid hex: %s" + test.enc) t.Fatalf("invalid hex %s: %v", test.enc, err)
} }
outptr := reflect.New(reflect.TypeOf(test.want)) outptr := reflect.New(reflect.TypeOf(test.want))
err = abi.Unpack(outptr.Interface(), "method", encb) err = abi.Unpack(outptr.Interface(), "method", encb)

View file

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

View file

@ -35,7 +35,7 @@ var ErrNotSupported = errors.New("not supported")
// ErrInvalidPassphrase is returned when a decryption operation receives a bad // ErrInvalidPassphrase is returned when a decryption operation receives a bad
// passphrase. // passphrase.
var ErrInvalidPassphrase = errors.New("invalid passphrase") var ErrInvalidPassphrase = errors.New("invalid password")
// ErrWalletAlreadyOpen is returned if a wallet is attempted to be opened the // ErrWalletAlreadyOpen is returned if a wallet is attempted to be opened the
// second time. // second time.

View file

@ -1,18 +1,18 @@
// Copyright 2018 The go-ethereum Authors // Copyright 2019 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 external package external
@ -204,14 +204,14 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
} }
func (api *ExternalSigner) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) { func (api *ExternalSigner) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
return []byte{}, fmt.Errorf("passphrase-operations not supported on external signers") return []byte{}, fmt.Errorf("password-operations not supported on external signers")
} }
func (api *ExternalSigner) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { func (api *ExternalSigner) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
return nil, fmt.Errorf("passphrase-operations not supported on external signers") return nil, fmt.Errorf("password-operations not supported on external signers")
} }
func (api *ExternalSigner) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) { func (api *ExternalSigner) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
return nil, fmt.Errorf("passphrase-operations not supported on external signers") return nil, fmt.Errorf("password-operations not supported on external signers")
} }
func (api *ExternalSigner) listAccounts() ([]common.Address, error) { func (api *ExternalSigner) listAccounts() ([]common.Address, error) {

View file

@ -43,7 +43,7 @@ import (
var ( var (
ErrLocked = accounts.NewAuthNeededError("password or unlock") ErrLocked = accounts.NewAuthNeededError("password or unlock")
ErrNoMatch = errors.New("no key for given address or file") ErrNoMatch = errors.New("no key for given address or file")
ErrDecrypt = errors.New("could not decrypt key with given passphrase") ErrDecrypt = errors.New("could not decrypt key with given password")
) )
// KeyStoreType is the reflect type of a keystore backend. // KeyStoreType is the reflect type of a keystore backend.

View file

@ -96,7 +96,7 @@ func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if _, err = ks.GetKey(k1.Address, account.URL.Path, "bar"); err != ErrDecrypt { if _, err = ks.GetKey(k1.Address, account.URL.Path, "bar"); err != ErrDecrypt {
t.Fatalf("wrong error for invalid passphrase\ngot %q\nwant %q", err, ErrDecrypt) t.Fatalf("wrong error for invalid password\ngot %q\nwant %q", err, ErrDecrypt)
} }
} }

View file

@ -1,5 +1,5 @@
This directory contains accounts for testing. This directory contains accounts for testing.
The passphrase that unlocks them is "foobar". The password that unlocks them is "foobar".
The "good" key files which are supposed to be loadable are: The "good" key files which are supposed to be loadable are:

View file

@ -21,6 +21,7 @@ import (
"sort" "sort"
"sync" "sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
) )
@ -162,6 +163,20 @@ func (am *Manager) Wallet(url string) (Wallet, error) {
return nil, ErrUnknownWallet return nil, ErrUnknownWallet
} }
// Accounts returns all account addresses of all wallets within the account manager
func (am *Manager) Accounts() []common.Address {
am.lock.RLock()
defer am.lock.RUnlock()
addresses := make([]common.Address, 0) // return [] instead of nil if empty
for _, wallet := range am.wallets {
for _, account := range wallet.Accounts() {
addresses = append(addresses, account.Address)
}
}
return addresses
}
// Find attempts to locate the wallet corresponding to a specific account. Since // Find attempts to locate the wallet corresponding to a specific account. Since
// accounts can be dynamically added to and removed from wallets, this method has // accounts can be dynamically added to and removed from wallets, this method has
// a linear runtime in the number of wallets. // a linear runtime in the number of wallets.

View file

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

View file

@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
@ -341,7 +342,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
op = ledgerP1ContTransactionData op = ledgerP1ContTransactionData
} }
// Extract the Ethereum signature and do a sanity validation // Extract the Ethereum signature and do a sanity validation
if len(reply) != 65 { if len(reply) != crypto.SignatureLength {
return common.Address{}, nil, errors.New("reply lacks signature") return common.Address{}, nil, errors.New("reply lacks signature")
} }
signature := append(reply[1:], reply[0]) signature := append(reply[1:], reply[0])

View file

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

View file

@ -479,7 +479,8 @@ func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
if _, ok := w.paths[address]; !ok { if _, ok := w.paths[address]; !ok {
w.accounts = append(w.accounts, account) w.accounts = append(w.accounts, account)
w.paths[address] = path w.paths[address] = make(accounts.DerivationPath, len(path))
copy(w.paths[address], path)
} }
return account, nil return account, nil
} }

View file

@ -23,8 +23,8 @@ environment:
install: install:
- git submodule update --init - git submodule update --init
- rmdir C:\go /s /q - rmdir C:\go /s /q
- appveyor DownloadFile https://dl.google.com/go/go1.12.6.windows-%GETH_ARCH%.zip - appveyor DownloadFile https://dl.google.com/go/go1.13.4.windows-%GETH_ARCH%.zip
- 7z x go1.12.6.windows-%GETH_ARCH%.zip -y -oC:\ > NUL - 7z x go1.13.4.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
- go version - go version
- gcc --version - gcc --version

View file

@ -22,19 +22,18 @@ variables `PPA_SIGNING_KEY` and `PPA_SSH_KEY` on Travis.
We want to build go-ethereum with the most recent version of Go, irrespective of the Go We want to build go-ethereum with the most recent version of Go, irrespective of the Go
version that is available in the main Ubuntu repository. In order to make this possible, version that is available in the main Ubuntu repository. In order to make this possible,
our PPA depends on the ~gophers/ubuntu/archive PPA. Our source package build-depends on we bundle the entire Go sources into our own source archive and start the built job by
golang-1.11, which is co-installable alongside the regular golang package. PPA dependencies compiling Go and then using that to build go-ethereum. On Trusty we have a special case
can be edited at https://launchpad.net/%7Eethereum/+archive/ubuntu/ethereum/+edit-dependencies requiring the `~gophers/ubuntu/archive` PPA since Trusty can't even build Go itself. PPA
deps are set at https://launchpad.net/%7Eethereum/+archive/ubuntu/ethereum/+edit-dependencies
## Building Packages Locally (for testing) ## Building Packages Locally (for testing)
You need to run Ubuntu to do test packaging. You need to run Ubuntu to do test packaging.
Add the gophers PPA and install Go 1.11 and Debian packaging tools: Install any version of Go and Debian packaging tools:
$ sudo apt-add-repository ppa:gophers/ubuntu/archive $ sudo apt-get install build-essential golang-go devscripts debhelper python-bzrlib python-paramiko
$ sudo apt-get update
$ sudo apt-get install build-essential golang-1.11 devscripts debhelper python-bzrlib python-paramiko
Create the source packages: Create the source packages:
@ -42,10 +41,10 @@ Create the source packages:
Then go into the source package directory for your running distribution and build the package: Then go into the source package directory for your running distribution and build the package:
$ cd dist/ethereum-unstable-1.6.0+xenial $ cd dist/ethereum-unstable-1.9.6+bionic
$ dpkg-buildpackage $ dpkg-buildpackage
Built packages are placed in the dist/ directory. Built packages are placed in the dist/ directory.
$ cd .. $ cd ..
$ dpkg-deb -c geth-unstable_1.6.0+xenial_amd64.deb $ dpkg-deb -c geth-unstable_1.9.6+bionic_amd64.deb

View file

@ -58,6 +58,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/internal/build" "github.com/ethereum/go-ethereum/internal/build"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
@ -137,7 +138,19 @@ var (
// Note: yakkety is unsupported because it was officially deprecated on Launchpad. // Note: yakkety is unsupported because it was officially deprecated on Launchpad.
// Note: zesty is unsupported because it was officially deprecated on Launchpad. // Note: zesty is unsupported because it was officially deprecated on Launchpad.
// Note: artful is unsupported because it was officially deprecated on Launchpad. // Note: artful is unsupported because it was officially deprecated on Launchpad.
debDistros = []string{"trusty", "xenial", "bionic", "cosmic", "disco"} // Note: cosmic is unsupported because it was officially deprecated on Launchpad.
debDistroGoBoots = map[string]string{
"trusty": "golang-1.11",
"xenial": "golang-go",
"bionic": "golang-go",
"disco": "golang-go",
"eoan": "golang-go",
}
debGoBootPaths = map[string]string{
"golang-1.11": "/usr/lib/go-1.11",
"golang-go": "/usr/lib/go",
}
) )
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin")) var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
@ -213,7 +226,6 @@ func doInstall(cmdline []string) {
if flag.NArg() > 0 { if flag.NArg() > 0 {
packages = flag.Args() packages = flag.Args()
} }
packages = build.ExpandPackagesNoVendor(packages)
if *arch == "" || *arch == runtime.GOARCH { if *arch == "" || *arch == runtime.GOARCH {
goinstall := goTool("install", buildFlags(env)...) goinstall := goTool("install", buildFlags(env)...)
@ -310,13 +322,12 @@ func doTest(cmdline []string) {
if len(flag.CommandLine.Args()) > 0 { if len(flag.CommandLine.Args()) > 0 {
packages = flag.CommandLine.Args() packages = flag.CommandLine.Args()
} }
packages = build.ExpandPackagesNoVendor(packages)
// Run the actual tests. // Run the actual tests.
// Test a single package at a time. CI builders are slow // Test a single package at a time. CI builders are slow
// and some tests run into timeouts under load. // and some tests run into timeouts under load.
gotest := goTool("test", buildFlags(env)...) gotest := goTool("test", buildFlags(env)...)
gotest.Args = append(gotest.Args, "-p", "1", "-timeout", "5m") gotest.Args = append(gotest.Args, "-p", "1", "-timeout", "5m", "--short")
if *coverage { if *coverage {
gotest.Args = append(gotest.Args, "-covermode=atomic", "-cover") gotest.Args = append(gotest.Args, "-covermode=atomic", "-cover")
} }
@ -460,11 +471,14 @@ func maybeSkipArchive(env build.Environment) {
// Debian Packaging // Debian Packaging
func doDebianSource(cmdline []string) { func doDebianSource(cmdline []string) {
var ( var (
signer = flag.String("signer", "", `Signing key name, also used as package author`) goversion = flag.String("goversion", "", `Go version to build with (will be included in the source package)`)
upload = flag.String("upload", "", `Where to upload the source package (usually "ethereum/ethereum")`) gobundle = flag.String("gobundle", "/tmp/go.tar.gz", `Filesystem path to cache the downloaded Go bundles at`)
sshUser = flag.String("sftp-user", "", `Username for SFTP upload (usually "geth-ci")`) gohash = flag.String("gohash", "", `SHA256 checksum of the Go sources requested to build with`)
workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`) signer = flag.String("signer", "", `Signing key name, also used as package author`)
now = time.Now() upload = flag.String("upload", "", `Where to upload the source package (usually "ethereum/ethereum")`)
sshUser = flag.String("sftp-user", "", `Username for SFTP upload (usually "geth-ci")`)
workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`)
now = time.Now()
) )
flag.CommandLine.Parse(cmdline) flag.CommandLine.Parse(cmdline)
*workdir = makeWorkdir(*workdir) *workdir = makeWorkdir(*workdir)
@ -477,12 +491,25 @@ func doDebianSource(cmdline []string) {
gpg.Stdin = bytes.NewReader(key) gpg.Stdin = bytes.NewReader(key)
build.MustRun(gpg) build.MustRun(gpg)
} }
// Download and verify the Go source package
if err := build.EnsureGoSources(*goversion, hexutil.MustDecode("0x"+*gohash), *gobundle); err != nil {
log.Fatalf("Failed to ensure Go source package: %v", err)
}
// Create Debian packages and upload them // Create Debian packages and upload them
for _, pkg := range debPackages { for _, pkg := range debPackages {
for _, distro := range debDistros { for distro, goboot := range debDistroGoBoots {
meta := newDebMetadata(distro, *signer, env, now, pkg.Name, pkg.Version, pkg.Executables) // Prepare the debian package with the go-ethereum sources
meta := newDebMetadata(distro, goboot, *signer, env, now, pkg.Name, pkg.Version, pkg.Executables)
pkgdir := stageDebianSource(*workdir, meta) pkgdir := stageDebianSource(*workdir, meta)
// Ship the Go sources along so we have a proper thing to build with
if err := build.ExtractTarballArchive(*gobundle, pkgdir); err != nil {
log.Fatalf("Failed to extract Go sources: %v", err)
}
if err := os.Rename(filepath.Join(pkgdir, "go"), filepath.Join(pkgdir, ".go")); err != nil {
log.Fatalf("Failed to rename Go source folder: %v", err)
}
// Run the packaging and upload to the PPA
debuild := exec.Command("debuild", "-S", "-sa", "-us", "-uc", "-d", "-Zxz") debuild := exec.Command("debuild", "-S", "-sa", "-us", "-uc", "-d", "-Zxz")
debuild.Dir = pkgdir debuild.Dir = pkgdir
build.MustRun(debuild) build.MustRun(debuild)
@ -562,7 +589,9 @@ type debPackage struct {
} }
type debMetadata struct { type debMetadata struct {
Env build.Environment Env build.Environment
GoBootPackage string
GoBootPath string
PackageName string PackageName string
@ -591,19 +620,21 @@ func (d debExecutable) Package() string {
return d.BinaryName return d.BinaryName
} }
func newDebMetadata(distro, author string, env build.Environment, t time.Time, name string, version string, exes []debExecutable) debMetadata { func newDebMetadata(distro, goboot, author string, env build.Environment, t time.Time, name string, version string, exes []debExecutable) debMetadata {
if author == "" { if author == "" {
// No signing key, use default author. // No signing key, use default author.
author = "Ethereum Builds <fjl@ethereum.org>" author = "Ethereum Builds <fjl@ethereum.org>"
} }
return debMetadata{ return debMetadata{
PackageName: name, GoBootPackage: goboot,
Env: env, GoBootPath: debGoBootPaths[goboot],
Author: author, PackageName: name,
Distro: distro, Env: env,
Version: version, Author: author,
Time: t.Format(time.RFC1123Z), Distro: distro,
Executables: exes, Version: version,
Time: t.Format(time.RFC1123Z),
Executables: exes,
} }
} }
@ -668,7 +699,6 @@ func stageDebianSource(tmpdir string, meta debMetadata) (pkgdir string) {
if err := os.Mkdir(pkgdir, 0755); err != nil { if err := os.Mkdir(pkgdir, 0755); err != nil {
log.Fatal(err) log.Fatal(err)
} }
// Copy the source code. // Copy the source code.
build.MustRunCommand("git", "checkout-index", "-a", "--prefix", pkgdir+string(filepath.Separator)) build.MustRunCommand("git", "checkout-index", "-a", "--prefix", pkgdir+string(filepath.Separator))
@ -686,7 +716,6 @@ func stageDebianSource(tmpdir string, meta debMetadata) (pkgdir string) {
build.Render("build/deb/"+meta.PackageName+"/deb.install", install, 0644, exe) build.Render("build/deb/"+meta.PackageName+"/deb.install", install, 0644, exe)
build.Render("build/deb/"+meta.PackageName+"/deb.docs", docs, 0644, exe) build.Render("build/deb/"+meta.PackageName+"/deb.docs", docs, 0644, exe)
} }
return pkgdir return pkgdir
} }
@ -899,7 +928,7 @@ func doXCodeFramework(cmdline []string) {
// Build the iOS XCode framework // Build the iOS XCode framework
build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile", "golang.org/x/mobile/cmd/gobind")) build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile", "golang.org/x/mobile/cmd/gobind"))
build.MustRun(gomobileTool("init")) build.MustRun(gomobileTool("init"))
bind := gomobileTool("bind", "-ldflags", "-s -w", "--target", "ios", "--tags", "ios", "-v", "github.com/ethereum/go-ethereum/mobile") bind := gomobileTool("bind", "-ldflags", "-s -w", "--target", "ios", "-v", "github.com/ethereum/go-ethereum/mobile")
if *local { if *local {
// If we're building locally, use the build folder and stop afterwards // If we're building locally, use the build folder and stop afterwards

View file

@ -2,7 +2,7 @@ Source: {{.Name}}
Section: science Section: science
Priority: extra Priority: extra
Maintainer: {{.Author}} Maintainer: {{.Author}}
Build-Depends: debhelper (>= 8.0.0), golang-1.11 Build-Depends: debhelper (>= 8.0.0), {{.GoBootPackage}}
Standards-Version: 3.9.5 Standards-Version: 3.9.5
Homepage: https://ethereum.org Homepage: https://ethereum.org
Vcs-Git: git://github.com/ethereum/go-ethereum.git Vcs-Git: git://github.com/ethereum/go-ethereum.git

View file

@ -6,9 +6,11 @@
# Launchpad rejects Go's access to $HOME/.cache, use custom folder # Launchpad rejects Go's access to $HOME/.cache, use custom folder
export GOCACHE=/tmp/go-build export GOCACHE=/tmp/go-build
export GOROOT_BOOTSTRAP={{.GoBootPath}}
override_dh_auto_build: override_dh_auto_build:
build/env.sh /usr/lib/go-1.11/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}} (cd .go/src && ./make.bash)
build/env.sh .go/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}}
override_dh_auto_test: override_dh_auto_test:

View file

@ -62,16 +62,22 @@ var (
skipPrefixes = []string{ skipPrefixes = []string{
// boring stuff // boring stuff
"vendor/", "tests/testdata/", "build/", "vendor/", "tests/testdata/", "build/",
// don't relicense vendored sources // don't relicense vendored sources
"cmd/internal/browser", "cmd/internal/browser",
"common/bitutil/bitutil",
"common/prque/",
"consensus/ethash/xor.go", "consensus/ethash/xor.go",
"crypto/bn256/", "crypto/bn256/",
"crypto/ecies/", "crypto/ecies/",
"crypto/secp256k1/curve.go", "graphql/graphiql.go",
"crypto/sha3/",
"internal/jsre/deps", "internal/jsre/deps",
"log/", "log/",
"common/bitutil/bitutil", "metrics/",
"signer/rules/deps",
// skip special licenses
"crypto/secp256k1", // Relicensed to BSD-3 via https://github.com/ethereum/go-ethereum/pull/17225
} }
// 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.
@ -144,6 +150,13 @@ func (i info) gpl() bool {
return false return false
} }
// authors implements the sort.Interface for strings in case-insensitive mode.
type authors []string
func (as authors) Len() int { return len(as) }
func (as authors) Less(i, j int) bool { return strings.ToLower(as[i]) < strings.ToLower(as[j]) }
func (as authors) Swap(i, j int) { as[i], as[j] = as[j], as[i] }
func main() { func main() {
var ( var (
files = getFiles() files = getFiles()
@ -262,27 +275,32 @@ func mailmapLookup(authors []string) []string {
} }
func writeAuthors(files []string) { func writeAuthors(files []string) {
merge := make(map[string]bool) var (
// Add authors that Git reports as contributorxs. dedup = make(map[string]bool)
list []string
)
// Add authors that Git reports as contributors.
// This is the primary source of author information. // This is the primary source of author information.
for _, a := range gitAuthors(files) { for _, a := range gitAuthors(files) {
merge[a] = true if la := strings.ToLower(a); !dedup[la] {
list = append(list, a)
dedup[la] = true
}
} }
// Add existing authors from the file. This should ensure that we // Add existing authors from the file. This should ensure that we
// never lose authors, even if Git stops listing them. We can also // never lose authors, even if Git stops listing them. We can also
// add authors manually this way. // add authors manually this way.
for _, a := range readAuthors() { for _, a := range readAuthors() {
merge[a] = true if la := strings.ToLower(a); !dedup[la] {
list = append(list, a)
dedup[la] = true
}
} }
// Write sorted list of authors back to the file. // Write sorted list of authors back to the file.
var result []string sort.Sort(authors(list))
for a := range merge {
result = append(result, a)
}
sort.Strings(result)
content := new(bytes.Buffer) content := new(bytes.Buffer)
content.WriteString(authorsFileHeader) content.WriteString(authorsFileHeader)
for _, a := range result { for _, a := range list {
content.WriteString(a) content.WriteString(a)
content.WriteString("\n") content.WriteString("\n")
} }

View file

@ -1,4 +1,4 @@
// Copyright 2019 The go-ethereum Authors // Copyright 2016 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
@ -18,62 +18,129 @@ package main
import ( import (
"encoding/json" "encoding/json"
"flag"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"strings" "strings"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common/compiler" "github.com/ethereum/go-ethereum/common/compiler"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"gopkg.in/urfave/cli.v1"
)
const (
commandHelperTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...]
{{if .Description}}{{.Description}}
{{end}}{{if .Subcommands}}
SUBCOMMANDS:
{{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
{{end}}{{end}}{{if .Flags}}
OPTIONS:
{{range $.Flags}}{{"\t"}}{{.}}
{{end}}
{{end}}`
) )
var ( var (
abiFlag = flag.String("abi", "", "Path to the Ethereum contract ABI json to bind, - for STDIN") // Git SHA1 commit hash of the release (set via linker flags)
binFlag = flag.String("bin", "", "Path to the Ethereum contract bytecode (generate deploy method)") gitCommit = ""
typFlag = flag.String("type", "", "Struct name for the binding (default = package name)") gitDate = ""
solFlag = flag.String("sol", "", "Path to the Ethereum contract Solidity source to build and bind") app *cli.App
solcFlag = flag.String("solc", "solc", "Solidity compiler to use if source builds are requested")
excFlag = flag.String("exc", "", "Comma separated types to exclude from binding")
vyFlag = flag.String("vy", "", "Path to the Ethereum contract Vyper source to build and bind") // Flags needed by abigen
vyperFlag = flag.String("vyper", "vyper", "Vyper compiler to use if source builds are requested") abiFlag = cli.StringFlag{
Name: "abi",
pkgFlag = flag.String("pkg", "", "Package name to generate the binding into") Usage: "Path to the Ethereum contract ABI json to bind, - for STDIN",
outFlag = flag.String("out", "", "Output file for the generated binding (default = stdout)") }
langFlag = flag.String("lang", "go", "Destination language for the bindings (go, java, objc)") binFlag = cli.StringFlag{
Name: "bin",
Usage: "Path to the Ethereum contract bytecode (generate deploy method)",
}
typeFlag = cli.StringFlag{
Name: "type",
Usage: "Struct name for the binding (default = package name)",
}
jsonFlag = cli.StringFlag{
Name: "combined-json",
Usage: "Path to the combined-json file generated by compiler",
}
solFlag = cli.StringFlag{
Name: "sol",
Usage: "Path to the Ethereum contract Solidity source to build and bind",
}
solcFlag = cli.StringFlag{
Name: "solc",
Usage: "Solidity compiler to use if source builds are requested",
Value: "solc",
}
vyFlag = cli.StringFlag{
Name: "vy",
Usage: "Path to the Ethereum contract Vyper source to build and bind",
}
vyperFlag = cli.StringFlag{
Name: "vyper",
Usage: "Vyper compiler to use if source builds are requested",
Value: "vyper",
}
excFlag = cli.StringFlag{
Name: "exc",
Usage: "Comma separated types to exclude from binding",
}
pkgFlag = cli.StringFlag{
Name: "pkg",
Usage: "Package name to generate the binding into",
}
outFlag = cli.StringFlag{
Name: "out",
Usage: "Output file for the generated binding (default = stdout)",
}
langFlag = cli.StringFlag{
Name: "lang",
Usage: "Destination language for the bindings (go, java, objc)",
Value: "go",
}
) )
func main() { func init() {
// Parse and ensure all needed inputs are specified app = utils.NewApp(gitCommit, gitDate, "ethereum checkpoint helper tool")
flag.Parse() app.Flags = []cli.Flag{
abiFlag,
if *abiFlag == "" && *solFlag == "" && *vyFlag == "" { binFlag,
fmt.Printf("No contract ABI (--abi), Solidity source (--sol), or Vyper source (--vy) specified\n") typeFlag,
os.Exit(-1) jsonFlag,
} else if (*abiFlag != "" || *binFlag != "" || *typFlag != "") && (*solFlag != "" || *vyFlag != "") { solFlag,
fmt.Printf("Contract ABI (--abi), bytecode (--bin) and type (--type) flags are mutually exclusive with the Solidity (--sol) and Vyper (--vy) flags\n") solcFlag,
os.Exit(-1) vyFlag,
} else if *solFlag != "" && *vyFlag != "" { vyperFlag,
fmt.Printf("Solidity (--sol) and Vyper (--vy) flags are mutually exclusive\n") excFlag,
os.Exit(-1) pkgFlag,
outFlag,
langFlag,
} }
if *pkgFlag == "" { app.Action = utils.MigrateFlags(abigen)
fmt.Printf("No destination package specified (--pkg)\n") cli.CommandHelpTemplate = commandHelperTemplate
os.Exit(-1) }
func abigen(c *cli.Context) error {
utils.CheckExclusive(c, abiFlag, jsonFlag, solFlag, vyFlag) // Only one source can be selected.
if c.GlobalString(pkgFlag.Name) == "" {
utils.Fatalf("No destination package specified (--pkg)")
} }
var lang bind.Lang var lang bind.Lang
switch *langFlag { switch c.GlobalString(langFlag.Name) {
case "go": case "go":
lang = bind.LangGo lang = bind.LangGo
case "java": case "java":
lang = bind.LangJava lang = bind.LangJava
case "objc": case "objc":
lang = bind.LangObjC lang = bind.LangObjC
utils.Fatalf("Objc binding generation is uncompleted")
default: default:
fmt.Printf("Unsupported destination language \"%s\" (--lang)\n", *langFlag) utils.Fatalf("Unsupported destination language \"%s\" (--lang)", c.GlobalString(langFlag.Name))
os.Exit(-1)
} }
// If the entire solidity code was specified, build and bind based on that // If the entire solidity code was specified, build and bind based on that
var ( var (
@ -81,35 +148,69 @@ func main() {
bins []string bins []string
types []string types []string
sigs []map[string]string sigs []map[string]string
libs = make(map[string]string)
) )
if *solFlag != "" || *vyFlag != "" || *abiFlag == "-" { if c.GlobalString(abiFlag.Name) != "" {
// Load up the ABI, optional bytecode and type name from the parameters
var (
abi []byte
err error
)
input := c.GlobalString(abiFlag.Name)
if input == "-" {
abi, err = ioutil.ReadAll(os.Stdin)
} else {
abi, err = ioutil.ReadFile(input)
}
if err != nil {
utils.Fatalf("Failed to read input ABI: %v", err)
}
abis = append(abis, string(abi))
var bin []byte
if binFile := c.GlobalString(binFlag.Name); binFile != "" {
if bin, err = ioutil.ReadFile(binFile); err != nil {
utils.Fatalf("Failed to read input bytecode: %v", err)
}
if strings.Contains(string(bin), "//") {
utils.Fatalf("Contract has additional library references, please use other mode(e.g. --combined-json) to catch library infos")
}
}
bins = append(bins, string(bin))
kind := c.GlobalString(typeFlag.Name)
if kind == "" {
kind = c.GlobalString(pkgFlag.Name)
}
types = append(types, kind)
} else {
// Generate the list of types to exclude from binding // Generate the list of types to exclude from binding
exclude := make(map[string]bool) exclude := make(map[string]bool)
for _, kind := range strings.Split(*excFlag, ",") { for _, kind := range strings.Split(c.GlobalString(excFlag.Name), ",") {
exclude[strings.ToLower(kind)] = true exclude[strings.ToLower(kind)] = true
} }
var contracts map[string]*compiler.Contract
var err error var err error
var contracts map[string]*compiler.Contract
switch { switch {
case *solFlag != "": case c.GlobalIsSet(solFlag.Name):
contracts, err = compiler.CompileSolidity(*solcFlag, *solFlag) contracts, err = compiler.CompileSolidity(c.GlobalString(solcFlag.Name), c.GlobalString(solFlag.Name))
if err != nil { if err != nil {
fmt.Printf("Failed to build Solidity contract: %v\n", err) utils.Fatalf("Failed to build Solidity contract: %v", err)
os.Exit(-1)
} }
case *vyFlag != "": case c.GlobalIsSet(vyFlag.Name):
contracts, err = compiler.CompileVyper(*vyperFlag, *vyFlag) contracts, err = compiler.CompileVyper(c.GlobalString(vyperFlag.Name), c.GlobalString(vyFlag.Name))
if err != nil { if err != nil {
fmt.Printf("Failed to build Vyper contract: %v\n", err) utils.Fatalf("Failed to build Vyper contract: %v", err)
os.Exit(-1)
} }
default: case c.GlobalIsSet(jsonFlag.Name):
contracts, err = contractsFromStdin() jsonOutput, err := ioutil.ReadFile(c.GlobalString(jsonFlag.Name))
if err != nil { if err != nil {
fmt.Printf("Failed to read input ABIs from STDIN: %v\n", err) utils.Fatalf("Failed to read combined-json from compiler: %v", err)
os.Exit(-1) }
contracts, err = compiler.ParseCombinedJSON(jsonOutput, "", "", "", "")
if err != nil {
utils.Fatalf("Failed to read contract information from json output: %v", err)
} }
} }
// Gather all non-excluded contract for binding // Gather all non-excluded contract for binding
@ -119,62 +220,39 @@ func main() {
} }
abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse
if err != nil { if err != nil {
fmt.Printf("Failed to parse ABIs from compiler output: %v\n", err) utils.Fatalf("Failed to parse ABIs from compiler output: %v", err)
os.Exit(-1)
} }
abis = append(abis, string(abi)) abis = append(abis, string(abi))
bins = append(bins, contract.Code) bins = append(bins, contract.Code)
sigs = append(sigs, contract.Hashes) sigs = append(sigs, contract.Hashes)
nameParts := strings.Split(name, ":") nameParts := strings.Split(name, ":")
types = append(types, nameParts[len(nameParts)-1]) types = append(types, nameParts[len(nameParts)-1])
}
} else {
// Otherwise load up the ABI, optional bytecode and type name from the parameters
abi, err := ioutil.ReadFile(*abiFlag)
if err != nil { libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36]
fmt.Printf("Failed to read input ABI: %v\n", err) libs[libPattern] = nameParts[len(nameParts)-1]
os.Exit(-1)
} }
abis = append(abis, string(abi))
var bin []byte
if *binFlag != "" {
if bin, err = ioutil.ReadFile(*binFlag); err != nil {
fmt.Printf("Failed to read input bytecode: %v\n", err)
os.Exit(-1)
}
}
bins = append(bins, string(bin))
kind := *typFlag
if kind == "" {
kind = *pkgFlag
}
types = append(types, kind)
} }
// Generate the contract binding // Generate the contract binding
code, err := bind.Bind(types, abis, bins, sigs, *pkgFlag, lang) code, err := bind.Bind(types, abis, bins, sigs, c.GlobalString(pkgFlag.Name), lang, libs)
if err != nil { if err != nil {
fmt.Printf("Failed to generate ABI binding: %v\n", err) utils.Fatalf("Failed to generate ABI binding: %v", err)
os.Exit(-1)
} }
// Either flush it out to a file or display on the standard output // Either flush it out to a file or display on the standard output
if *outFlag == "" { if !c.GlobalIsSet(outFlag.Name) {
fmt.Printf("%s\n", code) fmt.Printf("%s\n", code)
return return nil
} }
if err := ioutil.WriteFile(*outFlag, []byte(code), 0600); err != nil { if err := ioutil.WriteFile(c.GlobalString(outFlag.Name), []byte(code), 0600); err != nil {
fmt.Printf("Failed to write ABI binding: %v\n", err) utils.Fatalf("Failed to write ABI binding: %v", err)
os.Exit(-1)
} }
return nil
} }
func contractsFromStdin() (map[string]*compiler.Contract, error) { func main() {
bytes, err := ioutil.ReadAll(os.Stdin) log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
if err != nil {
return nil, err if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
} }
return compiler.ParseCombinedJSON(bytes, "", "", "", "")
} }

View file

@ -70,7 +70,9 @@ func main() {
if err = crypto.SaveECDSA(*genKey, nodeKey); err != nil { if err = crypto.SaveECDSA(*genKey, nodeKey); err != nil {
utils.Fatalf("%v", err) utils.Fatalf("%v", err)
} }
return if !*writeAddr {
return
}
case *nodeKeyFile == "" && *nodeKeyHex == "": case *nodeKeyFile == "" && *nodeKeyHex == "":
utils.Fatalf("Use -nodekey or -nodekeyhex to specify a private key") utils.Fatalf("Use -nodekey or -nodekeyhex to specify a private key")
case *nodeKeyFile != "" && *nodeKeyHex != "": case *nodeKeyFile != "" && *nodeKeyHex != "":

View file

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

View file

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

View file

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

View file

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

View file

@ -566,8 +566,8 @@ Response
### account_export ### account_export
#### Export account from keystore #### Export account from keystore
Export a private key from the keystore. The exported private key is encrypted with the original passphrase. When the Export a private key from the keystore. The exported private key is encrypted with the original password. When the
key is imported later this passphrase is required. key is imported later this password is required.
#### Arguments #### Arguments
- account [address]: export private key that is associated with this account - account [address]: export private key that is associated with this account
@ -914,7 +914,7 @@ A UI should conform to the following rules.
* For example, not load icons, stylesheets from the internet * For example, not load icons, stylesheets from the internet
* Not load files from the filesystem, unless they reside in the same local directory (e.g. config files) * Not load files from the filesystem, unless they reside in the same local directory (e.g. config files)
* A Graphical UI MUST show the blocky-identicon for ethereum addresses. * A Graphical UI MUST show the blocky-identicon for ethereum addresses.
* A UI MUST warn display approproate warning if the destination-account is formatted with invalid checksum. * A UI MUST warn display appropriate warning if the destination-account is formatted with invalid checksum.
* A UI MUST NOT open any ports or services * A UI MUST NOT open any ports or services
* The signer opens the public port * The signer opens the public port
* A UI SHOULD verify the permissions on the signer binary, and refuse to execute or warn if permissions allow non-user write. * A UI SHOULD verify the permissions on the signer binary, and refuse to execute or warn if permissions allow non-user write.

View file

@ -53,6 +53,8 @@ import (
"github.com/ethereum/go-ethereum/signer/fourbyte" "github.com/ethereum/go-ethereum/signer/fourbyte"
"github.com/ethereum/go-ethereum/signer/rules" "github.com/ethereum/go-ethereum/signer/rules"
"github.com/ethereum/go-ethereum/signer/storage" "github.com/ethereum/go-ethereum/signer/storage"
colorable "github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"gopkg.in/urfave/cli.v1" "gopkg.in/urfave/cli.v1"
) )
@ -333,7 +335,7 @@ func setCredential(ctx *cli.Context) error {
utils.Fatalf("Invalid address specified: %s", addr) utils.Fatalf("Invalid address specified: %s", addr)
} }
address := common.HexToAddress(addr) address := common.HexToAddress(addr)
password := getPassPhrase("Please enter a passphrase to store for this address:", true) password := getPassPhrase("Please enter a password to store for this address:", true)
fmt.Println() fmt.Println()
stretchedKey, err := readMasterKey(ctx, nil) stretchedKey, err := readMasterKey(ctx, nil)
@ -392,10 +394,37 @@ func initialize(c *cli.Context) error {
} }
fmt.Println() fmt.Println()
} }
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int(logLevelFlag.Name)), log.StreamHandler(logOutput, log.TerminalFormat(true)))) usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
output := io.Writer(logOutput)
if usecolor {
output = colorable.NewColorable(logOutput)
}
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int(logLevelFlag.Name)), log.StreamHandler(output, log.TerminalFormat(usecolor))))
return nil return nil
} }
// ipcEndpoint resolves an IPC endpoint based on a configured value, taking into
// account the set data folders as well as the designated platform we're currently
// running on.
func ipcEndpoint(ipcPath, datadir string) string {
// On windows we can only use plain top-level pipes
if runtime.GOOS == "windows" {
if strings.HasPrefix(ipcPath, `\\.\pipe\`) {
return ipcPath
}
return `\\.\pipe\` + ipcPath
}
// Resolve names into the data directory full paths otherwise
if filepath.Base(ipcPath) == ipcPath {
if datadir == "" {
return filepath.Join(os.TempDir(), ipcPath)
}
return filepath.Join(datadir, ipcPath)
}
return ipcPath
}
func signer(c *cli.Context) error { func signer(c *cli.Context) error {
// If we have some unrecognized command, bail out // If we have some unrecognized command, bail out
if args := c.Args(); len(args) > 0 { if args := c.Args(); len(args) > 0 {
@ -524,12 +553,8 @@ func signer(c *cli.Context) error {
}() }()
} }
if !c.GlobalBool(utils.IPCDisabledFlag.Name) { if !c.GlobalBool(utils.IPCDisabledFlag.Name) {
if c.IsSet(utils.IPCPathFlag.Name) { givenPath := c.GlobalString(utils.IPCPathFlag.Name)
ipcapiURL = c.GlobalString(utils.IPCPathFlag.Name) ipcapiURL = ipcEndpoint(filepath.Join(givenPath, "clef.ipc"), configDir)
} else {
ipcapiURL = filepath.Join(configDir, "clef.ipc")
}
listener, _, err := rpc.StartIPCEndpoint(ipcapiURL, rpcAPI) listener, _, err := rpc.StartIPCEndpoint(ipcapiURL, rpcAPI)
if err != nil { if err != nil {
utils.Fatalf("Could not start IPC api: %v", err) utils.Fatalf("Could not start IPC api: %v", err)
@ -539,7 +564,6 @@ func signer(c *cli.Context) error {
listener.Close() listener.Close()
log.Info("IPC endpoint closed", "url", ipcapiURL) log.Info("IPC endpoint closed", "url", ipcapiURL)
}() }()
} }
if c.GlobalBool(testFlag.Name) { if c.GlobalBool(testFlag.Name) {
@ -767,8 +791,8 @@ func testExternalUI(api *core.SignerAPI) {
data := `{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Person":[{"name":"name","type":"string"},{"name":"test","type":"uint8"},{"name":"wallet","type":"address"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person"},{"name":"contents","type":"string"}]},"primaryType":"Mail","domain":{"name":"Ether Mail","version":"1","chainId":"1","verifyingContract":"0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"},"message":{"from":{"name":"Cow","test":"3","wallet":"0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"},"to":{"name":"Bob","wallet":"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB","test":"2"},"contents":"Hello, Bob!"}}` data := `{"types":{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Person":[{"name":"name","type":"string"},{"name":"test","type":"uint8"},{"name":"wallet","type":"address"}],"Mail":[{"name":"from","type":"Person"},{"name":"to","type":"Person"},{"name":"contents","type":"string"}]},"primaryType":"Mail","domain":{"name":"Ether Mail","version":"1","chainId":"1","verifyingContract":"0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"},"message":{"from":{"name":"Cow","test":"3","wallet":"0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"},"to":{"name":"Bob","wallet":"0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB","test":"2"},"contents":"Hello, Bob!"}}`
//_, err := api.SignData(ctx, accounts.MimetypeTypedData, *addr, hexutil.Encode([]byte(data))) //_, err := api.SignData(ctx, accounts.MimetypeTypedData, *addr, hexutil.Encode([]byte(data)))
var typedData core.TypedData var typedData core.TypedData
err := json.Unmarshal([]byte(data), &typedData) json.Unmarshal([]byte(data), &typedData)
_, err = api.SignTypedData(ctx, *addr, typedData) _, err := api.SignTypedData(ctx, *addr, typedData)
expectApprove("sign 712 typed data", err) expectApprove("sign 712 typed data", err)
} }
{ // Sign data test - plain text { // Sign data test - plain text
@ -837,17 +861,17 @@ func testExternalUI(api *core.SignerAPI) {
// TODO: there are many `getPassPhrase` functions, it will be better to abstract them into one. // TODO: there are many `getPassPhrase` functions, it will be better to abstract them into one.
func getPassPhrase(prompt string, confirmation bool) string { func getPassPhrase(prompt string, confirmation bool) string {
fmt.Println(prompt) fmt.Println(prompt)
password, err := console.Stdin.PromptPassword("Passphrase: ") password, err := console.Stdin.PromptPassword("Password: ")
if err != nil { if err != nil {
utils.Fatalf("Failed to read passphrase: %v", err) utils.Fatalf("Failed to read password: %v", err)
} }
if confirmation { if confirmation {
confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ") confirm, err := console.Stdin.PromptPassword("Repeat password: ")
if err != nil { if err != nil {
utils.Fatalf("Failed to read passphrase confirmation: %v", err) utils.Fatalf("Failed to read password confirmation: %v", err)
} }
if password != confirm { if password != confirm {
utils.Fatalf("Passphrases do not match") utils.Fatalf("Passwords do not match")
} }
} }
return password return password

View file

@ -70,7 +70,7 @@ The Otto vm has a few [caveats](https://github.com/robertkrimen/otto):
Additionally, a few more have been added Additionally, a few more have been added
* The rule execution cannot load external javascript files. * The rule execution cannot load external javascript files.
* The only preloaded libary is [`bignumber.js`](https://github.com/MikeMcl/bignumber.js) version `2.0.3`. This one is fairly old, and is not aligned with the documentation at the github repository. * The only preloaded library is [`bignumber.js`](https://github.com/MikeMcl/bignumber.js) version `2.0.3`. This one is fairly old, and is not aligned with the documentation at the github repository.
* Each invocation is made in a fresh virtual machine. This means that you cannot store data in global variables between invocations. This is a deliberate choice -- if you want to store data, use the disk-backed `storage`, since rules should not rely on ephemeral data. * Each invocation is made in a fresh virtual machine. This means that you cannot store data in global variables between invocations. This is a deliberate choice -- if you want to store data, use the disk-backed `storage`, since rules should not rely on ephemeral data.
* Javascript API parameters are _always_ an object. This is also a design choice, to ensure that parameters are accessed by _key_ and not by order. This is to prevent mistakes due to missing parameters or parameter changes. * Javascript API parameters are _always_ an object. This is also a design choice, to ensure that parameters are accessed by _key_ and not by order. This is to prevent mistakes due to missing parameters or parameter changes.
* The JS engine has access to `storage` and `console`. * The JS engine has access to `storage` and `console`.

View file

@ -1,3 +1,19 @@
// Copyright 2019 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/>.
// This file is a test-utility for testing clef-functionality // This file is a test-utility for testing clef-functionality
// //
// Start clef with // Start clef with

View file

@ -22,8 +22,8 @@ Enter 'ok' to proceed:
The master seed of clef will be locked with a password. The master seed of clef will be locked with a password.
Please specify a password. Do not forget this password! Please specify a password. Do not forget this password!
Passphrase: Password:
Repeat passphrase: Repeat password:
A master seed has been generated into /home/martin/.clef/masterseed.json A master seed has been generated into /home/martin/.clef/masterseed.json
@ -124,7 +124,7 @@ $ sha256sum rules.js
$ clef attest 645b58e4f945e24d0221714ff29f6aa8e860382ced43490529db1695f5fcc71c $ clef attest 645b58e4f945e24d0221714ff29f6aa8e860382ced43490529db1695f5fcc71c
Decrypt master seed of clef Decrypt master seed of clef
Passphrase: Password:
INFO [07-01|13:25:03.290] Ruleset attestation updated sha256=645b58e4f945e24d0221714ff29f6aa8e860382ced43490529db1695f5fcc71c INFO [07-01|13:25:03.290] Ruleset attestation updated sha256=645b58e4f945e24d0221714ff29f6aa8e860382ced43490529db1695f5fcc71c
``` ```
@ -193,12 +193,12 @@ In order to make more useful rules - like signing transactions - the signer need
```text ```text
$ clef setpw 0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3 $ clef setpw 0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3
Please enter a passphrase to store for this address: Please enter a password to store for this address:
Passphrase: Password:
Repeat passphrase: Repeat password:
Decrypt master seed of clef Decrypt master seed of clef
Passphrase: Password:
INFO [07-01|14:05:56.031] Credential store updated key=0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3 INFO [07-01|14:05:56.031] Credential store updated key=0xd9c9cd5f6779558b6e0ed4e6acf6b1947e7fa1f3
``` ```
@ -227,7 +227,7 @@ In this example:
- Auto-rejected if the message does not contain `bazonk`, - Auto-rejected if the message does not contain `bazonk`,
- Any other requests will be passed along for manual confirmation. - Any other requests will be passed along for manual confirmation.
*Note, to make this example work, please use you own accounts. You can create a new account either via Clef or the traditional account CLI tools. If the latter was chosen, make sure both Clef and Geth use the same keystore by specifing `--keystore path/to/your/keystore` when running Clef.* *Note, to make this example work, please use you own accounts. You can create a new account either via Clef or the traditional account CLI tools. If the latter was chosen, make sure both Clef and Geth use the same keystore by specifying `--keystore path/to/your/keystore` when running Clef.*
Attest the new rule file so that Clef will accept loading it: Attest the new rule file so that Clef will accept loading it:
@ -237,7 +237,7 @@ f163a1738b649259bb9b369c593fdc4c6b6f86cc87e343c3ba58faee03c2a178 rules.js
$ clef attest f163a1738b649259bb9b369c593fdc4c6b6f86cc87e343c3ba58faee03c2a178 $ clef attest f163a1738b649259bb9b369c593fdc4c6b6f86cc87e343c3ba58faee03c2a178
Decrypt master seed of clef Decrypt master seed of clef
Passphrase: Password:
INFO [07-01|14:11:28.509] Ruleset attestation updated sha256=f163a1738b649259bb9b369c593fdc4c6b6f86cc87e343c3ba58faee03c2a178 INFO [07-01|14:11:28.509] Ruleset attestation updated sha256=f163a1738b649259bb9b369c593fdc4c6b6f86cc87e343c3ba58faee03c2a178
``` ```

152
cmd/devp2p/crawl.go Normal file
View file

@ -0,0 +1,152 @@
// Copyright 2019 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"time"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/enode"
)
type crawler struct {
input nodeSet
output nodeSet
disc *discover.UDPv4
iters []enode.Iterator
inputIter enode.Iterator
ch chan *enode.Node
closed chan struct{}
// settings
revalidateInterval time.Duration
}
func newCrawler(input nodeSet, disc *discover.UDPv4, iters ...enode.Iterator) *crawler {
c := &crawler{
input: input,
output: make(nodeSet, len(input)),
disc: disc,
iters: iters,
inputIter: enode.IterNodes(input.nodes()),
ch: make(chan *enode.Node),
closed: make(chan struct{}),
}
c.iters = append(c.iters, c.inputIter)
// Copy input to output initially. Any nodes that fail validation
// will be dropped from output during the run.
for id, n := range input {
c.output[id] = n
}
return c
}
func (c *crawler) run(timeout time.Duration) nodeSet {
var (
timeoutTimer = time.NewTimer(timeout)
timeoutCh <-chan time.Time
doneCh = make(chan enode.Iterator, len(c.iters))
liveIters = len(c.iters)
)
for _, it := range c.iters {
go c.runIterator(doneCh, it)
}
loop:
for {
select {
case n := <-c.ch:
c.updateNode(n)
case it := <-doneCh:
if it == c.inputIter {
// Enable timeout when we're done revalidating the input nodes.
log.Info("Revalidation of input set is done", "len", len(c.input))
if timeout > 0 {
timeoutCh = timeoutTimer.C
}
}
if liveIters--; liveIters == 0 {
break loop
}
case <-timeoutCh:
break loop
}
}
close(c.closed)
for _, it := range c.iters {
it.Close()
}
for ; liveIters > 0; liveIters-- {
<-doneCh
}
return c.output
}
func (c *crawler) runIterator(done chan<- enode.Iterator, it enode.Iterator) {
defer func() { done <- it }()
for it.Next() {
select {
case c.ch <- it.Node():
case <-c.closed:
return
}
}
}
func (c *crawler) updateNode(n *enode.Node) {
node, ok := c.output[n.ID()]
// Skip validation of recently-seen nodes.
if ok && time.Since(node.LastCheck) < c.revalidateInterval {
return
}
// Request the node record.
nn, err := c.disc.RequestENR(n)
node.LastCheck = truncNow()
if err != nil {
if node.Score == 0 {
// Node doesn't implement EIP-868.
log.Debug("Skipping node", "id", n.ID())
return
}
node.Score /= 2
} else {
node.N = nn
node.Seq = nn.Seq()
node.Score++
if node.FirstResponse.IsZero() {
node.FirstResponse = node.LastCheck
}
node.LastResponse = node.LastCheck
}
// Store/update node in output set.
if node.Score <= 0 {
log.Info("Removing node", "id", n.ID())
delete(c.output, n.ID())
} else {
log.Info("Updating node", "id", n.ID(), "seq", n.Seq(), "score", node.Score)
c.output[n.ID()] = node
}
}
func truncNow() time.Time {
return time.Now().UTC().Truncate(1 * time.Second)
}

View file

@ -19,10 +19,10 @@ package main
import ( import (
"fmt" "fmt"
"net" "net"
"sort"
"strings" "strings"
"time" "time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p/discover" "github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/p2p/enode"
@ -38,36 +38,59 @@ var (
discv4PingCommand, discv4PingCommand,
discv4RequestRecordCommand, discv4RequestRecordCommand,
discv4ResolveCommand, discv4ResolveCommand,
discv4ResolveJSONCommand,
discv4CrawlCommand,
}, },
} }
discv4PingCommand = cli.Command{ discv4PingCommand = cli.Command{
Name: "ping", Name: "ping",
Usage: "Sends ping to a node", Usage: "Sends ping to a node",
Action: discv4Ping, Action: discv4Ping,
ArgsUsage: "<node>",
} }
discv4RequestRecordCommand = cli.Command{ discv4RequestRecordCommand = cli.Command{
Name: "requestenr", Name: "requestenr",
Usage: "Requests a node record using EIP-868 enrRequest", Usage: "Requests a node record using EIP-868 enrRequest",
Action: discv4RequestRecord, Action: discv4RequestRecord,
ArgsUsage: "<node>",
} }
discv4ResolveCommand = cli.Command{ discv4ResolveCommand = cli.Command{
Name: "resolve", Name: "resolve",
Usage: "Finds a node in the DHT", Usage: "Finds a node in the DHT",
Action: discv4Resolve, Action: discv4Resolve,
Flags: []cli.Flag{bootnodesFlag}, ArgsUsage: "<node>",
Flags: []cli.Flag{bootnodesFlag},
}
discv4ResolveJSONCommand = cli.Command{
Name: "resolve-json",
Usage: "Re-resolves nodes in a nodes.json file",
Action: discv4ResolveJSON,
Flags: []cli.Flag{bootnodesFlag},
ArgsUsage: "<nodes.json file>",
}
discv4CrawlCommand = cli.Command{
Name: "crawl",
Usage: "Updates a nodes.json file with random nodes found in the DHT",
Action: discv4Crawl,
Flags: []cli.Flag{bootnodesFlag, crawlTimeoutFlag},
} }
) )
var bootnodesFlag = cli.StringFlag{ var (
Name: "bootnodes", bootnodesFlag = cli.StringFlag{
Usage: "Comma separated nodes used for bootstrapping", Name: "bootnodes",
} Usage: "Comma separated nodes used for bootstrapping",
}
crawlTimeoutFlag = cli.DurationFlag{
Name: "timeout",
Usage: "Time limit for the crawl.",
Value: 30 * time.Minute,
}
)
func discv4Ping(ctx *cli.Context) error { func discv4Ping(ctx *cli.Context) error {
n, disc, err := getNodeArgAndStartV4(ctx) n := getNodeArg(ctx)
if err != nil { disc := startV4(ctx)
return err
}
defer disc.Close() defer disc.Close()
start := time.Now() start := time.Now()
@ -79,10 +102,8 @@ func discv4Ping(ctx *cli.Context) error {
} }
func discv4RequestRecord(ctx *cli.Context) error { func discv4RequestRecord(ctx *cli.Context) error {
n, disc, err := getNodeArgAndStartV4(ctx) n := getNodeArg(ctx)
if err != nil { disc := startV4(ctx)
return err
}
defer disc.Close() defer disc.Close()
respN, err := disc.RequestENR(n) respN, err := disc.RequestENR(n)
@ -94,33 +115,61 @@ func discv4RequestRecord(ctx *cli.Context) error {
} }
func discv4Resolve(ctx *cli.Context) error { func discv4Resolve(ctx *cli.Context) error {
n, disc, err := getNodeArgAndStartV4(ctx) n := getNodeArg(ctx)
if err != nil { disc := startV4(ctx)
return err
}
defer disc.Close() defer disc.Close()
fmt.Println(disc.Resolve(n).String()) fmt.Println(disc.Resolve(n).String())
return nil return nil
} }
func getNodeArgAndStartV4(ctx *cli.Context) (*enode.Node, *discover.UDPv4, error) { func discv4ResolveJSON(ctx *cli.Context) error {
if ctx.NArg() != 1 { if ctx.NArg() < 1 {
return nil, nil, fmt.Errorf("missing node as command-line argument") return fmt.Errorf("need nodes file as argument")
} }
n, err := parseNode(ctx.Args()[0]) nodesFile := ctx.Args().Get(0)
if err != nil { inputSet := make(nodeSet)
return nil, nil, err if common.FileExist(nodesFile) {
inputSet = loadNodesJSON(nodesFile)
} }
var bootnodes []*enode.Node
if commandHasFlag(ctx, bootnodesFlag) { // Add extra nodes from command line arguments.
bootnodes, err = parseBootnodes(ctx) var nodeargs []*enode.Node
for i := 1; i < ctx.NArg(); i++ {
n, err := parseNode(ctx.Args().Get(i))
if err != nil { if err != nil {
return nil, nil, err exit(err)
} }
nodeargs = append(nodeargs, n)
} }
disc, err := startV4(bootnodes)
return n, disc, err // Run the crawler.
disc := startV4(ctx)
defer disc.Close()
c := newCrawler(inputSet, disc, enode.IterNodes(nodeargs))
c.revalidateInterval = 0
output := c.run(0)
writeNodesJSON(nodesFile, output)
return nil
}
func discv4Crawl(ctx *cli.Context) error {
if ctx.NArg() < 1 {
return fmt.Errorf("need nodes file as argument")
}
nodesFile := ctx.Args().First()
var inputSet nodeSet
if common.FileExist(nodesFile) {
inputSet = loadNodesJSON(nodesFile)
}
disc := startV4(ctx)
defer disc.Close()
c := newCrawler(inputSet, disc, disc.RandomNodes())
c.revalidateInterval = 10 * time.Minute
output := c.run(ctx.Duration(crawlTimeoutFlag.Name))
writeNodesJSON(nodesFile, output)
return nil
} }
func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) { func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) {
@ -139,28 +188,39 @@ func parseBootnodes(ctx *cli.Context) ([]*enode.Node, error) {
return nodes, nil return nodes, nil
} }
// commandHasFlag returns true if the current command supports the given flag. // startV4 starts an ephemeral discovery V4 node.
func commandHasFlag(ctx *cli.Context, flag cli.Flag) bool { func startV4(ctx *cli.Context) *discover.UDPv4 {
flags := ctx.FlagNames() socket, ln, cfg, err := listen()
sort.Strings(flags) if err != nil {
i := sort.SearchStrings(flags, flag.GetName()) exit(err)
return i != len(flags) && flags[i] == flag.GetName() }
if commandHasFlag(ctx, bootnodesFlag) {
bn, err := parseBootnodes(ctx)
if err != nil {
exit(err)
}
cfg.Bootnodes = bn
}
disc, err := discover.ListenV4(socket, ln, cfg)
if err != nil {
exit(err)
}
return disc
} }
// startV4 starts an ephemeral discovery V4 node. func listen() (*net.UDPConn, *enode.LocalNode, discover.Config, error) {
func startV4(bootnodes []*enode.Node) (*discover.UDPv4, error) {
var cfg discover.Config var cfg discover.Config
cfg.Bootnodes = bootnodes
cfg.PrivateKey, _ = crypto.GenerateKey() cfg.PrivateKey, _ = crypto.GenerateKey()
db, _ := enode.OpenDB("") db, _ := enode.OpenDB("")
ln := enode.NewLocalNode(db, cfg.PrivateKey) ln := enode.NewLocalNode(db, cfg.PrivateKey)
socket, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IP{0, 0, 0, 0}}) socket, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IP{0, 0, 0, 0}})
if err != nil { if err != nil {
return nil, err db.Close()
return nil, nil, cfg, err
} }
addr := socket.LocalAddr().(*net.UDPAddr) addr := socket.LocalAddr().(*net.UDPAddr)
ln.SetFallbackIP(net.IP{127, 0, 0, 1}) ln.SetFallbackIP(net.IP{127, 0, 0, 1})
ln.SetFallbackUDP(addr.Port) ln.SetFallbackUDP(addr.Port)
return discover.ListenUDP(socket, ln, cfg) return socket, ln, cfg, nil
} }

View file

@ -0,0 +1,163 @@
// Copyright 2019 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"fmt"
"strings"
"github.com/cloudflare/cloudflare-go"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/dnsdisc"
"gopkg.in/urfave/cli.v1"
)
var (
cloudflareTokenFlag = cli.StringFlag{
Name: "token",
Usage: "CloudFlare API token",
EnvVar: "CLOUDFLARE_API_TOKEN",
}
cloudflareZoneIDFlag = cli.StringFlag{
Name: "zoneid",
Usage: "CloudFlare Zone ID (optional)",
}
)
type cloudflareClient struct {
*cloudflare.API
zoneID string
}
// newCloudflareClient sets up a CloudFlare API client from command line flags.
func newCloudflareClient(ctx *cli.Context) *cloudflareClient {
token := ctx.String(cloudflareTokenFlag.Name)
if token == "" {
exit(fmt.Errorf("need cloudflare API token to proceed"))
}
api, err := cloudflare.NewWithAPIToken(token)
if err != nil {
exit(fmt.Errorf("can't create Cloudflare client: %v", err))
}
return &cloudflareClient{
API: api,
zoneID: ctx.String(cloudflareZoneIDFlag.Name),
}
}
// deploy uploads the given tree to CloudFlare DNS.
func (c *cloudflareClient) deploy(name string, t *dnsdisc.Tree) error {
if err := c.checkZone(name); err != nil {
return err
}
records := t.ToTXT(name)
return c.uploadRecords(name, records)
}
// checkZone verifies permissions on the CloudFlare DNS Zone for name.
func (c *cloudflareClient) checkZone(name string) error {
if c.zoneID == "" {
log.Info(fmt.Sprintf("Finding CloudFlare zone ID for %s", name))
id, err := c.ZoneIDByName(name)
if err != nil {
return err
}
c.zoneID = id
}
log.Info(fmt.Sprintf("Checking Permissions on zone %s", c.zoneID))
zone, err := c.ZoneDetails(c.zoneID)
if err != nil {
return err
}
if !strings.HasSuffix(name, "."+zone.Name) {
return fmt.Errorf("CloudFlare zone name %q does not match name %q to be deployed", zone.Name, name)
}
needPerms := map[string]bool{"#zone:edit": false, "#zone:read": false}
for _, perm := range zone.Permissions {
if _, ok := needPerms[perm]; ok {
needPerms[perm] = true
}
}
for _, ok := range needPerms {
if !ok {
return fmt.Errorf("wrong permissions on zone %s: %v", c.zoneID, needPerms)
}
}
return nil
}
// uploadRecords updates the TXT records at a particular subdomain. All non-root records
// will have a TTL of "infinity" and all existing records not in the new map will be
// nuked!
func (c *cloudflareClient) uploadRecords(name string, records map[string]string) error {
// Convert all names to lowercase.
lrecords := make(map[string]string, len(records))
for name, r := range records {
lrecords[strings.ToLower(name)] = r
}
records = lrecords
log.Info(fmt.Sprintf("Retrieving existing TXT records on %s", name))
entries, err := c.DNSRecords(c.zoneID, cloudflare.DNSRecord{Type: "TXT"})
if err != nil {
return err
}
existing := make(map[string]cloudflare.DNSRecord)
for _, entry := range entries {
if !strings.HasSuffix(entry.Name, name) {
continue
}
existing[strings.ToLower(entry.Name)] = entry
}
// Iterate over the new records and inject anything missing.
for path, val := range records {
old, exists := existing[path]
if !exists {
// Entry is unknown, push a new one to Cloudflare.
log.Info(fmt.Sprintf("Creating %s = %q", path, val))
ttl := 1
if path != name {
ttl = 2147483647 // Max TTL permitted by Cloudflare
}
_, err = c.CreateDNSRecord(c.zoneID, cloudflare.DNSRecord{Type: "TXT", Name: path, Content: val, TTL: ttl})
} else if old.Content != val {
// Entry already exists, only change its content.
log.Info(fmt.Sprintf("Updating %s from %q to %q", path, old.Content, val))
old.Content = val
err = c.UpdateDNSRecord(c.zoneID, old.ID, old)
} else {
log.Info(fmt.Sprintf("Skipping %s = %q", path, val))
}
if err != nil {
return fmt.Errorf("failed to publish %s: %v", path, err)
}
}
// Iterate over the old records and delete anything stale.
for path, entry := range existing {
if _, ok := records[path]; ok {
continue
}
// Stale entry, nuke it.
log.Info(fmt.Sprintf("Deleting %s = %q", path, entry.Content))
if err := c.DeleteDNSRecord(c.zoneID, entry.ID); err != nil {
return fmt.Errorf("failed to delete %s: %v", path, err)
}
}
return nil
}

361
cmd/devp2p/dnscmd.go Normal file
View file

@ -0,0 +1,361 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"crypto/ecdsa"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"time"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/console"
"github.com/ethereum/go-ethereum/p2p/dnsdisc"
"github.com/ethereum/go-ethereum/p2p/enode"
cli "gopkg.in/urfave/cli.v1"
)
var (
dnsCommand = cli.Command{
Name: "dns",
Usage: "DNS Discovery Commands",
Subcommands: []cli.Command{
dnsSyncCommand,
dnsSignCommand,
dnsTXTCommand,
dnsCloudflareCommand,
},
}
dnsSyncCommand = cli.Command{
Name: "sync",
Usage: "Download a DNS discovery tree",
ArgsUsage: "<url> [ <directory> ]",
Action: dnsSync,
Flags: []cli.Flag{dnsTimeoutFlag},
}
dnsSignCommand = cli.Command{
Name: "sign",
Usage: "Sign a DNS discovery tree",
ArgsUsage: "<tree-directory> <key-file>",
Action: dnsSign,
Flags: []cli.Flag{dnsDomainFlag, dnsSeqFlag},
}
dnsTXTCommand = cli.Command{
Name: "to-txt",
Usage: "Create a DNS TXT records for a discovery tree",
ArgsUsage: "<tree-directory> <output-file>",
Action: dnsToTXT,
}
dnsCloudflareCommand = cli.Command{
Name: "to-cloudflare",
Usage: "Deploy DNS TXT records to cloudflare",
ArgsUsage: "<tree-directory>",
Action: dnsToCloudflare,
Flags: []cli.Flag{cloudflareTokenFlag, cloudflareZoneIDFlag},
}
)
var (
dnsTimeoutFlag = cli.DurationFlag{
Name: "timeout",
Usage: "Timeout for DNS lookups",
}
dnsDomainFlag = cli.StringFlag{
Name: "domain",
Usage: "Domain name of the tree",
}
dnsSeqFlag = cli.UintFlag{
Name: "seq",
Usage: "New sequence number of the tree",
}
)
// dnsSync performs dnsSyncCommand.
func dnsSync(ctx *cli.Context) error {
var (
c = dnsClient(ctx)
url = ctx.Args().Get(0)
outdir = ctx.Args().Get(1)
)
domain, _, err := dnsdisc.ParseURL(url)
if err != nil {
return err
}
if outdir == "" {
outdir = domain
}
t, err := c.SyncTree(url)
if err != nil {
return err
}
def := treeToDefinition(url, t)
def.Meta.LastModified = time.Now()
writeTreeMetadata(outdir, def)
writeTreeNodes(outdir, def)
return nil
}
func dnsSign(ctx *cli.Context) error {
if ctx.NArg() < 2 {
return fmt.Errorf("need tree definition directory and key file as arguments")
}
var (
defdir = ctx.Args().Get(0)
keyfile = ctx.Args().Get(1)
def = loadTreeDefinition(defdir)
domain = directoryName(defdir)
)
if def.Meta.URL != "" {
d, _, err := dnsdisc.ParseURL(def.Meta.URL)
if err != nil {
return fmt.Errorf("invalid 'url' field: %v", err)
}
domain = d
}
if ctx.IsSet(dnsDomainFlag.Name) {
domain = ctx.String(dnsDomainFlag.Name)
}
if ctx.IsSet(dnsSeqFlag.Name) {
def.Meta.Seq = ctx.Uint(dnsSeqFlag.Name)
} else {
def.Meta.Seq++ // Auto-bump sequence number if not supplied via flag.
}
t, err := dnsdisc.MakeTree(def.Meta.Seq, def.Nodes, def.Meta.Links)
if err != nil {
return err
}
key := loadSigningKey(keyfile)
url, err := t.Sign(key, domain)
if err != nil {
return fmt.Errorf("can't sign: %v", err)
}
def = treeToDefinition(url, t)
def.Meta.LastModified = time.Now()
writeTreeMetadata(defdir, def)
return nil
}
func directoryName(dir string) string {
abs, err := filepath.Abs(dir)
if err != nil {
exit(err)
}
return filepath.Base(abs)
}
// dnsToTXT peforms dnsTXTCommand.
func dnsToTXT(ctx *cli.Context) error {
if ctx.NArg() < 1 {
return fmt.Errorf("need tree definition directory as argument")
}
output := ctx.Args().Get(1)
if output == "" {
output = "-" // default to stdout
}
domain, t, err := loadTreeDefinitionForExport(ctx.Args().Get(0))
if err != nil {
return err
}
writeTXTJSON(output, t.ToTXT(domain))
return nil
}
// dnsToCloudflare peforms dnsCloudflareCommand.
func dnsToCloudflare(ctx *cli.Context) error {
if ctx.NArg() < 1 {
return fmt.Errorf("need tree definition directory as argument")
}
domain, t, err := loadTreeDefinitionForExport(ctx.Args().Get(0))
if err != nil {
return err
}
client := newCloudflareClient(ctx)
return client.deploy(domain, t)
}
// loadSigningKey loads a private key in Ethereum keystore format.
func loadSigningKey(keyfile string) *ecdsa.PrivateKey {
keyjson, err := ioutil.ReadFile(keyfile)
if err != nil {
exit(fmt.Errorf("failed to read the keyfile at '%s': %v", keyfile, err))
}
password, _ := console.Stdin.PromptPassword("Please enter the password for '" + keyfile + "': ")
key, err := keystore.DecryptKey(keyjson, password)
if err != nil {
exit(fmt.Errorf("error decrypting key: %v", err))
}
return key.PrivateKey
}
// dnsClient configures the DNS discovery client from command line flags.
func dnsClient(ctx *cli.Context) *dnsdisc.Client {
var cfg dnsdisc.Config
if commandHasFlag(ctx, dnsTimeoutFlag) {
cfg.Timeout = ctx.Duration(dnsTimeoutFlag.Name)
}
c, _ := dnsdisc.NewClient(cfg) // cannot fail because no URLs given
return c
}
// There are two file formats for DNS node trees on disk:
//
// The 'TXT' format is a single JSON file containing DNS TXT records
// as a JSON object where the keys are names and the values are objects
// containing the value of the record.
//
// The 'definition' format is a directory containing two files:
//
// enrtree-info.json -- contains sequence number & links to other trees
// nodes.json -- contains the nodes as a JSON array.
//
// This format exists because it's convenient to edit. nodes.json can be generated
// in multiple ways: it may be written by a DHT crawler or compiled by a human.
type dnsDefinition struct {
Meta dnsMetaJSON
Nodes []*enode.Node
}
type dnsMetaJSON struct {
URL string `json:"url,omitempty"`
Seq uint `json:"seq"`
Sig string `json:"signature,omitempty"`
Links []string `json:"links"`
LastModified time.Time `json:"lastModified"`
}
func treeToDefinition(url string, t *dnsdisc.Tree) *dnsDefinition {
meta := dnsMetaJSON{
URL: url,
Seq: t.Seq(),
Sig: t.Signature(),
Links: t.Links(),
}
if meta.Links == nil {
meta.Links = []string{}
}
return &dnsDefinition{Meta: meta, Nodes: t.Nodes()}
}
// loadTreeDefinition loads a directory in 'definition' format.
func loadTreeDefinition(directory string) *dnsDefinition {
metaFile, nodesFile := treeDefinitionFiles(directory)
var def dnsDefinition
err := common.LoadJSON(metaFile, &def.Meta)
if err != nil && !os.IsNotExist(err) {
exit(err)
}
if def.Meta.Links == nil {
def.Meta.Links = []string{}
}
// Check link syntax.
for _, link := range def.Meta.Links {
if _, _, err := dnsdisc.ParseURL(link); err != nil {
exit(fmt.Errorf("invalid link %q: %v", link, err))
}
}
// Check/convert nodes.
nodes := loadNodesJSON(nodesFile)
if err := nodes.verify(); err != nil {
exit(err)
}
def.Nodes = nodes.nodes()
return &def
}
// loadTreeDefinitionForExport loads a DNS tree and ensures it is signed.
func loadTreeDefinitionForExport(dir string) (domain string, t *dnsdisc.Tree, err error) {
metaFile, _ := treeDefinitionFiles(dir)
def := loadTreeDefinition(dir)
if def.Meta.URL == "" {
return "", nil, fmt.Errorf("missing 'url' field in %v", metaFile)
}
domain, pubkey, err := dnsdisc.ParseURL(def.Meta.URL)
if err != nil {
return "", nil, fmt.Errorf("invalid 'url' field in %v: %v", metaFile, err)
}
if t, err = dnsdisc.MakeTree(def.Meta.Seq, def.Nodes, def.Meta.Links); err != nil {
return "", nil, err
}
if err := ensureValidTreeSignature(t, pubkey, def.Meta.Sig); err != nil {
return "", nil, err
}
return domain, t, nil
}
// ensureValidTreeSignature checks that sig is valid for tree and assigns it as the
// tree's signature if valid.
func ensureValidTreeSignature(t *dnsdisc.Tree, pubkey *ecdsa.PublicKey, sig string) error {
if sig == "" {
return fmt.Errorf("missing signature, run 'devp2p dns sign' first")
}
if err := t.SetSignature(pubkey, sig); err != nil {
return fmt.Errorf("invalid signature on tree, run 'devp2p dns sign' to update it")
}
return nil
}
// writeTreeMetadata writes a DNS node tree metadata file to the given directory.
func writeTreeMetadata(directory string, def *dnsDefinition) {
metaJSON, err := json.MarshalIndent(&def.Meta, "", jsonIndent)
if err != nil {
exit(err)
}
if err := os.Mkdir(directory, 0744); err != nil && !os.IsExist(err) {
exit(err)
}
metaFile, _ := treeDefinitionFiles(directory)
if err := ioutil.WriteFile(metaFile, metaJSON, 0644); err != nil {
exit(err)
}
}
func writeTreeNodes(directory string, def *dnsDefinition) {
ns := make(nodeSet, len(def.Nodes))
ns.add(def.Nodes...)
_, nodesFile := treeDefinitionFiles(directory)
writeNodesJSON(nodesFile, ns)
}
func treeDefinitionFiles(directory string) (string, string) {
meta := filepath.Join(directory, "enrtree-info.json")
nodes := filepath.Join(directory, "nodes.json")
return meta, nodes
}
// writeTXTJSON writes TXT records in JSON format.
func writeTXTJSON(file string, txt map[string]string) {
txtJSON, err := json.MarshalIndent(txt, "", jsonIndent)
if err != nil {
exit(err)
}
if file == "-" {
os.Stdout.Write(txtJSON)
fmt.Println()
return
}
if err := ioutil.WriteFile(file, txtJSON, 0644); err != nil {
exit(err)
}
}

View file

@ -20,8 +20,10 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"sort"
"github.com/ethereum/go-ethereum/internal/debug" "github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"gopkg.in/urfave/cli.v1" "gopkg.in/urfave/cli.v1"
) )
@ -57,12 +59,39 @@ func init() {
app.Commands = []cli.Command{ app.Commands = []cli.Command{
enrdumpCommand, enrdumpCommand,
discv4Command, discv4Command,
dnsCommand,
nodesetCommand,
} }
} }
func main() { func main() {
if err := app.Run(os.Args); err != nil { exit(app.Run(os.Args))
fmt.Fprintln(os.Stderr, err) }
os.Exit(1)
} // commandHasFlag returns true if the current command supports the given flag.
func commandHasFlag(ctx *cli.Context, flag cli.Flag) bool {
flags := ctx.FlagNames()
sort.Strings(flags)
i := sort.SearchStrings(flags, flag.GetName())
return i != len(flags) && flags[i] == flag.GetName()
}
// getNodeArg handles the common case of a single node descriptor argument.
func getNodeArg(ctx *cli.Context) *enode.Node {
if ctx.NArg() != 1 {
exit("missing node as command-line argument")
}
n, err := parseNode(ctx.Args()[0])
if err != nil {
exit(err)
}
return n
}
func exit(err interface{}) {
if err == nil {
os.Exit(0)
}
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
} }

102
cmd/devp2p/nodeset.go Normal file
View file

@ -0,0 +1,102 @@
// Copyright 2019 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"sort"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/p2p/enode"
)
const jsonIndent = " "
// nodeSet is the nodes.json file format. It holds a set of node records
// as a JSON object.
type nodeSet map[enode.ID]nodeJSON
type nodeJSON struct {
Seq uint64 `json:"seq"`
N *enode.Node `json:"record"`
// The score tracks how many liveness checks were performed. It is incremented by one
// every time the node passes a check, and halved every time it doesn't.
Score int `json:"score,omitempty"`
// These two track the time of last successful contact.
FirstResponse time.Time `json:"firstResponse,omitempty"`
LastResponse time.Time `json:"lastResponse,omitempty"`
// This one tracks the time of our last attempt to contact the node.
LastCheck time.Time `json:"lastCheck,omitempty"`
}
func loadNodesJSON(file string) nodeSet {
var nodes nodeSet
if err := common.LoadJSON(file, &nodes); err != nil {
exit(err)
}
return nodes
}
func writeNodesJSON(file string, nodes nodeSet) {
nodesJSON, err := json.MarshalIndent(nodes, "", jsonIndent)
if err != nil {
exit(err)
}
if file == "-" {
os.Stdout.Write(nodesJSON)
return
}
if err := ioutil.WriteFile(file, nodesJSON, 0644); err != nil {
exit(err)
}
}
func (ns nodeSet) nodes() []*enode.Node {
result := make([]*enode.Node, 0, len(ns))
for _, n := range ns {
result = append(result, n.N)
}
// Sort by ID.
sort.Slice(result, func(i, j int) bool {
return bytes.Compare(result[i].ID().Bytes(), result[j].ID().Bytes()) < 0
})
return result
}
func (ns nodeSet) add(nodes ...*enode.Node) {
for _, n := range nodes {
ns[n.ID()] = nodeJSON{Seq: n.Seq(), N: n}
}
}
func (ns nodeSet) verify() error {
for id, n := range ns {
if n.N.ID() != id {
return fmt.Errorf("invalid node %v: ID does not match ID %v in record", id, n.N.ID())
}
if n.N.Seq() != n.Seq {
return fmt.Errorf("invalid node %v: 'seq' does not match seq %d from record", id, n.N.Seq())
}
}
return nil
}

193
cmd/devp2p/nodesetcmd.go Normal file
View file

@ -0,0 +1,193 @@
// Copyright 2019 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"fmt"
"net"
"time"
"github.com/ethereum/go-ethereum/core/forkid"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"gopkg.in/urfave/cli.v1"
)
var (
nodesetCommand = cli.Command{
Name: "nodeset",
Usage: "Node set tools",
Subcommands: []cli.Command{
nodesetInfoCommand,
nodesetFilterCommand,
},
}
nodesetInfoCommand = cli.Command{
Name: "info",
Usage: "Shows statistics about a node set",
Action: nodesetInfo,
ArgsUsage: "<nodes.json>",
}
nodesetFilterCommand = cli.Command{
Name: "filter",
Usage: "Filters a node set",
Action: nodesetFilter,
ArgsUsage: "<nodes.json> filters..",
SkipFlagParsing: true,
}
)
func nodesetInfo(ctx *cli.Context) error {
if ctx.NArg() < 1 {
return fmt.Errorf("need nodes file as argument")
}
ns := loadNodesJSON(ctx.Args().First())
fmt.Printf("Set contains %d nodes.\n", len(ns))
return nil
}
func nodesetFilter(ctx *cli.Context) error {
if ctx.NArg() < 1 {
return fmt.Errorf("need nodes file as argument")
}
ns := loadNodesJSON(ctx.Args().First())
filter, err := andFilter(ctx.Args().Tail())
if err != nil {
return err
}
result := make(nodeSet)
for id, n := range ns {
if filter(n) {
result[id] = n
}
}
writeNodesJSON("-", result)
return nil
}
type nodeFilter func(nodeJSON) bool
type nodeFilterC struct {
narg int
fn func([]string) (nodeFilter, error)
}
var filterFlags = map[string]nodeFilterC{
"-ip": {1, ipFilter},
"-min-age": {1, minAgeFilter},
"-eth-network": {1, ethFilter},
"-les-server": {0, lesFilter},
}
func parseFilters(args []string) ([]nodeFilter, error) {
var filters []nodeFilter
for len(args) > 0 {
fc, ok := filterFlags[args[0]]
if !ok {
return nil, fmt.Errorf("invalid filter %q", args[0])
}
if len(args) < fc.narg {
return nil, fmt.Errorf("filter %q wants %d arguments, have %d", args[0], fc.narg, len(args))
}
filter, err := fc.fn(args[1:])
if err != nil {
return nil, fmt.Errorf("%s: %v", args[0], err)
}
filters = append(filters, filter)
args = args[fc.narg+1:]
}
return filters, nil
}
func andFilter(args []string) (nodeFilter, error) {
checks, err := parseFilters(args)
if err != nil {
return nil, err
}
f := func(n nodeJSON) bool {
for _, filter := range checks {
if !filter(n) {
return false
}
}
return true
}
return f, nil
}
func ipFilter(args []string) (nodeFilter, error) {
_, cidr, err := net.ParseCIDR(args[0])
if err != nil {
return nil, err
}
f := func(n nodeJSON) bool { return cidr.Contains(n.N.IP()) }
return f, nil
}
func minAgeFilter(args []string) (nodeFilter, error) {
minage, err := time.ParseDuration(args[0])
if err != nil {
return nil, err
}
f := func(n nodeJSON) bool {
age := n.LastResponse.Sub(n.FirstResponse)
return age >= minage
}
return f, nil
}
func ethFilter(args []string) (nodeFilter, error) {
var filter forkid.Filter
switch args[0] {
case "mainnet":
filter = forkid.NewStaticFilter(params.MainnetChainConfig, params.MainnetGenesisHash)
case "rinkeby":
filter = forkid.NewStaticFilter(params.RinkebyChainConfig, params.RinkebyGenesisHash)
case "goerli":
filter = forkid.NewStaticFilter(params.GoerliChainConfig, params.GoerliGenesisHash)
case "ropsten":
filter = forkid.NewStaticFilter(params.TestnetChainConfig, params.TestnetGenesisHash)
default:
return nil, fmt.Errorf("unknown network %q", args[0])
}
f := func(n nodeJSON) bool {
var eth struct {
ForkID forkid.ID
_ []rlp.RawValue `rlp:"tail"`
}
if n.N.Load(enr.WithEntry("eth", &eth)) != nil {
return false
}
return filter(eth.ForkID) == nil
}
return f, nil
}
func lesFilter(args []string) (nodeFilter, error) {
f := func(n nodeJSON) bool {
var les struct {
_ []rlp.RawValue `rlp:"tail"`
}
return n.N.Load(enr.WithEntry("les", &les)) == nil
}
return f, nil
}

View file

@ -35,18 +35,18 @@ It is possible to refer to a file containing the message.
To sign a message contained in a file, use the --msgfile flag. To sign a message contained in a file, use the --msgfile flag.
### `ethkey changepassphrase <keyfile>` ### `ethkey changepassword <keyfile>`
Change the passphrase of a keyfile. Change the password of a keyfile.
use the `--newpasswordfile` to point to the new password file. use the `--newpasswordfile` to point to the new password file.
## Passphrases ## Passwords
For every command that uses a keyfile, you will be prompted to provide the For every command that uses a keyfile, you will be prompted to provide the
passphrase for decrypting the keyfile. To avoid this message, it is possible password for decrypting the keyfile. To avoid this message, it is possible
to pass the passphrase by using the `--passwordfile` flag pointing to a file that to pass the password by using the `--passwordfile` flag pointing to a file that
contains the passphrase. contains the password.
## JSON ## JSON

View file

@ -1,3 +1,19 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main package main
import ( import (
@ -12,15 +28,15 @@ import (
var newPassphraseFlag = cli.StringFlag{ var newPassphraseFlag = cli.StringFlag{
Name: "newpasswordfile", Name: "newpasswordfile",
Usage: "the file that contains the new passphrase for the keyfile", Usage: "the file that contains the new password for the keyfile",
} }
var commandChangePassphrase = cli.Command{ var commandChangePassphrase = cli.Command{
Name: "changepassphrase", Name: "changepassword",
Usage: "change the passphrase on a keyfile", Usage: "change the password on a keyfile",
ArgsUsage: "<keyfile>", ArgsUsage: "<keyfile>",
Description: ` Description: `
Change the passphrase of a keyfile.`, Change the password of a keyfile.`,
Flags: []cli.Flag{ Flags: []cli.Flag{
passphraseFlag, passphraseFlag,
newPassphraseFlag, newPassphraseFlag,
@ -42,12 +58,12 @@ Change the passphrase of a keyfile.`,
} }
// Get a new passphrase. // Get a new passphrase.
fmt.Println("Please provide a new passphrase") fmt.Println("Please provide a new password")
var newPhrase string var newPhrase string
if passFile := ctx.String(newPassphraseFlag.Name); passFile != "" { if passFile := ctx.String(newPassphraseFlag.Name); passFile != "" {
content, err := ioutil.ReadFile(passFile) content, err := ioutil.ReadFile(passFile)
if err != nil { if err != nil {
utils.Fatalf("Failed to read new passphrase file '%s': %v", passFile, err) utils.Fatalf("Failed to read new password file '%s': %v", passFile, err)
} }
newPhrase = strings.TrimRight(string(content), "\r\n") newPhrase = strings.TrimRight(string(content), "\r\n")
} else { } else {
@ -57,7 +73,7 @@ Change the passphrase of a keyfile.`,
// Encrypt the key with the new passphrase. // Encrypt the key with the new passphrase.
newJson, err := keystore.EncryptKey(key, newPhrase, keystore.StandardScryptN, keystore.StandardScryptP) newJson, err := keystore.EncryptKey(key, newPhrase, keystore.StandardScryptN, keystore.StandardScryptP)
if err != nil { if err != nil {
utils.Fatalf("Error encrypting with new passphrase: %v", err) utils.Fatalf("Error encrypting with new password: %v", err)
} }
// Then write the new keyfile in place of the old one. // Then write the new keyfile in place of the old one.

View file

@ -49,7 +49,7 @@ func init() {
var ( var (
passphraseFlag = cli.StringFlag{ passphraseFlag = cli.StringFlag{
Name: "passwordfile", Name: "passwordfile",
Usage: "the file that contains the passphrase for the keyfile", Usage: "the file that contains the password for the keyfile",
} }
jsonFlag = cli.BoolFlag{ jsonFlag = cli.BoolFlag{
Name: "json", Name: "json",

View file

@ -37,8 +37,8 @@ func TestMessageSignVerify(t *testing.T) {
generate := runEthkey(t, "generate", keyfile) generate := runEthkey(t, "generate", keyfile)
generate.Expect(` generate.Expect(`
!! Unsupported terminal, password will be echoed. !! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "foobar"}} Password: {{.InputLine "foobar"}}
Repeat passphrase: {{.InputLine "foobar"}} Repeat password: {{.InputLine "foobar"}}
`) `)
_, matches := generate.ExpectRegexp(`Address: (0x[0-9a-fA-F]{40})\n`) _, matches := generate.ExpectRegexp(`Address: (0x[0-9a-fA-F]{40})\n`)
address := matches[1] address := matches[1]
@ -48,7 +48,7 @@ Repeat passphrase: {{.InputLine "foobar"}}
sign := runEthkey(t, "signmessage", keyfile, message) sign := runEthkey(t, "signmessage", keyfile, message)
sign.Expect(` sign.Expect(`
!! Unsupported terminal, password will be echoed. !! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "foobar"}} Password: {{.InputLine "foobar"}}
`) `)
_, matches = sign.ExpectRegexp(`Signature: ([0-9a-f]+)\n`) _, matches = sign.ExpectRegexp(`Signature: ([0-9a-f]+)\n`)
signature := matches[1] signature := matches[1]

View file

@ -31,18 +31,18 @@ import (
// promptPassphrase prompts the user for a passphrase. Set confirmation to true // promptPassphrase prompts the user for a passphrase. Set confirmation to true
// to require the user to confirm the passphrase. // to require the user to confirm the passphrase.
func promptPassphrase(confirmation bool) string { func promptPassphrase(confirmation bool) string {
passphrase, err := console.Stdin.PromptPassword("Passphrase: ") passphrase, err := console.Stdin.PromptPassword("Password: ")
if err != nil { if err != nil {
utils.Fatalf("Failed to read passphrase: %v", err) utils.Fatalf("Failed to read password: %v", err)
} }
if confirmation { if confirmation {
confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ") confirm, err := console.Stdin.PromptPassword("Repeat password: ")
if err != nil { if err != nil {
utils.Fatalf("Failed to read passphrase confirmation: %v", err) utils.Fatalf("Failed to read password confirmation: %v", err)
} }
if passphrase != confirm { if passphrase != confirm {
utils.Fatalf("Passphrases do not match") utils.Fatalf("Passwords do not match")
} }
} }
@ -58,7 +58,7 @@ func getPassphrase(ctx *cli.Context) string {
if passphraseFile != "" { if passphraseFile != "" {
content, err := ioutil.ReadFile(passphraseFile) content, err := ioutil.ReadFile(passphraseFile)
if err != nil { if err != nil {
utils.Fatalf("Failed to read passphrase file '%s': %v", utils.Fatalf("Failed to read password file '%s': %v",
passphraseFile, err) passphraseFile, err)
} }
return strings.TrimRight(string(content), "\r\n") return strings.TrimRight(string(content), "\r\n")

View file

@ -17,6 +17,7 @@
package main package main
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -145,6 +146,7 @@ func runCmd(ctx *cli.Context) error {
} else { } else {
hexcode = []byte(codeFlag) hexcode = []byte(codeFlag)
} }
hexcode = bytes.TrimSpace(hexcode)
if len(hexcode)%2 != 0 { if len(hexcode)%2 != 0 {
fmt.Printf("Invalid input length for hex data (%d)\n", len(hexcode)) fmt.Printf("Invalid input length for hex data (%d)\n", len(hexcode))
os.Exit(1) os.Exit(1)
@ -198,6 +200,8 @@ func runCmd(ctx *cli.Context) error {
if chainConfig != nil { if chainConfig != nil {
runtimeConfig.ChainConfig = chainConfig runtimeConfig.ChainConfig = chainConfig
} else {
runtimeConfig.ChainConfig = params.AllEthashProtocolChanges
} }
tstart := time.Now() tstart := time.Now()
var leftOverGas uint64 var leftOverGas uint64

View file

@ -506,7 +506,10 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
Time: time.Now(), Time: time.Now(),
Tx: signed, Tx: signed,
}) })
f.timeouts[username] = time.Now().Add(time.Duration(*minutesFlag*int(math.Pow(3, float64(msg.Tier)))) * time.Minute) timeout := time.Duration(*minutesFlag*int(math.Pow(3, float64(msg.Tier)))) * time.Minute
grace := timeout / 288 // 24h timeout => 5m grace
f.timeouts[username] = time.Now().Add(timeout - grace)
fund = true fund = true
} }
f.lock.Unlock() f.lock.Unlock()

View file

@ -116,11 +116,11 @@ Print a short summary of all accounts`,
Creates a new account and prints the address. Creates a new account and prints the address.
The account is saved in encrypted format, you are prompted for a passphrase. The account is saved in encrypted format, you are prompted for a password.
You must remember this passphrase to unlock your account in the future. You must remember this password to unlock your account in the future.
For non-interactive use the passphrase can be specified with the --password flag: For non-interactive use the password can be specified with the --password flag:
Note, this is meant to be used for testing only, it is a bad idea to save your Note, this is meant to be used for testing only, it is a bad idea to save your
password to file or expose in any other way. password to file or expose in any other way.
@ -142,12 +142,12 @@ password to file or expose in any other way.
Update an existing account. Update an existing account.
The account is saved in the newest version in encrypted format, you are prompted The account is saved in the newest version in encrypted format, you are prompted
for a passphrase to unlock the account and another to save the updated file. for a password to unlock the account and another to save the updated file.
This same command can therefore be used to migrate an account of a deprecated This same command can therefore be used to migrate an account of a deprecated
format to the newest format or change the password for an account. format to the newest format or change the password for an account.
For non-interactive use the passphrase can be specified with the --password flag: For non-interactive use the password can be specified with the --password flag:
geth account update [options] <address> geth account update [options] <address>
@ -174,11 +174,11 @@ Prints the address.
The keyfile is assumed to contain an unencrypted private key in hexadecimal format. The keyfile is assumed to contain an unencrypted private key in hexadecimal format.
The account is saved in encrypted format, you are prompted for a passphrase. The account is saved in encrypted format, you are prompted for a password.
You must remember this passphrase to unlock your account in the future. You must remember this password to unlock your account in the future.
For non-interactive use the passphrase can be specified with the -password flag: For non-interactive use the password can be specified with the -password flag:
geth account import [options] <keyfile> geth account import [options] <keyfile>
@ -247,17 +247,17 @@ func getPassPhrase(prompt string, confirmation bool, i int, passwords []string)
if prompt != "" { if prompt != "" {
fmt.Println(prompt) fmt.Println(prompt)
} }
password, err := console.Stdin.PromptPassword("Passphrase: ") password, err := console.Stdin.PromptPassword("Password: ")
if err != nil { if err != nil {
utils.Fatalf("Failed to read passphrase: %v", err) utils.Fatalf("Failed to read password: %v", err)
} }
if confirmation { if confirmation {
confirm, err := console.Stdin.PromptPassword("Repeat passphrase: ") confirm, err := console.Stdin.PromptPassword("Repeat password: ")
if err != nil { if err != nil {
utils.Fatalf("Failed to read passphrase confirmation: %v", err) utils.Fatalf("Failed to read password confirmation: %v", err)
} }
if password != confirm { if password != confirm {
utils.Fatalf("Passphrases do not match") utils.Fatalf("Passwords do not match")
} }
} }
return password return password
@ -268,7 +268,7 @@ func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrErr
for _, a := range err.Matches { for _, a := range err.Matches {
fmt.Println(" ", a.URL) fmt.Println(" ", a.URL)
} }
fmt.Println("Testing your passphrase against all of them...") fmt.Println("Testing your password against all of them...")
var match *accounts.Account var match *accounts.Account
for _, a := range err.Matches { for _, a := range err.Matches {
if err := ks.Unlock(a, auth); err == nil { if err := ks.Unlock(a, auth); err == nil {
@ -279,7 +279,7 @@ func ambiguousAddrRecovery(ks *keystore.KeyStore, err *keystore.AmbiguousAddrErr
if match == nil { if match == nil {
utils.Fatalf("None of the listed files could be unlocked.") utils.Fatalf("None of the listed files could be unlocked.")
} }
fmt.Printf("Your passphrase unlocked %s\n", match.URL) fmt.Printf("Your password unlocked %s\n", match.URL)
fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:") fmt.Println("In order to avoid this warning, you need to remove the following duplicate key files:")
for _, a := range err.Matches { for _, a := range err.Matches {
if a != *match { if a != *match {

View file

@ -72,8 +72,8 @@ func TestAccountNew(t *testing.T) {
geth.Expect(` geth.Expect(`
Your new account is locked with a password. Please give a password. Do not forget this password. Your new account is locked with a password. Please give a password. Do not forget this password.
!! Unsupported terminal, password will be echoed. !! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "foobar"}} Password: {{.InputLine "foobar"}}
Repeat passphrase: {{.InputLine "foobar"}} Repeat password: {{.InputLine "foobar"}}
Your new key was generated Your new key was generated
`) `)
@ -94,9 +94,9 @@ func TestAccountNewBadRepeat(t *testing.T) {
geth.Expect(` geth.Expect(`
Your new account is locked with a password. Please give a password. Do not forget this password. Your new account is locked with a password. Please give a password. Do not forget this password.
!! Unsupported terminal, password will be echoed. !! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "something"}} Password: {{.InputLine "something"}}
Repeat passphrase: {{.InputLine "something else"}} Repeat password: {{.InputLine "something else"}}
Fatal: Passphrases do not match Fatal: Passwords do not match
`) `)
} }
@ -109,10 +109,10 @@ func TestAccountUpdate(t *testing.T) {
geth.Expect(` geth.Expect(`
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
!! Unsupported terminal, password will be echoed. !! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "foobar"}} Password: {{.InputLine "foobar"}}
Please give a new password. Do not forget this password. Please give a new password. Do not forget this password.
Passphrase: {{.InputLine "foobar2"}} Password: {{.InputLine "foobar2"}}
Repeat passphrase: {{.InputLine "foobar2"}} Repeat password: {{.InputLine "foobar2"}}
`) `)
} }
@ -121,7 +121,7 @@ func TestWalletImport(t *testing.T) {
defer geth.ExpectExit() defer geth.ExpectExit()
geth.Expect(` geth.Expect(`
!! Unsupported terminal, password will be echoed. !! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "foo"}} Password: {{.InputLine "foo"}}
Address: {d4584b5f6229b7be90727b0fc8c6b91bb427821f} Address: {d4584b5f6229b7be90727b0fc8c6b91bb427821f}
`) `)
@ -136,8 +136,8 @@ func TestWalletImportBadPassword(t *testing.T) {
defer geth.ExpectExit() defer geth.ExpectExit()
geth.Expect(` geth.Expect(`
!! Unsupported terminal, password will be echoed. !! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "wrong"}} Password: {{.InputLine "wrong"}}
Fatal: could not decrypt key with given passphrase Fatal: could not decrypt key with given password
`) `)
} }
@ -150,7 +150,7 @@ func TestUnlockFlag(t *testing.T) {
geth.Expect(` geth.Expect(`
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
!! Unsupported terminal, password will be echoed. !! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "foobar"}} Password: {{.InputLine "foobar"}}
`) `)
geth.ExpectExit() geth.ExpectExit()
@ -174,12 +174,12 @@ func TestUnlockFlagWrongPassword(t *testing.T) {
geth.Expect(` geth.Expect(`
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
!! Unsupported terminal, password will be echoed. !! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "wrong1"}} Password: {{.InputLine "wrong1"}}
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 2/3 Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 2/3
Passphrase: {{.InputLine "wrong2"}} Password: {{.InputLine "wrong2"}}
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 3/3 Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 3/3
Passphrase: {{.InputLine "wrong3"}} Password: {{.InputLine "wrong3"}}
Fatal: Failed to unlock account f466859ead1932d743d622cb74fc058882e8648a (could not decrypt key with given passphrase) Fatal: Failed to unlock account f466859ead1932d743d622cb74fc058882e8648a (could not decrypt key with given password)
`) `)
} }
@ -193,9 +193,9 @@ func TestUnlockFlagMultiIndex(t *testing.T) {
geth.Expect(` geth.Expect(`
Unlocking account 0 | Attempt 1/3 Unlocking account 0 | Attempt 1/3
!! Unsupported terminal, password will be echoed. !! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "foobar"}} Password: {{.InputLine "foobar"}}
Unlocking account 2 | Attempt 1/3 Unlocking account 2 | Attempt 1/3
Passphrase: {{.InputLine "foobar"}} Password: {{.InputLine "foobar"}}
`) `)
geth.ExpectExit() geth.ExpectExit()
@ -238,7 +238,7 @@ func TestUnlockFlagPasswordFileWrongPassword(t *testing.T) {
"--password", "testdata/wrong-passwords.txt", "--unlock", "0,2") "--password", "testdata/wrong-passwords.txt", "--unlock", "0,2")
defer geth.ExpectExit() defer geth.ExpectExit()
geth.Expect(` geth.Expect(`
Fatal: Failed to unlock account 0 (could not decrypt key with given passphrase) Fatal: Failed to unlock account 0 (could not decrypt key with given password)
`) `)
} }
@ -258,12 +258,12 @@ func TestUnlockFlagAmbiguous(t *testing.T) {
geth.Expect(` geth.Expect(`
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
!! Unsupported terminal, password will be echoed. !! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "foobar"}} Password: {{.InputLine "foobar"}}
Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a: Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a:
keystore://{{keypath "1"}} keystore://{{keypath "1"}}
keystore://{{keypath "2"}} keystore://{{keypath "2"}}
Testing your passphrase against all of them... Testing your password against all of them...
Your passphrase unlocked keystore://{{keypath "1"}} Your password unlocked keystore://{{keypath "1"}}
In order to avoid this warning, you need to remove the following duplicate key files: In order to avoid this warning, you need to remove the following duplicate key files:
keystore://{{keypath "2"}} keystore://{{keypath "2"}}
`) `)
@ -295,11 +295,11 @@ func TestUnlockFlagAmbiguousWrongPassword(t *testing.T) {
geth.Expect(` geth.Expect(`
Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3 Unlocking account f466859ead1932d743d622cb74fc058882e8648a | Attempt 1/3
!! Unsupported terminal, password will be echoed. !! Unsupported terminal, password will be echoed.
Passphrase: {{.InputLine "wrong"}} Password: {{.InputLine "wrong"}}
Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a: Multiple key files exist for address f466859ead1932d743d622cb74fc058882e8648a:
keystore://{{keypath "1"}} keystore://{{keypath "1"}}
keystore://{{keypath "2"}} keystore://{{keypath "2"}}
Testing your passphrase against all of them... Testing your password against all of them...
Fatal: None of the listed files could be unlocked. Fatal: None of the listed files could be unlocked.
`) `)
geth.ExpectExit() geth.ExpectExit()

View file

@ -20,6 +20,7 @@ import (
"bufio" "bufio"
"errors" "errors"
"fmt" "fmt"
"math/big"
"os" "os"
"reflect" "reflect"
"unicode" "unicode"
@ -123,7 +124,6 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
} }
// Apply flags. // Apply flags.
utils.SetULC(ctx, &cfg.Eth)
utils.SetNodeConfig(ctx, &cfg.Node) utils.SetNodeConfig(ctx, &cfg.Node)
stack, err := node.New(&cfg.Node) stack, err := node.New(&cfg.Node)
if err != nil { if err != nil {
@ -133,7 +133,6 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) { if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) {
cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name) cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
} }
utils.SetShhConfig(ctx, stack, &cfg.Shh) utils.SetShhConfig(ctx, stack, &cfg.Shh)
utils.SetDashboardConfig(ctx, &cfg.Dashboard) utils.SetDashboardConfig(ctx, &cfg.Dashboard)
@ -152,6 +151,9 @@ func enableWhisper(ctx *cli.Context) bool {
func makeFullNode(ctx *cli.Context) *node.Node { func makeFullNode(ctx *cli.Context) *node.Node {
stack, cfg := makeConfigNode(ctx) stack, cfg := makeConfigNode(ctx)
if ctx.GlobalIsSet(utils.OverrideIstanbulFlag.Name) {
cfg.Eth.OverrideIstanbul = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideIstanbulFlag.Name))
}
utils.RegisterEthService(stack, &cfg.Eth) utils.RegisterEthService(stack, &cfg.Eth)
if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) { if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {

View file

@ -77,6 +77,7 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Cons
// same time. // same time.
func localConsole(ctx *cli.Context) error { func localConsole(ctx *cli.Context) error {
// Create and start the node based on the CLI flags // Create and start the node based on the CLI flags
prepare(ctx)
node := makeFullNode(ctx) node := makeFullNode(ctx)
startNode(ctx, node) startNode(ctx, node)
defer node.Close() defer node.Close()

View file

@ -87,7 +87,7 @@ func TestIPCAttachWelcome(t *testing.T) {
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
"--etherbase", coinbase, "--shh", "--ipcpath", ipc) "--etherbase", coinbase, "--shh", "--ipcpath", ipc)
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open waitForEndpoint(t, ipc, 3*time.Second)
testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs) testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs)
geth.Interrupt() geth.Interrupt()
@ -101,8 +101,9 @@ func TestHTTPAttachWelcome(t *testing.T) {
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
"--etherbase", coinbase, "--rpc", "--rpcport", port) "--etherbase", coinbase, "--rpc", "--rpcport", port)
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open endpoint := "http://127.0.0.1:" + port
testAttachWelcome(t, geth, "http://localhost:"+port, httpAPIs) waitForEndpoint(t, endpoint, 3*time.Second)
testAttachWelcome(t, geth, endpoint, httpAPIs)
geth.Interrupt() geth.Interrupt()
geth.ExpectExit() geth.ExpectExit()
@ -116,8 +117,9 @@ func TestWSAttachWelcome(t *testing.T) {
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
"--etherbase", coinbase, "--ws", "--wsport", port) "--etherbase", coinbase, "--ws", "--wsport", port)
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open endpoint := "ws://127.0.0.1:" + port
testAttachWelcome(t, geth, "ws://localhost:"+port, httpAPIs) waitForEndpoint(t, endpoint, 3*time.Second)
testAttachWelcome(t, geth, endpoint, httpAPIs)
geth.Interrupt() geth.Interrupt()
geth.ExpectExit() geth.ExpectExit()

View file

@ -21,6 +21,7 @@ import (
"fmt" "fmt"
"math" "math"
"os" "os"
"runtime"
godebug "runtime/debug" godebug "runtime/debug"
"sort" "sort"
"strconv" "strconv"
@ -68,6 +69,7 @@ var (
utils.ExternalSignerFlag, utils.ExternalSignerFlag,
utils.NoUSBFlag, utils.NoUSBFlag,
utils.SmartCardDaemonPathFlag, utils.SmartCardDaemonPathFlag,
utils.OverrideIstanbulFlag,
utils.DashboardEnabledFlag, utils.DashboardEnabledFlag,
utils.DashboardAddrFlag, utils.DashboardAddrFlag,
utils.DashboardPortFlag, utils.DashboardPortFlag,
@ -89,18 +91,19 @@ var (
utils.TxPoolAccountQueueFlag, utils.TxPoolAccountQueueFlag,
utils.TxPoolGlobalQueueFlag, utils.TxPoolGlobalQueueFlag,
utils.TxPoolLifetimeFlag, utils.TxPoolLifetimeFlag,
utils.ULCModeConfigFlag,
utils.OnlyAnnounceModeFlag,
utils.ULCTrustedNodesFlag,
utils.ULCMinTrustedFractionFlag,
utils.SyncModeFlag, utils.SyncModeFlag,
utils.ExitWhenSyncedFlag, utils.ExitWhenSyncedFlag,
utils.GCModeFlag, utils.GCModeFlag,
utils.LightServFlag, utils.LightServeFlag,
utils.LightBandwidthInFlag, utils.LightLegacyServFlag,
utils.LightBandwidthOutFlag, utils.LightIngressFlag,
utils.LightPeersFlag, utils.LightEgressFlag,
utils.LightMaxPeersFlag,
utils.LightLegacyPeersFlag,
utils.LightKDFFlag, utils.LightKDFFlag,
utils.UltraLightServersFlag,
utils.UltraLightFractionFlag,
utils.UltraLightOnlyAnnounceFlag,
utils.WhitelistFlag, utils.WhitelistFlag,
utils.CacheFlag, utils.CacheFlag,
utils.CacheDatabaseFlag, utils.CacheDatabaseFlag,
@ -240,42 +243,6 @@ func init() {
if err := debug.Setup(ctx, logdir); err != nil { if err := debug.Setup(ctx, logdir); err != nil {
return err return err
} }
// If we're a full node on mainnet without --cache specified, bump default cache allowance
if ctx.GlobalString(utils.SyncModeFlag.Name) != "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) && !ctx.GlobalIsSet(utils.NetworkIdFlag.Name) {
// Make sure we're not on any supported preconfigured testnet either
if !ctx.GlobalIsSet(utils.TestnetFlag.Name) && !ctx.GlobalIsSet(utils.RinkebyFlag.Name) && !ctx.GlobalIsSet(utils.GoerliFlag.Name) {
// Nope, we're really on mainnet. Bump that cache up!
log.Info("Bumping default cache on mainnet", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 4096)
ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(4096))
}
}
// If we're running a light client on any network, drop the cache to some meaningfully low amount
if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) {
log.Info("Dropping default light client cache", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 128)
ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(128))
}
// Cap the cache allowance and tune the garbage collector
var mem gosigar.Mem
if err := mem.Get(); err == nil {
allowance := int(mem.Total / 1024 / 1024 / 3)
if cache := ctx.GlobalInt(utils.CacheFlag.Name); cache > allowance {
log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance)
ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(allowance))
}
}
// Ensure Go's GC ignores the database cache for trigger percentage
cache := ctx.GlobalInt(utils.CacheFlag.Name)
gogc := math.Max(20, math.Min(100, 100/(float64(cache)/1024)))
log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc))
godebug.SetGCPercent(int(gogc))
// Start metrics export if enabled
utils.SetupMetrics(ctx)
// Start system runtime metrics collection
go metrics.CollectProcessMetrics(3 * time.Second)
return nil return nil
} }
@ -293,6 +260,50 @@ func main() {
} }
} }
// prepare manipulates memory cache allowance and setups metric system.
// This function should be called before launching devp2p stack.
func prepare(ctx *cli.Context) {
// If we're a full node on mainnet without --cache specified, bump default cache allowance
if ctx.GlobalString(utils.SyncModeFlag.Name) != "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) && !ctx.GlobalIsSet(utils.NetworkIdFlag.Name) {
// Make sure we're not on any supported preconfigured testnet either
if !ctx.GlobalIsSet(utils.TestnetFlag.Name) && !ctx.GlobalIsSet(utils.RinkebyFlag.Name) && !ctx.GlobalIsSet(utils.GoerliFlag.Name) && !ctx.GlobalIsSet(utils.DeveloperFlag.Name) {
// Nope, we're really on mainnet. Bump that cache up!
log.Info("Bumping default cache on mainnet", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 4096)
ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(4096))
}
}
// If we're running a light client on any network, drop the cache to some meaningfully low amount
if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) {
log.Info("Dropping default light client cache", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 128)
ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(128))
}
// Cap the cache allowance and tune the garbage collector
var mem gosigar.Mem
// Workaround until OpenBSD support lands into gosigar
// Check https://github.com/elastic/gosigar#supported-platforms
if runtime.GOOS != "openbsd" {
if err := mem.Get(); err == nil {
allowance := int(mem.Total / 1024 / 1024 / 3)
if cache := ctx.GlobalInt(utils.CacheFlag.Name); cache > allowance {
log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance)
ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(allowance))
}
}
}
// Ensure Go's GC ignores the database cache for trigger percentage
cache := ctx.GlobalInt(utils.CacheFlag.Name)
gogc := math.Max(20, math.Min(100, 100/(float64(cache)/1024)))
log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc))
godebug.SetGCPercent(int(gogc))
// Start metrics export if enabled
utils.SetupMetrics(ctx)
// Start system runtime metrics collection
go metrics.CollectProcessMetrics(3 * time.Second)
}
// geth is the main entry point into the system if no special subcommand is ran. // geth is the main entry point into the system if no special subcommand is ran.
// It creates a default node based on the command line arguments and runs it in // It creates a default node based on the command line arguments and runs it in
// blocking mode, waiting for it to be shut down. // blocking mode, waiting for it to be shut down.
@ -300,6 +311,7 @@ func geth(ctx *cli.Context) error {
if args := ctx.Args(); len(args) > 0 { if args := ctx.Args(); len(args) > 0 {
return fmt.Errorf("invalid command: %q", args[0]) return fmt.Errorf("invalid command: %q", args[0])
} }
prepare(ctx)
node := makeFullNode(ctx) node := makeFullNode(ctx)
defer node.Close() defer node.Close()
startNode(ctx, node) startNode(ctx, node)
@ -332,7 +344,7 @@ func startNode(ctx *cli.Context, stack *node.Node) {
// Set contract backend for ethereum service if local node // Set contract backend for ethereum service if local node
// is serving LES requests. // is serving LES requests.
if ctx.GlobalInt(utils.LightServFlag.Name) > 0 { if ctx.GlobalInt(utils.LightLegacyServFlag.Name) > 0 || ctx.GlobalInt(utils.LightServeFlag.Name) > 0 {
var ethService *eth.Ethereum var ethService *eth.Ethereum
if err := stack.Service(&ethService); err != nil { if err := stack.Service(&ethService); err != nil {
utils.Fatalf("Failed to retrieve ethereum service: %v", err) utils.Fatalf("Failed to retrieve ethereum service: %v", err)

View file

@ -86,7 +86,7 @@ type RetestethEthAPI interface {
} }
type RetestethDebugAPI interface { type RetestethDebugAPI interface {
AccountRangeAt(ctx context.Context, AccountRange(ctx context.Context,
blockHashOrNumber *math.HexOrDecimal256, txIndex uint64, blockHashOrNumber *math.HexOrDecimal256, txIndex uint64,
addressHash *math.HexOrDecimal256, maxResults uint64, addressHash *math.HexOrDecimal256, maxResults uint64,
) (AccountRangeResult, error) ) (AccountRangeResult, error)
@ -132,6 +132,7 @@ type CParamsParams struct {
ByzantiumForkBlock *math.HexOrDecimal64 `json:"byzantiumForkBlock"` ByzantiumForkBlock *math.HexOrDecimal64 `json:"byzantiumForkBlock"`
ConstantinopleForkBlock *math.HexOrDecimal64 `json:"constantinopleForkBlock"` ConstantinopleForkBlock *math.HexOrDecimal64 `json:"constantinopleForkBlock"`
ConstantinopleFixForkBlock *math.HexOrDecimal64 `json:"constantinopleFixForkBlock"` ConstantinopleFixForkBlock *math.HexOrDecimal64 `json:"constantinopleFixForkBlock"`
IstanbulBlock *math.HexOrDecimal64 `json:"istanbulForkBlock"`
ChainID *math.HexOrDecimal256 `json:"chainID"` ChainID *math.HexOrDecimal256 `json:"chainID"`
MaximumExtraDataSize math.HexOrDecimal64 `json:"maximumExtraDataSize"` MaximumExtraDataSize math.HexOrDecimal64 `json:"maximumExtraDataSize"`
TieBreakingGas bool `json:"tieBreakingGas"` TieBreakingGas bool `json:"tieBreakingGas"`
@ -319,6 +320,7 @@ func (api *RetestethAPI) SetChainParams(ctx context.Context, chainParams ChainPa
byzantiumBlock *big.Int byzantiumBlock *big.Int
constantinopleBlock *big.Int constantinopleBlock *big.Int
petersburgBlock *big.Int petersburgBlock *big.Int
istanbulBlock *big.Int
) )
if chainParams.Params.HomesteadForkBlock != nil { if chainParams.Params.HomesteadForkBlock != nil {
homesteadBlock = big.NewInt(int64(*chainParams.Params.HomesteadForkBlock)) homesteadBlock = big.NewInt(int64(*chainParams.Params.HomesteadForkBlock))
@ -345,6 +347,10 @@ func (api *RetestethAPI) SetChainParams(ctx context.Context, chainParams ChainPa
if constantinopleBlock != nil && petersburgBlock == nil { if constantinopleBlock != nil && petersburgBlock == nil {
petersburgBlock = big.NewInt(100000000000) petersburgBlock = big.NewInt(100000000000)
} }
if chainParams.Params.IstanbulBlock != nil {
istanbulBlock = big.NewInt(int64(*chainParams.Params.IstanbulBlock))
}
genesis := &core.Genesis{ genesis := &core.Genesis{
Config: &params.ChainConfig{ Config: &params.ChainConfig{
ChainID: chainId, ChainID: chainId,
@ -357,6 +363,7 @@ func (api *RetestethAPI) SetChainParams(ctx context.Context, chainParams ChainPa
ByzantiumBlock: byzantiumBlock, ByzantiumBlock: byzantiumBlock,
ConstantinopleBlock: constantinopleBlock, ConstantinopleBlock: constantinopleBlock,
PetersburgBlock: petersburgBlock, PetersburgBlock: petersburgBlock,
IstanbulBlock: istanbulBlock,
}, },
Nonce: uint64(chainParams.Genesis.Nonce), Nonce: uint64(chainParams.Genesis.Nonce),
Timestamp: uint64(chainParams.Genesis.Timestamp), Timestamp: uint64(chainParams.Genesis.Timestamp),
@ -501,7 +508,7 @@ func (api *RetestethAPI) mineBlock() error {
statedb.Prepare(tx.Hash(), common.Hash{}, txCount) statedb.Prepare(tx.Hash(), common.Hash{}, txCount)
snap := statedb.Snapshot() snap := statedb.Snapshot()
receipt, _, err := core.ApplyTransaction( receipt, err := core.ApplyTransaction(
api.chainConfig, api.chainConfig,
api.blockchain, api.blockchain,
&api.author, &api.author,
@ -533,6 +540,9 @@ func (api *RetestethAPI) mineBlock() error {
} }
} }
block, err := api.engine.FinalizeAndAssemble(api.blockchain, header, statedb, txs, []*types.Header{}, receipts) block, err := api.engine.FinalizeAndAssemble(api.blockchain, header, statedb, txs, []*types.Header{}, receipts)
if err != nil {
return err
}
return api.importBlock(block) return api.importBlock(block)
} }
@ -604,7 +614,7 @@ func (api *RetestethAPI) GetBlockByNumber(ctx context.Context, blockNr math.HexO
return nil, fmt.Errorf("block %d not found", blockNr) return nil, fmt.Errorf("block %d not found", blockNr)
} }
func (api *RetestethAPI) AccountRangeAt(ctx context.Context, func (api *RetestethAPI) AccountRange(ctx context.Context,
blockHashOrNumber *math.HexOrDecimal256, txIndex uint64, blockHashOrNumber *math.HexOrDecimal256, txIndex uint64,
addressHash *math.HexOrDecimal256, maxResults uint64, addressHash *math.HexOrDecimal256, maxResults uint64,
) (AccountRangeResult, error) { ) (AccountRangeResult, error) {
@ -669,7 +679,7 @@ func (api *RetestethAPI) AccountRangeAt(ctx context.Context,
} }
it := trie.NewIterator(accountTrie.NodeIterator(common.BigToHash((*big.Int)(addressHash)).Bytes())) it := trie.NewIterator(accountTrie.NodeIterator(common.BigToHash((*big.Int)(addressHash)).Bytes()))
result := AccountRangeResult{AddressMap: make(map[common.Hash]common.Address)} result := AccountRangeResult{AddressMap: make(map[common.Hash]common.Address)}
for i := 0; /*i < int(maxResults) && */ it.Next(); i++ { for i := 0; i < int(maxResults) && it.Next(); i++ {
if preimage := accountTrie.GetKey(it.Key); preimage != nil { if preimage := accountTrie.GetKey(it.Key); preimage != nil {
result.AddressMap[common.BytesToHash(it.Key)] = common.BytesToAddress(preimage) result.AddressMap[common.BytesToHash(it.Key)] = common.BytesToAddress(preimage)
//fmt.Printf("%x: %x\n", it.Key, preimage) //fmt.Printf("%x: %x\n", it.Key, preimage)
@ -765,10 +775,10 @@ func (api *RetestethAPI) StorageRangeAt(ctx context.Context,
} }
// Ensure any modifications are committed to the state // Ensure any modifications are committed to the state
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect // Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
root = statedb.IntermediateRoot(vmenv.ChainConfig().IsEIP158(block.Number())) _ = statedb.IntermediateRoot(vmenv.ChainConfig().IsEIP158(block.Number()))
if idx == int(txIndex) { if idx == int(txIndex) {
// This is to make sure root can be opened by OpenTrie // This is to make sure root can be opened by OpenTrie
root, err = statedb.Commit(vmenv.ChainConfig().IsEIP158(block.Number())) _, err = statedb.Commit(vmenv.ChainConfig().IsEIP158(block.Number()))
if err != nil { if err != nil {
return StorageRangeResult{}, err return StorageRangeResult{}, err
} }
@ -829,7 +839,7 @@ func retesteth(ctx *cli.Context) error {
log.Info("Welcome to retesteth!") log.Info("Welcome to retesteth!")
// register signer API with server // register signer API with server
var ( var (
extapiURL = "n/a" extapiURL string
) )
apiImpl := &RetestethAPI{} apiImpl := &RetestethAPI{}
var testApi RetestethTestAPI = apiImpl var testApi RetestethTestAPI = apiImpl

View file

@ -17,13 +17,16 @@
package main package main
import ( import (
"context"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"testing" "testing"
"time"
"github.com/docker/docker/pkg/reexec" "github.com/docker/docker/pkg/reexec"
"github.com/ethereum/go-ethereum/internal/cmdtest" "github.com/ethereum/go-ethereum/internal/cmdtest"
"github.com/ethereum/go-ethereum/rpc"
) )
func tmpdir(t *testing.T) string { func tmpdir(t *testing.T) string {
@ -96,3 +99,28 @@ func runGeth(t *testing.T, args ...string) *testgeth {
return tt return tt
} }
// waitForEndpoint attempts to connect to an RPC endpoint until it succeeds.
func waitForEndpoint(t *testing.T, endpoint string, timeout time.Duration) {
probe := func() bool {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
c, err := rpc.DialContext(ctx, endpoint)
if c != nil {
_, err = c.SupportedModules()
c.Close()
}
return err == nil
}
start := time.Now()
for {
if probe() {
return
}
if time.Since(start) > timeout {
t.Fatal("endpoint", endpoint, "did not open within", timeout)
}
time.Sleep(200 * time.Millisecond)
}
}

View file

@ -82,14 +82,22 @@ var AppHelpFlagGroups = []flagGroup{
utils.GCModeFlag, utils.GCModeFlag,
utils.EthStatsURLFlag, utils.EthStatsURLFlag,
utils.IdentityFlag, utils.IdentityFlag,
utils.LightServFlag,
utils.LightBandwidthInFlag,
utils.LightBandwidthOutFlag,
utils.LightPeersFlag,
utils.LightKDFFlag, utils.LightKDFFlag,
utils.WhitelistFlag, utils.WhitelistFlag,
}, },
}, },
{
Name: "LIGHT CLIENT",
Flags: []cli.Flag{
utils.LightServeFlag,
utils.LightIngressFlag,
utils.LightEgressFlag,
utils.LightMaxPeersFlag,
utils.UltraLightServersFlag,
utils.UltraLightFractionFlag,
utils.UltraLightOnlyAnnounceFlag,
},
},
{ {
Name: "DEVELOPER CHAIN", Name: "DEVELOPER CHAIN",
Flags: []cli.Flag{ Flags: []cli.Flag{
@ -245,6 +253,8 @@ var AppHelpFlagGroups = []flagGroup{
{ {
Name: "DEPRECATED", Name: "DEPRECATED",
Flags: []cli.Flag{ Flags: []cli.Flag{
utils.LightLegacyServFlag,
utils.LightLegacyPeersFlag,
utils.MinerLegacyThreadsFlag, utils.MinerLegacyThreadsFlag,
utils.MinerLegacyGasTargetFlag, utils.MinerLegacyGasTargetFlag,
utils.MinerLegacyGasPriceFlag, utils.MinerLegacyGasPriceFlag,

View file

@ -36,25 +36,27 @@ import (
type alethGenesisSpec struct { type alethGenesisSpec struct {
SealEngine string `json:"sealEngine"` SealEngine string `json:"sealEngine"`
Params struct { Params struct {
AccountStartNonce math2.HexOrDecimal64 `json:"accountStartNonce"` AccountStartNonce math2.HexOrDecimal64 `json:"accountStartNonce"`
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"` MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
HomesteadForkBlock hexutil.Uint64 `json:"homesteadForkBlock"` HomesteadForkBlock *hexutil.Big `json:"homesteadForkBlock,omitempty"`
DaoHardforkBlock math2.HexOrDecimal64 `json:"daoHardforkBlock"` DaoHardforkBlock math2.HexOrDecimal64 `json:"daoHardforkBlock"`
EIP150ForkBlock hexutil.Uint64 `json:"EIP150ForkBlock"` EIP150ForkBlock *hexutil.Big `json:"EIP150ForkBlock,omitempty"`
EIP158ForkBlock hexutil.Uint64 `json:"EIP158ForkBlock"` EIP158ForkBlock *hexutil.Big `json:"EIP158ForkBlock,omitempty"`
ByzantiumForkBlock hexutil.Uint64 `json:"byzantiumForkBlock"` ByzantiumForkBlock *hexutil.Big `json:"byzantiumForkBlock,omitempty"`
ConstantinopleForkBlock hexutil.Uint64 `json:"constantinopleForkBlock"` ConstantinopleForkBlock *hexutil.Big `json:"constantinopleForkBlock,omitempty"`
MinGasLimit hexutil.Uint64 `json:"minGasLimit"` ConstantinopleFixForkBlock *hexutil.Big `json:"constantinopleFixForkBlock,omitempty"`
MaxGasLimit hexutil.Uint64 `json:"maxGasLimit"` IstanbulForkBlock *hexutil.Big `json:"istanbulForkBlock,omitempty"`
TieBreakingGas bool `json:"tieBreakingGas"` MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
GasLimitBoundDivisor math2.HexOrDecimal64 `json:"gasLimitBoundDivisor"` MaxGasLimit hexutil.Uint64 `json:"maxGasLimit"`
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"` TieBreakingGas bool `json:"tieBreakingGas"`
DifficultyBoundDivisor *math2.HexOrDecimal256 `json:"difficultyBoundDivisor"` GasLimitBoundDivisor math2.HexOrDecimal64 `json:"gasLimitBoundDivisor"`
DurationLimit *math2.HexOrDecimal256 `json:"durationLimit"` MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
BlockReward *hexutil.Big `json:"blockReward"` DifficultyBoundDivisor *math2.HexOrDecimal256 `json:"difficultyBoundDivisor"`
NetworkID hexutil.Uint64 `json:"networkID"` DurationLimit *math2.HexOrDecimal256 `json:"durationLimit"`
ChainID hexutil.Uint64 `json:"chainID"` BlockReward *hexutil.Big `json:"blockReward"`
AllowFutureBlocks bool `json:"allowFutureBlocks"` NetworkID hexutil.Uint64 `json:"networkID"`
ChainID hexutil.Uint64 `json:"chainID"`
AllowFutureBlocks bool `json:"allowFutureBlocks"`
} `json:"params"` } `json:"params"`
Genesis struct { Genesis struct {
@ -74,7 +76,7 @@ type alethGenesisSpec struct {
// alethGenesisSpecAccount is the prefunded genesis account and/or precompiled // alethGenesisSpecAccount is the prefunded genesis account and/or precompiled
// contract definition. // contract definition.
type alethGenesisSpecAccount struct { type alethGenesisSpecAccount struct {
Balance *math2.HexOrDecimal256 `json:"balance"` Balance *math2.HexOrDecimal256 `json:"balance,omitempty"`
Nonce uint64 `json:"nonce,omitempty"` Nonce uint64 `json:"nonce,omitempty"`
Precompiled *alethGenesisSpecBuiltin `json:"precompiled,omitempty"` Precompiled *alethGenesisSpecBuiltin `json:"precompiled,omitempty"`
} }
@ -82,7 +84,7 @@ type alethGenesisSpecAccount struct {
// alethGenesisSpecBuiltin is the precompiled contract definition. // alethGenesisSpecBuiltin is the precompiled contract definition.
type alethGenesisSpecBuiltin struct { type alethGenesisSpecBuiltin struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
StartingBlock hexutil.Uint64 `json:"startingBlock,omitempty"` StartingBlock *hexutil.Big `json:"startingBlock,omitempty"`
Linear *alethGenesisSpecLinearPricing `json:"linear,omitempty"` Linear *alethGenesisSpecLinearPricing `json:"linear,omitempty"`
} }
@ -106,21 +108,33 @@ func newAlethGenesisSpec(network string, genesis *core.Genesis) (*alethGenesisSp
spec.Params.AccountStartNonce = 0 spec.Params.AccountStartNonce = 0
spec.Params.TieBreakingGas = false spec.Params.TieBreakingGas = false
spec.Params.AllowFutureBlocks = false spec.Params.AllowFutureBlocks = false
// Dao hardfork block is a special one. The fork block is listed as 0 in the
// config but aleth will sync with ETC clients up until the actual dao hard
// fork block.
spec.Params.DaoHardforkBlock = 0 spec.Params.DaoHardforkBlock = 0
spec.Params.HomesteadForkBlock = (hexutil.Uint64)(genesis.Config.HomesteadBlock.Uint64()) if num := genesis.Config.HomesteadBlock; num != nil {
spec.Params.EIP150ForkBlock = (hexutil.Uint64)(genesis.Config.EIP150Block.Uint64()) spec.Params.HomesteadForkBlock = (*hexutil.Big)(num)
spec.Params.EIP158ForkBlock = (hexutil.Uint64)(genesis.Config.EIP158Block.Uint64()) }
if num := genesis.Config.EIP150Block; num != nil {
// Byzantium spec.Params.EIP150ForkBlock = (*hexutil.Big)(num)
}
if num := genesis.Config.EIP158Block; num != nil {
spec.Params.EIP158ForkBlock = (*hexutil.Big)(num)
}
if num := genesis.Config.ByzantiumBlock; num != nil { if num := genesis.Config.ByzantiumBlock; num != nil {
spec.setByzantium(num) spec.Params.ByzantiumForkBlock = (*hexutil.Big)(num)
} }
// Constantinople
if num := genesis.Config.ConstantinopleBlock; num != nil { if num := genesis.Config.ConstantinopleBlock; num != nil {
spec.setConstantinople(num) spec.Params.ConstantinopleForkBlock = (*hexutil.Big)(num)
}
if num := genesis.Config.PetersburgBlock; num != nil {
spec.Params.ConstantinopleFixForkBlock = (*hexutil.Big)(num)
}
if num := genesis.Config.IstanbulBlock; num != nil {
spec.Params.IstanbulForkBlock = (*hexutil.Big)(num)
} }
spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64()) spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
spec.Params.ChainID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64()) spec.Params.ChainID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize) spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
@ -157,15 +171,32 @@ func newAlethGenesisSpec(network string, genesis *core.Genesis) (*alethGenesisSp
Linear: &alethGenesisSpecLinearPricing{Base: 15, Word: 3}}) Linear: &alethGenesisSpecLinearPricing{Base: 15, Word: 3}})
if genesis.Config.ByzantiumBlock != nil { if genesis.Config.ByzantiumBlock != nil {
spec.setPrecompile(5, &alethGenesisSpecBuiltin{Name: "modexp", spec.setPrecompile(5, &alethGenesisSpecBuiltin{Name: "modexp",
StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64())}) StartingBlock: (*hexutil.Big)(genesis.Config.ByzantiumBlock)})
spec.setPrecompile(6, &alethGenesisSpecBuiltin{Name: "alt_bn128_G1_add", spec.setPrecompile(6, &alethGenesisSpecBuiltin{Name: "alt_bn128_G1_add",
StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()), StartingBlock: (*hexutil.Big)(genesis.Config.ByzantiumBlock),
Linear: &alethGenesisSpecLinearPricing{Base: 500}}) Linear: &alethGenesisSpecLinearPricing{Base: 500}})
spec.setPrecompile(7, &alethGenesisSpecBuiltin{Name: "alt_bn128_G1_mul", spec.setPrecompile(7, &alethGenesisSpecBuiltin{Name: "alt_bn128_G1_mul",
StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()), StartingBlock: (*hexutil.Big)(genesis.Config.ByzantiumBlock),
Linear: &alethGenesisSpecLinearPricing{Base: 40000}}) Linear: &alethGenesisSpecLinearPricing{Base: 40000}})
spec.setPrecompile(8, &alethGenesisSpecBuiltin{Name: "alt_bn128_pairing_product", spec.setPrecompile(8, &alethGenesisSpecBuiltin{Name: "alt_bn128_pairing_product",
StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64())}) StartingBlock: (*hexutil.Big)(genesis.Config.ByzantiumBlock)})
}
if genesis.Config.IstanbulBlock != nil {
if genesis.Config.ByzantiumBlock == nil {
return nil, errors.New("invalid genesis, istanbul fork is enabled while byzantium is not")
}
spec.setPrecompile(6, &alethGenesisSpecBuiltin{
Name: "alt_bn128_G1_add",
StartingBlock: (*hexutil.Big)(genesis.Config.ByzantiumBlock),
}) // Aleth hardcoded the gas policy
spec.setPrecompile(7, &alethGenesisSpecBuiltin{
Name: "alt_bn128_G1_mul",
StartingBlock: (*hexutil.Big)(genesis.Config.ByzantiumBlock),
}) // Aleth hardcoded the gas policy
spec.setPrecompile(9, &alethGenesisSpecBuiltin{
Name: "blake2_compression",
StartingBlock: (*hexutil.Big)(genesis.Config.IstanbulBlock),
})
} }
return spec, nil return spec, nil
} }
@ -196,14 +227,6 @@ func (spec *alethGenesisSpec) setAccount(address common.Address, account core.Ge
} }
func (spec *alethGenesisSpec) setByzantium(num *big.Int) {
spec.Params.ByzantiumForkBlock = hexutil.Uint64(num.Uint64())
}
func (spec *alethGenesisSpec) setConstantinople(num *big.Int) {
spec.Params.ConstantinopleForkBlock = hexutil.Uint64(num.Uint64())
}
// parityChainSpec is the chain specification format used by Parity. // parityChainSpec is the chain specification format used by Parity.
type parityChainSpec struct { type parityChainSpec struct {
Name string `json:"name"` Name string `json:"name"`
@ -223,29 +246,33 @@ type parityChainSpec struct {
} `json:"engine"` } `json:"engine"`
Params struct { Params struct {
AccountStartNonce hexutil.Uint64 `json:"accountStartNonce"` AccountStartNonce hexutil.Uint64 `json:"accountStartNonce"`
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"` MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
MinGasLimit hexutil.Uint64 `json:"minGasLimit"` MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
GasLimitBoundDivisor math2.HexOrDecimal64 `json:"gasLimitBoundDivisor"` GasLimitBoundDivisor math2.HexOrDecimal64 `json:"gasLimitBoundDivisor"`
NetworkID hexutil.Uint64 `json:"networkID"` NetworkID hexutil.Uint64 `json:"networkID"`
ChainID hexutil.Uint64 `json:"chainID"` ChainID hexutil.Uint64 `json:"chainID"`
MaxCodeSize hexutil.Uint64 `json:"maxCodeSize"` MaxCodeSize hexutil.Uint64 `json:"maxCodeSize"`
MaxCodeSizeTransition hexutil.Uint64 `json:"maxCodeSizeTransition"` MaxCodeSizeTransition hexutil.Uint64 `json:"maxCodeSizeTransition"`
EIP98Transition hexutil.Uint64 `json:"eip98Transition"` EIP98Transition hexutil.Uint64 `json:"eip98Transition"`
EIP150Transition hexutil.Uint64 `json:"eip150Transition"` EIP150Transition hexutil.Uint64 `json:"eip150Transition"`
EIP160Transition hexutil.Uint64 `json:"eip160Transition"` EIP160Transition hexutil.Uint64 `json:"eip160Transition"`
EIP161abcTransition hexutil.Uint64 `json:"eip161abcTransition"` EIP161abcTransition hexutil.Uint64 `json:"eip161abcTransition"`
EIP161dTransition hexutil.Uint64 `json:"eip161dTransition"` EIP161dTransition hexutil.Uint64 `json:"eip161dTransition"`
EIP155Transition hexutil.Uint64 `json:"eip155Transition"` EIP155Transition hexutil.Uint64 `json:"eip155Transition"`
EIP140Transition hexutil.Uint64 `json:"eip140Transition"` EIP140Transition hexutil.Uint64 `json:"eip140Transition"`
EIP211Transition hexutil.Uint64 `json:"eip211Transition"` EIP211Transition hexutil.Uint64 `json:"eip211Transition"`
EIP214Transition hexutil.Uint64 `json:"eip214Transition"` EIP214Transition hexutil.Uint64 `json:"eip214Transition"`
EIP658Transition hexutil.Uint64 `json:"eip658Transition"` EIP658Transition hexutil.Uint64 `json:"eip658Transition"`
EIP145Transition hexutil.Uint64 `json:"eip145Transition"` EIP145Transition hexutil.Uint64 `json:"eip145Transition"`
EIP1014Transition hexutil.Uint64 `json:"eip1014Transition"` EIP1014Transition hexutil.Uint64 `json:"eip1014Transition"`
EIP1052Transition hexutil.Uint64 `json:"eip1052Transition"` EIP1052Transition hexutil.Uint64 `json:"eip1052Transition"`
EIP1283Transition hexutil.Uint64 `json:"eip1283Transition"` EIP1283Transition hexutil.Uint64 `json:"eip1283Transition"`
EIP1283DisableTransition hexutil.Uint64 `json:"eip1283DisableTransition"` EIP1283DisableTransition hexutil.Uint64 `json:"eip1283DisableTransition"`
EIP1283ReenableTransition hexutil.Uint64 `json:"eip1283ReenableTransition"`
EIP1344Transition hexutil.Uint64 `json:"eip1344Transition"`
EIP1884Transition hexutil.Uint64 `json:"eip1884Transition"`
EIP2028Transition hexutil.Uint64 `json:"eip2028Transition"`
} `json:"params"` } `json:"params"`
Genesis struct { Genesis struct {
@ -278,17 +305,22 @@ type parityChainSpecAccount struct {
// parityChainSpecBuiltin is the precompiled contract definition. // parityChainSpecBuiltin is the precompiled contract definition.
type parityChainSpecBuiltin struct { type parityChainSpecBuiltin struct {
Name string `json:"name,omitempty"` Name string `json:"name"` // Each builtin should has it own name
ActivateAt math2.HexOrDecimal64 `json:"activate_at,omitempty"` Pricing *parityChainSpecPricing `json:"pricing"` // Each builtin should has it own price strategy
Pricing *parityChainSpecPricing `json:"pricing,omitempty"` ActivateAt *hexutil.Big `json:"activate_at,omitempty"` // ActivateAt can't be omitted if empty, default means no fork
EIP1108Transition *hexutil.Big `json:"eip1108_transition,omitempty"` // EIP1108Transition can't be omitted if empty, default means no fork
} }
// parityChainSpecPricing represents the different pricing models that builtin // parityChainSpecPricing represents the different pricing models that builtin
// contracts might advertise using. // contracts might advertise using.
type parityChainSpecPricing struct { type parityChainSpecPricing struct {
Linear *parityChainSpecLinearPricing `json:"linear,omitempty"` Linear *parityChainSpecLinearPricing `json:"linear,omitempty"`
ModExp *parityChainSpecModExpPricing `json:"modexp,omitempty"` ModExp *parityChainSpecModExpPricing `json:"modexp,omitempty"`
AltBnPairing *parityChainSpecAltBnPairingPricing `json:"alt_bn128_pairing,omitempty"` AltBnPairing *parityChainSpecAltBnPairingPricing `json:"alt_bn128_pairing,omitempty"`
AltBnConstOperation *parityChainSpecAltBnConstOperationPricing `json:"alt_bn128_const_operations,omitempty"`
// Blake2F is the price per round of Blake2 compression
Blake2F *parityChainSpecBlakePricing `json:"blake2_f,omitempty"`
} }
type parityChainSpecLinearPricing struct { type parityChainSpecLinearPricing struct {
@ -300,9 +332,20 @@ type parityChainSpecModExpPricing struct {
Divisor uint64 `json:"divisor"` Divisor uint64 `json:"divisor"`
} }
type parityChainSpecAltBnConstOperationPricing struct {
Price uint64 `json:"price"`
EIP1108TransitionPrice uint64 `json:"eip1108_transition_price,omitempty"` // Before Istanbul fork, this field is nil
}
type parityChainSpecAltBnPairingPricing struct { type parityChainSpecAltBnPairingPricing struct {
Base uint64 `json:"base"` Base uint64 `json:"base"`
Pair uint64 `json:"pair"` Pair uint64 `json:"pair"`
EIP1108TransitionBase uint64 `json:"eip1108_transition_base,omitempty"` // Before Istanbul fork, this field is nil
EIP1108TransitionPair uint64 `json:"eip1108_transition_pair,omitempty"` // Before Istanbul fork, this field is nil
}
type parityChainSpecBlakePricing struct {
GasPerRound uint64 `json:"gas_per_round"`
} }
// newParityChainSpec converts a go-ethereum genesis block into a Parity specific // newParityChainSpec converts a go-ethereum genesis block into a Parity specific
@ -352,7 +395,10 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
if num := genesis.Config.PetersburgBlock; num != nil { if num := genesis.Config.PetersburgBlock; num != nil {
spec.setConstantinopleFix(num) spec.setConstantinopleFix(num)
} }
// Istanbul
if num := genesis.Config.IstanbulBlock; num != nil {
spec.setIstanbul(num)
}
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize) spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit) spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor) spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor)
@ -398,18 +444,34 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
Name: "identity", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 15, Word: 3}}, Name: "identity", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 15, Word: 3}},
}) })
if genesis.Config.ByzantiumBlock != nil { if genesis.Config.ByzantiumBlock != nil {
blnum := math2.HexOrDecimal64(genesis.Config.ByzantiumBlock.Uint64())
spec.setPrecompile(5, &parityChainSpecBuiltin{ spec.setPrecompile(5, &parityChainSpecBuiltin{
Name: "modexp", ActivateAt: blnum, Pricing: &parityChainSpecPricing{ModExp: &parityChainSpecModExpPricing{Divisor: 20}}, Name: "modexp", ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), Pricing: &parityChainSpecPricing{ModExp: &parityChainSpecModExpPricing{Divisor: 20}},
}) })
spec.setPrecompile(6, &parityChainSpecBuiltin{ spec.setPrecompile(6, &parityChainSpecBuiltin{
Name: "alt_bn128_add", ActivateAt: blnum, Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 500}}, Name: "alt_bn128_add", ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), Pricing: &parityChainSpecPricing{AltBnConstOperation: &parityChainSpecAltBnConstOperationPricing{Price: 500}},
}) })
spec.setPrecompile(7, &parityChainSpecBuiltin{ spec.setPrecompile(7, &parityChainSpecBuiltin{
Name: "alt_bn128_mul", ActivateAt: blnum, Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 40000}}, Name: "alt_bn128_mul", ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), Pricing: &parityChainSpecPricing{AltBnConstOperation: &parityChainSpecAltBnConstOperationPricing{Price: 40000}},
}) })
spec.setPrecompile(8, &parityChainSpecBuiltin{ spec.setPrecompile(8, &parityChainSpecBuiltin{
Name: "alt_bn128_pairing", ActivateAt: blnum, Pricing: &parityChainSpecPricing{AltBnPairing: &parityChainSpecAltBnPairingPricing{Base: 100000, Pair: 80000}}, Name: "alt_bn128_pairing", ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), Pricing: &parityChainSpecPricing{AltBnPairing: &parityChainSpecAltBnPairingPricing{Base: 100000, Pair: 80000}},
})
}
if genesis.Config.IstanbulBlock != nil {
if genesis.Config.ByzantiumBlock == nil {
return nil, errors.New("invalid genesis, istanbul fork is enabled while byzantium is not")
}
spec.setPrecompile(6, &parityChainSpecBuiltin{
Name: "alt_bn128_add", ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), EIP1108Transition: (*hexutil.Big)(genesis.Config.IstanbulBlock), Pricing: &parityChainSpecPricing{AltBnConstOperation: &parityChainSpecAltBnConstOperationPricing{Price: 500, EIP1108TransitionPrice: 150}},
})
spec.setPrecompile(7, &parityChainSpecBuiltin{
Name: "alt_bn128_mul", ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), EIP1108Transition: (*hexutil.Big)(genesis.Config.IstanbulBlock), Pricing: &parityChainSpecPricing{AltBnConstOperation: &parityChainSpecAltBnConstOperationPricing{Price: 40000, EIP1108TransitionPrice: 6000}},
})
spec.setPrecompile(8, &parityChainSpecBuiltin{
Name: "alt_bn128_pairing", ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), EIP1108Transition: (*hexutil.Big)(genesis.Config.IstanbulBlock), Pricing: &parityChainSpecPricing{AltBnPairing: &parityChainSpecAltBnPairingPricing{Base: 100000, Pair: 80000, EIP1108TransitionBase: 45000, EIP1108TransitionPair: 34000}},
})
spec.setPrecompile(9, &parityChainSpecBuiltin{
Name: "blake2_f", ActivateAt: (*hexutil.Big)(genesis.Config.IstanbulBlock), Pricing: &parityChainSpecPricing{Blake2F: &parityChainSpecBlakePricing{GasPerRound: 1}},
}) })
} }
return spec, nil return spec, nil
@ -451,6 +513,15 @@ func (spec *parityChainSpec) setConstantinopleFix(num *big.Int) {
spec.Params.EIP1283DisableTransition = hexutil.Uint64(num.Uint64()) spec.Params.EIP1283DisableTransition = hexutil.Uint64(num.Uint64())
} }
func (spec *parityChainSpec) setIstanbul(num *big.Int) {
// spec.Params.EIP152Transition = hexutil.Uint64(num.Uint64())
// spec.Params.EIP1108Transition = hexutil.Uint64(num.Uint64())
spec.Params.EIP1344Transition = hexutil.Uint64(num.Uint64())
spec.Params.EIP1884Transition = hexutil.Uint64(num.Uint64())
spec.Params.EIP2028Transition = hexutil.Uint64(num.Uint64())
spec.Params.EIP1283ReenableTransition = hexutil.Uint64(num.Uint64())
}
// pyEthereumGenesisSpec represents the genesis specification format used by the // pyEthereumGenesisSpec represents the genesis specification format used by the
// Python Ethereum implementation. // Python Ethereum implementation.
type pyEthereumGenesisSpec struct { type pyEthereumGenesisSpec struct {

View file

@ -18,7 +18,6 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"reflect" "reflect"
"strings" "strings"
@ -61,7 +60,7 @@ func TestAlethSturebyConverter(t *testing.T) {
got := strings.Split(c.Sdump(spec), "\n") got := strings.Split(c.Sdump(spec), "\n")
for i := 0; i < len(exp) && i < len(got); i++ { for i := 0; i < len(exp) && i < len(got); i++ {
if exp[i] != got[i] { if exp[i] != got[i] {
fmt.Printf("got: %v\nexp: %v\n", exp[i], got[i]) t.Logf("got: %v\nexp: %v\n", exp[i], got[i])
} }
} }
} }
@ -77,7 +76,7 @@ func TestParitySturebyConverter(t *testing.T) {
if err := json.Unmarshal(blob, &genesis); err != nil { if err := json.Unmarshal(blob, &genesis); err != nil {
t.Fatalf("failed parsing genesis: %v", err) t.Fatalf("failed parsing genesis: %v", err)
} }
spec, err := newParityChainSpec("Stureby", &genesis, []string{}) spec, err := newParityChainSpec("stureby", &genesis, []string{})
if err != nil { if err != nil {
t.Fatalf("failed creating chainspec: %v", err) t.Fatalf("failed creating chainspec: %v", err)
} }
@ -102,7 +101,7 @@ func TestParitySturebyConverter(t *testing.T) {
got := strings.Split(c.Sdump(spec), "\n") got := strings.Split(c.Sdump(spec), "\n")
for i := 0; i < len(exp) && i < len(got); i++ { for i := 0; i < len(exp) && i < len(got); i++ {
if exp[i] != got[i] { if exp[i] != got[i] {
fmt.Printf("got: %v\nexp: %v\n", exp[i], got[i]) t.Logf("got: %v\nexp: %v\n", exp[i], got[i])
} }
} }
} }

View file

@ -30,108 +30,86 @@ import (
// explorerDockerfile is the Dockerfile required to run a block explorer. // explorerDockerfile is the Dockerfile required to run a block explorer.
var explorerDockerfile = ` var explorerDockerfile = `
FROM puppeth/explorer:latest FROM puppeth/blockscout:latest
ADD ethstats.json /ethstats.json
ADD chain.json /chain.json
ADD genesis.json /genesis.json
RUN \ RUN \
echo '(cd ../eth-net-intelligence-api && pm2 start /ethstats.json)' > explorer.sh && \ echo 'geth --cache 512 init /genesis.json' > explorer.sh && \
echo '(cd ../etherchain-light && npm start &)' >> explorer.sh && \ echo $'geth --networkid {{.NetworkID}} --syncmode "full" --gcmode "archive" --port {{.EthPort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --rpc --rpcapi "net,web3,eth,shh,debug" --rpccorsdomain "*" --rpcvhosts "*" --ws --wsorigins "*" --exitwhensynced' >> explorer.sh && \
echo 'exec /parity/parity --chain=/chain.json --port={{.NodePort}} --tracing=on --fat-db=on --pruning=archive' >> explorer.sh echo $'exec geth --networkid {{.NetworkID}} --syncmode "full" --gcmode "archive" --port {{.EthPort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --rpc --rpcapi "net,web3,eth,shh,debug" --rpccorsdomain "*" --rpcvhosts "*" --ws --wsorigins "*" &' >> explorer.sh && \
echo '/usr/local/bin/docker-entrypoint.sh postgres &' >> explorer.sh && \
echo 'sleep 5' >> explorer.sh && \
echo 'mix do ecto.drop --force, ecto.create, ecto.migrate' >> explorer.sh && \
echo 'mix phx.server' >> explorer.sh
ENTRYPOINT ["/bin/sh", "explorer.sh"] ENTRYPOINT ["/bin/sh", "explorer.sh"]
` `
// explorerEthstats is the configuration file for the ethstats javascript client.
var explorerEthstats = `[
{
"name" : "node-app",
"script" : "app.js",
"log_date_format" : "YYYY-MM-DD HH:mm Z",
"merge_logs" : false,
"watch" : false,
"max_restarts" : 10,
"exec_interpreter" : "node",
"exec_mode" : "fork_mode",
"env":
{
"NODE_ENV" : "production",
"RPC_HOST" : "localhost",
"RPC_PORT" : "8545",
"LISTENING_PORT" : "{{.Port}}",
"INSTANCE_NAME" : "{{.Name}}",
"CONTACT_DETAILS" : "",
"WS_SERVER" : "{{.Host}}",
"WS_SECRET" : "{{.Secret}}",
"VERBOSITY" : 2
}
}
]`
// explorerComposefile is the docker-compose.yml file required to deploy and // explorerComposefile is the docker-compose.yml file required to deploy and
// maintain a block explorer. // maintain a block explorer.
var explorerComposefile = ` var explorerComposefile = `
version: '2' version: '2'
services: services:
explorer: explorer:
build: . build: .
image: {{.Network}}/explorer image: {{.Network}}/explorer
container_name: {{.Network}}_explorer_1 container_name: {{.Network}}_explorer_1
ports: ports:
- "{{.NodePort}}:{{.NodePort}}" - "{{.EthPort}}:{{.EthPort}}"
- "{{.NodePort}}:{{.NodePort}}/udp"{{if not .VHost}} - "{{.EthPort}}:{{.EthPort}}/udp"{{if not .VHost}}
- "{{.WebPort}}:3000"{{end}} - "{{.WebPort}}:4000"{{end}}
volumes: environment:
- {{.Datadir}}:/root/.local/share/io.parity.ethereum - ETH_PORT={{.EthPort}}
environment: - ETH_NAME={{.EthName}}
- NODE_PORT={{.NodePort}}/tcp - BLOCK_TRANSFORMER={{.Transformer}}{{if .VHost}}
- STATS={{.Ethstats}}{{if .VHost}} - VIRTUAL_HOST={{.VHost}}
- VIRTUAL_HOST={{.VHost}} - VIRTUAL_PORT=4000{{end}}
- VIRTUAL_PORT=3000{{end}} volumes:
logging: - {{.Datadir}}:/opt/app/.ethereum
driver: "json-file" - {{.DBDir}}:/var/lib/postgresql/data
options: logging:
max-size: "1m" driver: "json-file"
max-file: "10" options:
restart: always max-size: "1m"
max-file: "10"
restart: always
` `
// deployExplorer deploys a new block explorer container to a remote machine via // deployExplorer deploys a new block explorer container to a remote machine via
// SSH, docker and docker-compose. If an instance with the specified network name // SSH, 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 deployExplorer(client *sshClient, network string, chainspec []byte, config *explorerInfos, nocache bool) ([]byte, error) { func deployExplorer(client *sshClient, network string, bootnodes []string, config *explorerInfos, nocache bool, isClique bool) ([]byte, error) {
// 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())
files := make(map[string][]byte) files := make(map[string][]byte)
dockerfile := new(bytes.Buffer) dockerfile := new(bytes.Buffer)
template.Must(template.New("").Parse(explorerDockerfile)).Execute(dockerfile, map[string]interface{}{ template.Must(template.New("").Parse(explorerDockerfile)).Execute(dockerfile, map[string]interface{}{
"NodePort": config.nodePort, "NetworkID": config.node.network,
"Bootnodes": strings.Join(bootnodes, ","),
"Ethstats": config.node.ethstats,
"EthPort": config.node.port,
}) })
files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes() files[filepath.Join(workdir, "Dockerfile")] = dockerfile.Bytes()
ethstats := new(bytes.Buffer) transformer := "base"
template.Must(template.New("").Parse(explorerEthstats)).Execute(ethstats, map[string]interface{}{ if isClique {
"Port": config.nodePort, transformer = "clique"
"Name": config.ethstats[:strings.Index(config.ethstats, ":")], }
"Secret": config.ethstats[strings.Index(config.ethstats, ":")+1 : strings.Index(config.ethstats, "@")],
"Host": config.ethstats[strings.Index(config.ethstats, "@")+1:],
})
files[filepath.Join(workdir, "ethstats.json")] = ethstats.Bytes()
composefile := new(bytes.Buffer) composefile := new(bytes.Buffer)
template.Must(template.New("").Parse(explorerComposefile)).Execute(composefile, map[string]interface{}{ template.Must(template.New("").Parse(explorerComposefile)).Execute(composefile, map[string]interface{}{
"Datadir": config.datadir, "Network": network,
"Network": network, "VHost": config.host,
"NodePort": config.nodePort, "Ethstats": config.node.ethstats,
"VHost": config.webHost, "Datadir": config.node.datadir,
"WebPort": config.webPort, "DBDir": config.dbdir,
"Ethstats": config.ethstats[:strings.Index(config.ethstats, ":")], "EthPort": config.node.port,
"EthName": config.node.ethstats[:strings.Index(config.node.ethstats, ":")],
"WebPort": config.port,
"Transformer": transformer,
}) })
files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes() files[filepath.Join(workdir, "docker-compose.yaml")] = composefile.Bytes()
files[filepath.Join(workdir, "genesis.json")] = config.node.genesis
files[filepath.Join(workdir, "chain.json")] = chainspec
// Upload the deployment files to the remote server (and clean up afterwards) // Upload the deployment files to the remote server (and clean up afterwards)
if out, err := client.Upload(files); err != nil { if out, err := client.Upload(files); err != nil {
@ -149,22 +127,20 @@ func deployExplorer(client *sshClient, network string, chainspec []byte, config
// explorerInfos is returned from a block explorer status check to allow reporting // explorerInfos is returned from a block explorer status check to allow reporting
// various configuration parameters. // various configuration parameters.
type explorerInfos struct { type explorerInfos struct {
datadir string node *nodeInfos
ethstats string dbdir string
nodePort int host string
webHost string port int
webPort int
} }
// Report converts the typed struct into a plain string->string map, containing // Report converts the typed struct into a plain string->string map, containing
// most - but not all - fields for reporting to the user. // most - but not all - fields for reporting to the user.
func (info *explorerInfos) Report() map[string]string { func (info *explorerInfos) Report() map[string]string {
report := map[string]string{ report := map[string]string{
"Data directory": info.datadir, "Website address ": info.host,
"Node listener port ": strconv.Itoa(info.nodePort), "Website listener port ": strconv.Itoa(info.port),
"Ethstats username": info.ethstats, "Ethereum listener port ": strconv.Itoa(info.node.port),
"Website address ": info.webHost, "Ethstats username": info.node.ethstats,
"Website listener port ": strconv.Itoa(info.webPort),
} }
return report return report
} }
@ -172,7 +148,7 @@ func (info *explorerInfos) Report() map[string]string {
// checkExplorer does a health-check against a block explorer server to verify // checkExplorer does a health-check against a block explorer server to verify
// whether it's running, and if yes, whether it's responsive. // whether it's running, and if yes, whether it's responsive.
func checkExplorer(client *sshClient, network string) (*explorerInfos, error) { func checkExplorer(client *sshClient, network string) (*explorerInfos, error) {
// Inspect a possible block explorer container on the host // Inspect a possible explorer container on the host
infos, err := inspectContainer(client, fmt.Sprintf("%s_explorer_1", network)) infos, err := inspectContainer(client, fmt.Sprintf("%s_explorer_1", network))
if err != nil { if err != nil {
return nil, err return nil, err
@ -181,13 +157,13 @@ func checkExplorer(client *sshClient, network string) (*explorerInfos, error) {
return nil, ErrServiceOffline return nil, ErrServiceOffline
} }
// Resolve the port from the host, or the reverse proxy // Resolve the port from the host, or the reverse proxy
webPort := infos.portmap["3000/tcp"] port := infos.portmap["4000/tcp"]
if webPort == 0 { if port == 0 {
if proxy, _ := checkNginx(client, network); proxy != nil { if proxy, _ := checkNginx(client, network); proxy != nil {
webPort = proxy.port port = proxy.port
} }
} }
if webPort == 0 { if port == 0 {
return nil, ErrNotExposed return nil, ErrNotExposed
} }
// Resolve the host from the reverse-proxy and the config values // Resolve the host from the reverse-proxy and the config values
@ -196,17 +172,23 @@ func checkExplorer(client *sshClient, network string) (*explorerInfos, error) {
host = client.server host = client.server
} }
// Run a sanity check to see if the devp2p is reachable // Run a sanity check to see if the devp2p is reachable
nodePort := infos.portmap[infos.envvars["NODE_PORT"]] p2pPort := infos.portmap[infos.envvars["ETH_PORT"]+"/tcp"]
if err = checkPort(client.server, nodePort); err != nil { if err = checkPort(host, p2pPort); err != nil {
log.Warn(fmt.Sprintf("Explorer devp2p port seems unreachable"), "server", client.server, "port", nodePort, "err", err) log.Warn("Explorer node seems unreachable", "server", host, "port", p2pPort, "err", err)
}
if err = checkPort(host, port); err != nil {
log.Warn("Explorer service seems unreachable", "server", host, "port", port, "err", err)
} }
// Assemble and return the useful infos // Assemble and return the useful infos
stats := &explorerInfos{ stats := &explorerInfos{
datadir: infos.volumes["/root/.local/share/io.parity.ethereum"], node: &nodeInfos{
nodePort: nodePort, datadir: infos.volumes["/opt/app/.ethereum"],
webHost: host, port: infos.portmap[infos.envvars["ETH_PORT"]+"/tcp"],
webPort: webPort, ethstats: infos.envvars["ETH_NAME"],
ethstats: infos.envvars["STATS"], },
dbdir: infos.volumes["/var/lib/postgresql/data"],
host: host,
port: port,
} }
return stats, nil return stats, nil
} }

View file

@ -1,112 +1,113 @@
{ {
"sealEngine":"Ethash", "sealEngine": "Ethash",
"params":{ "params": {
"accountStartNonce":"0x00", "accountStartNonce": "0x0",
"maximumExtraDataSize":"0x20", "maximumExtraDataSize": "0x20",
"homesteadForkBlock":"0x2710", "homesteadForkBlock": "0x2710",
"daoHardforkBlock":"0x00", "daoHardforkBlock": "0x0",
"EIP150ForkBlock":"0x3a98", "EIP150ForkBlock": "0x3a98",
"EIP158ForkBlock":"0x59d8", "EIP158ForkBlock": "0x59d8",
"byzantiumForkBlock":"0x7530", "byzantiumForkBlock": "0x7530",
"constantinopleForkBlock":"0x9c40", "constantinopleForkBlock": "0x9c40",
"minGasLimit":"0x1388", "constantinopleFixForkBlock": "0x9c40",
"maxGasLimit":"0x7fffffffffffffff", "istanbulForkBlock": "0xc350",
"tieBreakingGas":false, "minGasLimit": "0x1388",
"gasLimitBoundDivisor":"0x0400", "maxGasLimit": "0x7fffffffffffffff",
"minimumDifficulty":"0x20000", "tieBreakingGas": false,
"difficultyBoundDivisor":"0x0800", "gasLimitBoundDivisor": "0x400",
"durationLimit":"0x0d", "minimumDifficulty": "0x20000",
"blockReward":"0x4563918244F40000", "difficultyBoundDivisor": "0x800",
"networkID":"0x4cb2e", "durationLimit": "0xd",
"chainID":"0x4cb2e", "blockReward": "0x4563918244f40000",
"allowFutureBlocks":false "networkID": "0x4cb2e",
"chainID": "0x4cb2e",
"allowFutureBlocks": false
}, },
"genesis":{ "genesis": {
"nonce":"0x0000000000000000", "nonce": "0x0000000000000000",
"difficulty":"0x20000", "difficulty": "0x20000",
"mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"author":"0x0000000000000000000000000000000000000000", "author": "0x0000000000000000000000000000000000000000",
"timestamp":"0x59a4e76d", "timestamp": "0x59a4e76d",
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData":"0x0000000000000000000000000000000000000000000000000000000b4dc0ffee", "extraData": "0x0000000000000000000000000000000000000000000000000000000b4dc0ffee",
"gasLimit":"0x47b760" "gasLimit": "0x47b760"
}, },
"accounts":{ "accounts": {
"0000000000000000000000000000000000000001":{ "0000000000000000000000000000000000000001": {
"balance":"1", "balance": "0x1",
"precompiled":{ "precompiled": {
"name":"ecrecover", "name": "ecrecover",
"linear":{ "linear": {
"base":3000, "base": 3000,
"word":0 "word": 0
} }
} }
}, },
"0000000000000000000000000000000000000002":{ "0000000000000000000000000000000000000002": {
"balance":"1", "balance": "0x1",
"precompiled":{ "precompiled": {
"name":"sha256", "name": "sha256",
"linear":{ "linear": {
"base":60, "base": 60,
"word":12 "word": 12
} }
} }
}, },
"0000000000000000000000000000000000000003":{ "0000000000000000000000000000000000000003": {
"balance":"1", "balance": "0x1",
"precompiled":{ "precompiled": {
"name":"ripemd160", "name": "ripemd160",
"linear":{ "linear": {
"base":600, "base": 600,
"word":120 "word": 120
} }
} }
}, },
"0000000000000000000000000000000000000004":{ "0000000000000000000000000000000000000004": {
"balance":"1", "balance": "0x1",
"precompiled":{ "precompiled": {
"name":"identity", "name": "identity",
"linear":{ "linear": {
"base":15, "base": 15,
"word":3 "word": 3
} }
} }
}, },
"0000000000000000000000000000000000000005":{ "0000000000000000000000000000000000000005": {
"balance":"1", "balance": "0x1",
"precompiled":{ "precompiled": {
"name":"modexp", "name": "modexp",
"startingBlock":"0x7530" "startingBlock": "0x7530"
} }
}, },
"0000000000000000000000000000000000000006":{ "0000000000000000000000000000000000000006": {
"balance":"1", "balance": "0x1",
"precompiled":{ "precompiled": {
"name":"alt_bn128_G1_add", "name": "alt_bn128_G1_add",
"startingBlock":"0x7530", "startingBlock": "0x7530"
"linear":{
"base":500,
"word":0
}
} }
}, },
"0000000000000000000000000000000000000007":{ "0000000000000000000000000000000000000007": {
"balance":"1", "balance": "0x1",
"precompiled":{ "precompiled": {
"name":"alt_bn128_G1_mul", "name": "alt_bn128_G1_mul",
"startingBlock":"0x7530", "startingBlock": "0x7530"
"linear":{
"base":40000,
"word":0
}
} }
}, },
"0000000000000000000000000000000000000008":{ "0000000000000000000000000000000000000008": {
"balance":"1", "balance": "0x1",
"precompiled":{ "precompiled": {
"name":"alt_bn128_pairing_product", "name": "alt_bn128_pairing_product",
"startingBlock":"0x7530" "startingBlock": "0x7530"
}
},
"0000000000000000000000000000000000000009": {
"balance": "0x1",
"precompiled": {
"name": "blake2_compression",
"startingBlock": "0xc350"
} }
} }
} }
} }

View file

@ -1,6 +1,5 @@
{ {
"config": { "config": {
"ethash":{},
"chainId": 314158, "chainId": 314158,
"homesteadBlock": 10000, "homesteadBlock": 10000,
"eip150Block": 15000, "eip150Block": 15000,
@ -8,11 +7,13 @@
"eip155Block": 23000, "eip155Block": 23000,
"eip158Block": 23000, "eip158Block": 23000,
"byzantiumBlock": 30000, "byzantiumBlock": 30000,
"constantinopleBlock": 40000 "constantinopleBlock": 40000,
"petersburgBlock": 40000,
"istanbulBlock": 50000,
"ethash": {}
}, },
"nonce": "0x0", "nonce": "0x0",
"timestamp": "0x59a4e76d", "timestamp": "0x59a4e76d",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x0000000000000000000000000000000000000000000000000000000b4dc0ffee", "extraData": "0x0000000000000000000000000000000000000000000000000000000b4dc0ffee",
"gasLimit": "0x47b760", "gasLimit": "0x47b760",
"difficulty": "0x20000", "difficulty": "0x20000",
@ -20,28 +21,34 @@
"coinbase": "0x0000000000000000000000000000000000000000", "coinbase": "0x0000000000000000000000000000000000000000",
"alloc": { "alloc": {
"0000000000000000000000000000000000000001": { "0000000000000000000000000000000000000001": {
"balance": "0x01" "balance": "0x1"
}, },
"0000000000000000000000000000000000000002": { "0000000000000000000000000000000000000002": {
"balance": "0x01" "balance": "0x1"
}, },
"0000000000000000000000000000000000000003": { "0000000000000000000000000000000000000003": {
"balance": "0x01" "balance": "0x1"
}, },
"0000000000000000000000000000000000000004": { "0000000000000000000000000000000000000004": {
"balance": "0x01" "balance": "0x1"
}, },
"0000000000000000000000000000000000000005": { "0000000000000000000000000000000000000005": {
"balance": "0x01" "balance": "0x1"
}, },
"0000000000000000000000000000000000000006": { "0000000000000000000000000000000000000006": {
"balance": "0x01" "balance": "0x1"
}, },
"0000000000000000000000000000000000000007": { "0000000000000000000000000000000000000007": {
"balance": "0x01" "balance": "0x1"
}, },
"0000000000000000000000000000000000000008": { "0000000000000000000000000000000000000008": {
"balance": "0x01" "balance": "0x1"
},
"0000000000000000000000000000000000000009": {
"balance": "0x1"
} }
} },
} "number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}

View file

@ -1,181 +1,186 @@
{ {
"name":"Stureby", "name": "stureby",
"dataDir":"stureby", "dataDir": "stureby",
"engine":{ "engine": {
"Ethash":{ "Ethash": {
"params":{ "params": {
"minimumDifficulty":"0x20000", "minimumDifficulty": "0x20000",
"difficultyBoundDivisor":"0x800", "difficultyBoundDivisor": "0x800",
"durationLimit":"0xd", "durationLimit": "0xd",
"blockReward":{ "blockReward": {
"0x0":"0x4563918244f40000", "0x0": "0x4563918244f40000",
"0x7530":"0x29a2241af62c0000", "0x7530": "0x29a2241af62c0000",
"0x9c40":"0x1bc16d674ec80000" "0x9c40": "0x1bc16d674ec80000"
}, },
"homesteadTransition":"0x2710", "difficultyBombDelays": {
"eip100bTransition":"0x7530", "0x7530": "0x2dc6c0",
"difficultyBombDelays":{ "0x9c40": "0x1e8480"
"0x7530":"0x2dc6c0", },
"0x9c40":"0x1e8480" "homesteadTransition": "0x2710",
} "eip100bTransition": "0x7530"
} }
} }
}, },
"params":{ "params": {
"accountStartNonce":"0x0", "accountStartNonce": "0x0",
"maximumExtraDataSize":"0x20", "maximumExtraDataSize": "0x20",
"gasLimitBoundDivisor":"0x400", "minGasLimit": "0x1388",
"minGasLimit":"0x1388", "gasLimitBoundDivisor": "0x400",
"networkID":"0x4cb2e", "networkID": "0x4cb2e",
"chainID":"0x4cb2e", "chainID": "0x4cb2e",
"maxCodeSize":"0x6000", "maxCodeSize": "0x6000",
"maxCodeSizeTransition":"0x0", "maxCodeSizeTransition": "0x0",
"eip98Transition": "0x7fffffffffffffff", "eip98Transition": "0x7fffffffffffffff",
"eip150Transition":"0x3a98", "eip150Transition": "0x3a98",
"eip160Transition":"0x59d8", "eip160Transition": "0x59d8",
"eip161abcTransition":"0x59d8", "eip161abcTransition": "0x59d8",
"eip161dTransition":"0x59d8", "eip161dTransition": "0x59d8",
"eip155Transition":"0x59d8", "eip155Transition": "0x59d8",
"eip140Transition":"0x7530", "eip140Transition": "0x7530",
"eip211Transition":"0x7530", "eip211Transition": "0x7530",
"eip214Transition":"0x7530", "eip214Transition": "0x7530",
"eip658Transition":"0x7530", "eip658Transition": "0x7530",
"eip145Transition":"0x9c40", "eip145Transition": "0x9c40",
"eip1014Transition":"0x9c40", "eip1014Transition": "0x9c40",
"eip1052Transition":"0x9c40", "eip1052Transition": "0x9c40",
"eip1283Transition":"0x9c40" "eip1283Transition": "0x9c40",
"eip1283DisableTransition": "0x9c40",
"eip1283ReenableTransition": "0xc350",
"eip1344Transition": "0xc350",
"eip1884Transition": "0xc350",
"eip2028Transition": "0xc350"
}, },
"genesis":{ "genesis": {
"seal":{ "seal": {
"ethereum":{ "ethereum": {
"nonce":"0x0000000000000000", "nonce": "0x0000000000000000",
"mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000" "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
} }
}, },
"difficulty":"0x20000", "difficulty": "0x20000",
"author":"0x0000000000000000000000000000000000000000", "author": "0x0000000000000000000000000000000000000000",
"timestamp":"0x59a4e76d", "timestamp": "0x59a4e76d",
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData":"0x0000000000000000000000000000000000000000000000000000000b4dc0ffee", "extraData": "0x0000000000000000000000000000000000000000000000000000000b4dc0ffee",
"gasLimit":"0x47b760" "gasLimit": "0x47b760"
}, },
"nodes":[ "nodes": [],
"enode://dfa7aca3f5b635fbfe7d0b20575f25e40d9e27b4bfbb3cf74364a42023ad9f25c1a4383bcc8cced86ee511a7d03415345a4df05be37f1dff040e4c780699f1c0@168.61.153.255:31303", "accounts": {
"enode://ef441b20dd70aeabf0eac35c3b8a2854e5ce04db0e30be9152ea9fd129359dcbb3f803993303ff5781c755dfd7223f3fe43505f583cccb740949407677412ba9@40.74.91.252:31303", "0000000000000000000000000000000000000001": {
"enode://953b5ea1c8987cf46008232a0160324fd00d41320ecf00e23af86ec8f5396b19eb57ddab37c78141be56f62e9077de4f4dfa0747fa768ed8c8531bbfb1046237@40.70.214.166:31303", "balance": "0x1",
"enode://276e613dd4b277a66591e565711e6c8bb107f0905248a9f8f8228c1a87992e156e5114bb9937c02824a9d9d25f76340442cf86e2028bf5293cae19904fb2b98e@35.178.251.52:30303", "builtin": {
"enode://064c820d41e52ed7d426ac64b60506c2998235bedc7e67cb497c6faf7bb4fc54fe56fc82d0add3180b747c0c4f40a1108a6f84d7d0629ed606d504528e61cc57@3.8.5.3:30303", "name": "ecrecover",
"enode://90069fdabcc5e684fa5d59430bebbb12755d9362dfe5006a1485b13d71a78a3812d36e74dd7d88e50b51add01e097ea80f16263aeaa4f0230db6c79e2a97e7ca@217.29.191.142:30303", "pricing": {
"enode://0aac74b7fd28726275e466acb5e03bc88a95927e9951eb66b5efb239b2f798ada0690853b2f2823fe4efa408f0f3d4dd258430bc952a5ff70677b8625b3e3b14@40.115.33.57:40404", "linear": {
"enode://0b96415a10f835106d83e090a0528eed5e7887e5c802a6d084e9f1993a9d0fc713781e6e4101f6365e9b91259712f291acc0a9e6e667e22023050d602c36fbe2@40.115.33.57:40414" "base": 3000,
], "word": 0
"accounts":{
"0000000000000000000000000000000000000001":{
"balance":"1",
"nonce":"0",
"builtin":{
"name":"ecrecover",
"pricing":{
"linear":{
"base":3000,
"word":0
} }
} }
} }
}, },
"0000000000000000000000000000000000000002":{ "0000000000000000000000000000000000000002": {
"balance":"1", "balance": "0x1",
"nonce":"0", "builtin": {
"builtin":{ "name": "sha256",
"name":"sha256", "pricing": {
"pricing":{ "linear": {
"linear":{ "base": 60,
"base":60, "word": 12
"word":12
} }
} }
} }
}, },
"0000000000000000000000000000000000000003":{ "0000000000000000000000000000000000000003": {
"balance":"1", "balance": "0x1",
"nonce":"0", "builtin": {
"builtin":{ "name": "ripemd160",
"name":"ripemd160", "pricing": {
"pricing":{ "linear": {
"linear":{ "base": 600,
"base":600, "word": 120
"word":120
} }
} }
} }
}, },
"0000000000000000000000000000000000000004":{ "0000000000000000000000000000000000000004": {
"balance":"1", "balance": "0x1",
"nonce":"0", "builtin": {
"builtin":{ "name": "identity",
"name":"identity", "pricing": {
"pricing":{ "linear": {
"linear":{ "base": 15,
"base":15, "word": 3
"word":3
} }
} }
} }
}, },
"0000000000000000000000000000000000000005":{ "0000000000000000000000000000000000000005": {
"balance":"1", "balance": "0x1",
"nonce":"0", "builtin": {
"builtin":{ "name": "modexp",
"name":"modexp", "pricing": {
"activate_at":"0x7530", "modexp": {
"pricing":{ "divisor": 20
"modexp":{
"divisor":20
} }
} },
"activate_at": "0x7530"
} }
}, },
"0000000000000000000000000000000000000006":{ "0000000000000000000000000000000000000006": {
"balance":"1", "balance": "0x1",
"nonce":"0", "builtin": {
"builtin":{ "name": "alt_bn128_add",
"name":"alt_bn128_add", "pricing": {
"activate_at":"0x7530", "alt_bn128_const_operations": {
"pricing":{ "price": 500,
"linear":{ "eip1108_transition_price": 150
"base":500,
"word":0
} }
} },
"activate_at": "0x7530",
"eip1108_transition": "0xc350"
} }
}, },
"0000000000000000000000000000000000000007":{ "0000000000000000000000000000000000000007": {
"balance":"1", "balance": "0x1",
"nonce":"0", "builtin": {
"builtin":{ "name": "alt_bn128_mul",
"name":"alt_bn128_mul", "pricing": {
"activate_at":"0x7530", "alt_bn128_const_operations": {
"pricing":{ "price": 40000,
"linear":{ "eip1108_transition_price": 6000
"base":40000,
"word":0
} }
} },
"activate_at": "0x7530",
"eip1108_transition": "0xc350"
} }
}, },
"0000000000000000000000000000000000000008":{ "0000000000000000000000000000000000000008": {
"balance":"1", "balance": "0x1",
"nonce":"0", "builtin": {
"builtin":{ "name": "alt_bn128_pairing",
"name":"alt_bn128_pairing", "pricing": {
"activate_at":"0x7530", "alt_bn128_pairing": {
"pricing":{ "base": 100000,
"alt_bn128_pairing":{ "pair": 80000,
"base":100000, "eip1108_transition_base": 45000,
"pair":80000 "eip1108_transition_pair": 34000
} }
} },
"activate_at": "0x7530",
"eip1108_transition": "0xc350"
}
},
"0000000000000000000000000000000000000009": {
"balance": "0x1",
"builtin": {
"name": "blake2_f",
"pricing": {
"blake2_f": {
"gas_per_round": 1
}
},
"activate_at": "0xc350"
} }
} }
} }
} }

View file

@ -77,7 +77,7 @@ func (w *wizard) deployDashboard() {
} }
case "explorer": case "explorer":
if infos, err := checkExplorer(client, w.network); err == nil { if infos, err := checkExplorer(client, w.network); err == nil {
port = infos.webPort port = infos.port
} }
case "wallet": case "wallet":
if infos, err := checkWallet(client, w.network); err == nil { if infos, err := checkWallet(client, w.network); err == nil {

View file

@ -35,10 +35,6 @@ func (w *wizard) deployExplorer() {
log.Error("No ethstats server configured") log.Error("No ethstats server configured")
return return
} }
if w.conf.Genesis.Config.Ethash == nil {
log.Error("Only ethash network supported")
return
}
// Select the server to interact with // Select the server to interact with
server := w.selectServer() server := w.selectServer()
if server == "" { if server == "" {
@ -50,50 +46,57 @@ func (w *wizard) deployExplorer() {
infos, err := checkExplorer(client, w.network) infos, err := checkExplorer(client, w.network)
if err != nil { if err != nil {
infos = &explorerInfos{ infos = &explorerInfos{
nodePort: 30303, webPort: 80, webHost: client.server, node: &nodeInfos{port: 30303},
port: 80,
host: client.server,
} }
} }
existed := err == nil existed := err == nil
chainspec, err := newParityChainSpec(w.network, w.conf.Genesis, w.conf.bootnodes) infos.node.genesis, _ = json.MarshalIndent(w.conf.Genesis, "", " ")
if err != nil { infos.node.network = w.conf.Genesis.Config.ChainID.Int64()
log.Error("Failed to create chain spec for explorer", "err", err)
return
}
chain, _ := json.MarshalIndent(chainspec, "", " ")
// Figure out which port to listen on // Figure out which port to listen on
fmt.Println() fmt.Println()
fmt.Printf("Which port should the explorer listen on? (default = %d)\n", infos.webPort) fmt.Printf("Which port should the explorer listen on? (default = %d)\n", infos.port)
infos.webPort = w.readDefaultInt(infos.webPort) infos.port = w.readDefaultInt(infos.port)
// Figure which virtual-host to deploy ethstats on // Figure which virtual-host to deploy ethstats on
if infos.webHost, err = w.ensureVirtualHost(client, infos.webPort, infos.webHost); err != nil { if infos.host, err = w.ensureVirtualHost(client, infos.port, infos.host); err != nil {
log.Error("Failed to decide on explorer host", "err", err) log.Error("Failed to decide on explorer host", "err", err)
return return
} }
// Figure out where the user wants to store the persistent data // Figure out where the user wants to store the persistent data
fmt.Println() fmt.Println()
if infos.datadir == "" { if infos.node.datadir == "" {
fmt.Printf("Where should data be stored on the remote machine?\n") fmt.Printf("Where should node data be stored on the remote machine?\n")
infos.datadir = w.readString() infos.node.datadir = w.readString()
} else { } else {
fmt.Printf("Where should data be stored on the remote machine? (default = %s)\n", infos.datadir) fmt.Printf("Where should node data be stored on the remote machine? (default = %s)\n", infos.node.datadir)
infos.datadir = w.readDefaultString(infos.datadir) infos.node.datadir = w.readDefaultString(infos.node.datadir)
}
// Figure out where the user wants to store the persistent data for backend database
fmt.Println()
if infos.dbdir == "" {
fmt.Printf("Where should postgres data be stored on the remote machine?\n")
infos.dbdir = w.readString()
} else {
fmt.Printf("Where should postgres data be stored on the remote machine? (default = %s)\n", infos.dbdir)
infos.dbdir = w.readDefaultString(infos.dbdir)
} }
// 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 archive node listen on? (default = %d)\n", infos.nodePort) fmt.Printf("Which TCP/UDP port should the archive node listen on? (default = %d)\n", infos.node.port)
infos.nodePort = w.readDefaultInt(infos.nodePort) 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()
if infos.ethstats == "" { if infos.node.ethstats == "" {
fmt.Printf("What should the explorer be called on the stats page?\n") fmt.Printf("What should the explorer be called on the stats page?\n")
infos.ethstats = w.readString() + ":" + w.conf.ethstats infos.node.ethstats = w.readString() + ":" + w.conf.ethstats
} else { } else {
fmt.Printf("What should the explorer be called on the stats page? (default = %s)\n", infos.ethstats) fmt.Printf("What should the explorer be called on the stats page? (default = %s)\n", infos.node.ethstats)
infos.ethstats = w.readDefaultString(infos.ethstats) + ":" + w.conf.ethstats infos.node.ethstats = w.readDefaultString(infos.node.ethstats) + ":" + w.conf.ethstats
} }
// Try to deploy the explorer on the host // Try to deploy the explorer on the host
nocache := false nocache := false
@ -102,7 +105,7 @@ func (w *wizard) deployExplorer() {
fmt.Printf("Should the explorer be built from scratch (y/n)? (default = no)\n") fmt.Printf("Should the explorer be built from scratch (y/n)? (default = no)\n")
nocache = w.readDefaultYesNo(false) nocache = w.readDefaultYesNo(false)
} }
if out, err := deployExplorer(client, w.network, chain, infos, nocache); err != nil { if out, err := deployExplorer(client, w.network, w.conf.bootnodes, infos, nocache, w.conf.Genesis.Config.Clique != nil); err != nil {
log.Error("Failed to deploy explorer container", "err", err) log.Error("Failed to deploy explorer container", "err", err)
if len(out) > 0 { if len(out) > 0 {
fmt.Printf("%s\n", out) fmt.Printf("%s\n", out)

View file

@ -147,7 +147,7 @@ func (w *wizard) deployFaucet() {
infos.node.keyPass = w.readPassword() infos.node.keyPass = w.readPassword()
if _, err := keystore.DecryptKey([]byte(infos.node.keyJSON), infos.node.keyPass); err != nil { if _, err := keystore.DecryptKey([]byte(infos.node.keyJSON), infos.node.keyPass); err != nil {
log.Error("Failed to decrypt key with given passphrase") log.Error("Failed to decrypt key with given password")
infos.node.keyJSON = "" infos.node.keyJSON = ""
infos.node.keyPass = "" infos.node.keyPass = ""
} }

View file

@ -51,6 +51,7 @@ func (w *wizard) makeGenesis() {
ByzantiumBlock: big.NewInt(0), ByzantiumBlock: big.NewInt(0),
ConstantinopleBlock: big.NewInt(0), ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
}, },
} }
// Figure out which consensus engine to choose // Figure out which consensus engine to choose
@ -230,6 +231,10 @@ func (w *wizard) manageGenesis() {
fmt.Printf("Which block should Petersburg come into effect? (default = %v)\n", w.conf.Genesis.Config.PetersburgBlock) fmt.Printf("Which block should Petersburg come into effect? (default = %v)\n", w.conf.Genesis.Config.PetersburgBlock)
w.conf.Genesis.Config.PetersburgBlock = w.readDefaultBigInt(w.conf.Genesis.Config.PetersburgBlock) w.conf.Genesis.Config.PetersburgBlock = w.readDefaultBigInt(w.conf.Genesis.Config.PetersburgBlock)
fmt.Println()
fmt.Printf("Which block should Istanbul come into effect? (default = %v)\n", w.conf.Genesis.Config.IstanbulBlock)
w.conf.Genesis.Config.IstanbulBlock = w.readDefaultBigInt(w.conf.Genesis.Config.IstanbulBlock)
out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ") out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ")
fmt.Printf("Chain configuration updated:\n\n%s\n", out) fmt.Printf("Chain configuration updated:\n\n%s\n", out)
@ -268,7 +273,7 @@ func (w *wizard) manageGenesis() {
} else { } else {
saveGenesis(folder, w.network, "parity", spec) saveGenesis(folder, w.network, "parity", spec)
} }
// Export the genesis spec used by Harmony (formerly EthereumJ // Export the genesis spec used by Harmony (formerly EthereumJ)
saveGenesis(folder, w.network, "harmony", w.conf.Genesis) saveGenesis(folder, w.network, "harmony", w.conf.Genesis)
case "3": case "3":
@ -291,7 +296,7 @@ func (w *wizard) manageGenesis() {
func saveGenesis(folder, network, client string, spec interface{}) { func saveGenesis(folder, network, client string, spec interface{}) {
path := filepath.Join(folder, fmt.Sprintf("%s-%s.json", network, client)) path := filepath.Join(folder, fmt.Sprintf("%s-%s.json", network, client))
out, _ := json.Marshal(spec) out, _ := json.MarshalIndent(spec, "", " ")
if err := ioutil.WriteFile(path, out, 0644); err != nil { if err := ioutil.WriteFile(path, out, 0644); err != nil {
log.Error("Failed to save genesis file", "client", client, "err", err) log.Error("Failed to save genesis file", "client", client, "err", err)
return return

View file

@ -174,7 +174,7 @@ func (w *wizard) deployComponent() {
fmt.Println(" 1. Ethstats - Network monitoring tool") fmt.Println(" 1. Ethstats - Network monitoring tool")
fmt.Println(" 2. Bootnode - Entry point of the network") fmt.Println(" 2. Bootnode - Entry point of the network")
fmt.Println(" 3. Sealer - Full node minting new blocks") fmt.Println(" 3. Sealer - Full node minting new blocks")
fmt.Println(" 4. Explorer - Chain analysis webservice (ethash only)") fmt.Println(" 4. Explorer - Chain analysis webservice")
fmt.Println(" 5. Wallet - Browser wallet for quick sends") fmt.Println(" 5. Wallet - Browser wallet for quick sends")
fmt.Println(" 6. Faucet - Crypto faucet to give away funds") fmt.Println(" 6. Faucet - Crypto faucet to give away funds")
fmt.Println(" 7. Dashboard - Website listing above web-services") fmt.Println(" 7. Dashboard - Website listing above web-services")

View file

@ -142,7 +142,7 @@ func (w *wizard) deployNode(boot bool) {
infos.keyPass = w.readPassword() infos.keyPass = w.readPassword()
if _, err := keystore.DecryptKey([]byte(infos.keyJSON), infos.keyPass); err != nil { if _, err := keystore.DecryptKey([]byte(infos.keyJSON), infos.keyPass); err != nil {
log.Error("Failed to decrypt key with given passphrase") log.Error("Failed to decrypt key with given password")
return return
} }
} }

View file

@ -20,7 +20,6 @@ import (
"encoding" "encoding"
"errors" "errors"
"flag" "flag"
"fmt"
"math/big" "math/big"
"os" "os"
"os/user" "os/user"
@ -34,33 +33,44 @@ import (
// Custom type which is registered in the flags library which cli uses for // Custom type which is registered in the flags library which cli uses for
// argument parsing. This allows us to expand Value to an absolute path when // argument parsing. This allows us to expand Value to an absolute path when
// the argument is parsed // the argument is parsed
type DirectoryString struct { type DirectoryString string
Value string
func (s *DirectoryString) String() string {
return string(*s)
} }
func (self *DirectoryString) String() string { func (s *DirectoryString) Set(value string) error {
return self.Value *s = DirectoryString(expandPath(value))
}
func (self *DirectoryString) Set(value string) error {
self.Value = expandPath(value)
return nil return nil
} }
// Custom cli.Flag type which expand the received string to an absolute path. // Custom cli.Flag type which expand the received string to an absolute path.
// e.g. ~/.ethereum -> /home/username/.ethereum // e.g. ~/.ethereum -> /home/username/.ethereum
type DirectoryFlag struct { type DirectoryFlag struct {
Name string Name string
Value DirectoryString Value DirectoryString
Usage string Usage string
EnvVar string
} }
func (self DirectoryFlag) String() string { func (f DirectoryFlag) String() string {
fmtString := "%s %v\t%v" return cli.FlagStringer(f)
if len(self.Value.Value) > 0 { }
fmtString = "%s \"%v\"\t%v"
} // called by cli library, grabs variable from environment (if in env)
return fmt.Sprintf(fmtString, prefixedNames(self.Name), self.Value.Value, self.Usage) // and adds variable to flag set for parsing.
func (f DirectoryFlag) Apply(set *flag.FlagSet) {
eachName(f.Name, func(name string) {
set.Var(&f.Value, f.Name, f.Usage)
})
}
func (f DirectoryFlag) GetName() string {
return f.Name
}
func (f *DirectoryFlag) Set(value string) {
f.Value.Set(value)
} }
func eachName(longName string, fn func(string)) { func eachName(longName string, fn func(string)) {
@ -71,14 +81,6 @@ func eachName(longName string, fn func(string)) {
} }
} }
// called by cli library, grabs variable from environment (if in env)
// and adds variable to flag set for parsing.
func (self DirectoryFlag) Apply(set *flag.FlagSet) {
eachName(self.Name, func(name string) {
set.Var(&self.Value, self.Name, self.Usage)
})
}
type TextMarshaler interface { type TextMarshaler interface {
encoding.TextMarshaler encoding.TextMarshaler
encoding.TextUnmarshaler encoding.TextUnmarshaler
@ -103,9 +105,10 @@ func (v textMarshalerVal) Set(s string) error {
// TextMarshalerFlag wraps a TextMarshaler value. // TextMarshalerFlag wraps a TextMarshaler value.
type TextMarshalerFlag struct { type TextMarshalerFlag struct {
Name string Name string
Value TextMarshaler Value TextMarshaler
Usage string Usage string
EnvVar string
} }
func (f TextMarshalerFlag) GetName() string { func (f TextMarshalerFlag) GetName() string {
@ -113,7 +116,7 @@ func (f TextMarshalerFlag) GetName() string {
} }
func (f TextMarshalerFlag) String() string { func (f TextMarshalerFlag) String() string {
return fmt.Sprintf("%s \"%v\"\t%v", prefixedNames(f.Name), f.Value, f.Usage) return cli.FlagStringer(f)
} }
func (f TextMarshalerFlag) Apply(set *flag.FlagSet) { func (f TextMarshalerFlag) Apply(set *flag.FlagSet) {
@ -134,9 +137,10 @@ func GlobalTextMarshaler(ctx *cli.Context, name string) TextMarshaler {
// BigFlag is a command line flag that accepts 256 bit big integers in decimal or // BigFlag is a command line flag that accepts 256 bit big integers in decimal or
// hexadecimal syntax. // hexadecimal syntax.
type BigFlag struct { type BigFlag struct {
Name string Name string
Value *big.Int Value *big.Int
Usage string Usage string
EnvVar string
} }
// bigValue turns *big.Int into a flag.Value // bigValue turns *big.Int into a flag.Value
@ -163,11 +167,7 @@ func (f BigFlag) GetName() string {
} }
func (f BigFlag) String() string { func (f BigFlag) String() string {
fmtString := "%s %v\t%v" return cli.FlagStringer(f)
if f.Value != nil {
fmtString = "%s \"%v\"\t%v"
}
return fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage)
} }
func (f BigFlag) Apply(set *flag.FlagSet) { func (f BigFlag) Apply(set *flag.FlagSet) {
@ -207,14 +207,6 @@ func prefixedNames(fullName string) (prefixed string) {
return return
} }
func (self DirectoryFlag) GetName() string {
return self.Name
}
func (self *DirectoryFlag) Set(value string) {
self.Value.Value = value
}
// Expands a file path // Expands a file path
// 1. replace tilde with users home dir // 1. replace tilde with users home dir
// 2. expands embedded environment variables // 2. expands embedded environment variables

View file

@ -19,15 +19,17 @@ package utils
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"math/big" "math/big"
"os" "os"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"text/tabwriter"
"text/template"
"time" "time"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
@ -91,8 +93,8 @@ GLOBAL OPTIONS:
{{range .Flags}}{{.}} {{range .Flags}}{{.}}
{{end}}{{end}} {{end}}{{end}}
` `
cli.CommandHelpTemplate = CommandHelpTemplate cli.CommandHelpTemplate = CommandHelpTemplate
cli.HelpPrinter = printHelp
} }
// NewApp creates an app with sane defaults. // NewApp creates an app with sane defaults.
@ -100,13 +102,23 @@ func NewApp(gitCommit, gitDate, usage string) *cli.App {
app := cli.NewApp() app := cli.NewApp()
app.Name = filepath.Base(os.Args[0]) app.Name = filepath.Base(os.Args[0])
app.Author = "" app.Author = ""
//app.Authors = nil
app.Email = "" app.Email = ""
app.Version = params.VersionWithCommit(gitCommit, gitDate) app.Version = params.VersionWithCommit(gitCommit, gitDate)
app.Usage = usage app.Usage = usage
return app return app
} }
func printHelp(out io.Writer, templ string, data interface{}) {
funcMap := template.FuncMap{"join": strings.Join}
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
w := tabwriter.NewWriter(out, 38, 8, 2, ' ', 0)
err := t.Execute(w, data)
if err != nil {
panic(err)
}
w.Flush()
}
// These are all the command line flags we support. // These are all the command line flags we support.
// If you add to this list, please remember to include the // If you add to this list, please remember to include the
// flag in the appropriate command definition. // flag in the appropriate command definition.
@ -119,7 +131,7 @@ var (
DataDirFlag = DirectoryFlag{ DataDirFlag = DirectoryFlag{
Name: "datadir", Name: "datadir",
Usage: "Data directory for the databases and keystore", Usage: "Data directory for the databases and keystore",
Value: DirectoryString{node.DefaultDataDir()}, Value: DirectoryString(node.DefaultDataDir()),
} }
AncientFlag = DirectoryFlag{ AncientFlag = DirectoryFlag{
Name: "datadir.ancient", Name: "datadir.ancient",
@ -170,28 +182,12 @@ var (
DocRootFlag = DirectoryFlag{ DocRootFlag = DirectoryFlag{
Name: "docroot", Name: "docroot",
Usage: "Document Root for HTTPClient file scheme", Usage: "Document Root for HTTPClient file scheme",
Value: DirectoryString{homeDir()}, Value: DirectoryString(homeDir()),
} }
ExitWhenSyncedFlag = cli.BoolFlag{ ExitWhenSyncedFlag = cli.BoolFlag{
Name: "exitwhensynced", Name: "exitwhensynced",
Usage: "Exits after block synchronisation completes", Usage: "Exits after block synchronisation completes",
} }
ULCModeConfigFlag = cli.StringFlag{
Name: "ulc.config",
Usage: "Config file to use for ultra light client mode",
}
OnlyAnnounceModeFlag = cli.BoolFlag{
Name: "ulc.onlyannounce",
Usage: "ULC server sends announcements only",
}
ULCMinTrustedFractionFlag = cli.IntFlag{
Name: "ulc.fraction",
Usage: "Minimum % of trusted ULC servers required to announce a new head",
}
ULCTrustedNodesFlag = cli.StringFlag{
Name: "ulc.trusted",
Usage: "List of trusted ULC servers",
}
IterativeOutputFlag = cli.BoolFlag{ IterativeOutputFlag = cli.BoolFlag{
Name: "iterative", Name: "iterative",
Usage: "Print streaming JSON iteratively, delimited by newlines", Usage: "Print streaming JSON iteratively, delimited by newlines",
@ -219,26 +215,6 @@ var (
Usage: `Blockchain garbage collection mode ("full", "archive")`, Usage: `Blockchain garbage collection mode ("full", "archive")`,
Value: "full", Value: "full",
} }
LightServFlag = cli.IntFlag{
Name: "lightserv",
Usage: "Maximum percentage of time allowed for serving LES requests (multi-threaded processing allows values over 100)",
Value: 0,
}
LightBandwidthInFlag = cli.IntFlag{
Name: "lightbwin",
Usage: "Incoming bandwidth limit for light server (kilobytes/sec, 0 = unlimited)",
Value: 0,
}
LightBandwidthOutFlag = cli.IntFlag{
Name: "lightbwout",
Usage: "Outgoing bandwidth limit for light server (kilobytes/sec, 0 = unlimited)",
Value: 0,
}
LightPeersFlag = cli.IntFlag{
Name: "lightpeers",
Usage: "Maximum number of LES client peers",
Value: eth.DefaultConfig.LightPeers,
}
LightKDFFlag = cli.BoolFlag{ LightKDFFlag = cli.BoolFlag{
Name: "lightkdf", Name: "lightkdf",
Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
@ -247,6 +223,55 @@ var (
Name: "whitelist", Name: "whitelist",
Usage: "Comma separated block number-to-hash mappings to enforce (<number>=<hash>)", Usage: "Comma separated block number-to-hash mappings to enforce (<number>=<hash>)",
} }
OverrideIstanbulFlag = cli.Uint64Flag{
Name: "override.istanbul",
Usage: "Manually specify Istanbul fork-block, overriding the bundled setting",
}
// Light server and client settings
LightLegacyServFlag = cli.IntFlag{ // Deprecated in favor of light.serve, remove in 2021
Name: "lightserv",
Usage: "Maximum percentage of time allowed for serving LES requests (deprecated, use --light.serve)",
Value: eth.DefaultConfig.LightServ,
}
LightServeFlag = cli.IntFlag{
Name: "light.serve",
Usage: "Maximum percentage of time allowed for serving LES requests (multi-threaded processing allows values over 100)",
Value: eth.DefaultConfig.LightServ,
}
LightIngressFlag = cli.IntFlag{
Name: "light.ingress",
Usage: "Incoming bandwidth limit for serving light clients (kilobytes/sec, 0 = unlimited)",
Value: eth.DefaultConfig.LightIngress,
}
LightEgressFlag = cli.IntFlag{
Name: "light.egress",
Usage: "Outgoing bandwidth limit for serving light clients (kilobytes/sec, 0 = unlimited)",
Value: eth.DefaultConfig.LightEgress,
}
LightLegacyPeersFlag = cli.IntFlag{ // Deprecated in favor of light.maxpeers, remove in 2021
Name: "lightpeers",
Usage: "Maximum number of light clients to serve, or light servers to attach to (deprecated, use --light.maxpeers)",
Value: eth.DefaultConfig.LightPeers,
}
LightMaxPeersFlag = cli.IntFlag{
Name: "light.maxpeers",
Usage: "Maximum number of light clients to serve, or light servers to attach to",
Value: eth.DefaultConfig.LightPeers,
}
UltraLightServersFlag = cli.StringFlag{
Name: "ulc.servers",
Usage: "List of trusted ultra-light servers",
Value: strings.Join(eth.DefaultConfig.UltraLightServers, ","),
}
UltraLightFractionFlag = cli.IntFlag{
Name: "ulc.fraction",
Usage: "Minimum % of trusted ultra-light servers required to announce a new head",
Value: eth.DefaultConfig.UltraLightFraction,
}
UltraLightOnlyAnnounceFlag = cli.BoolFlag{
Name: "ulc.onlyannounce",
Usage: "Ultra light server sends announcements only",
}
// Dashboard settings // Dashboard settings
DashboardEnabledFlag = cli.BoolFlag{ DashboardEnabledFlag = cli.BoolFlag{
Name: "dashboard", Name: "dashboard",
@ -284,8 +309,8 @@ var (
} }
EthashDatasetDirFlag = DirectoryFlag{ EthashDatasetDirFlag = DirectoryFlag{
Name: "ethash.dagdir", Name: "ethash.dagdir",
Usage: "Directory to store the ethash mining DAGs (default = inside home folder)", Usage: "Directory to store the ethash mining DAGs",
Value: DirectoryString{eth.DefaultConfig.Ethash.DatasetDir}, Value: DirectoryString(eth.DefaultConfig.Ethash.DatasetDir),
} }
EthashDatasetsInMemoryFlag = cli.IntFlag{ EthashDatasetsInMemoryFlag = cli.IntFlag{
Name: "ethash.dagsinmem", Name: "ethash.dagsinmem",
@ -944,7 +969,7 @@ func setWS(ctx *cli.Context, cfg *node.Config) {
// setIPC creates an IPC path configuration from the set command line flags, // setIPC creates an IPC path configuration from the set command line flags,
// returning an empty string if IPC was explicitly disabled, or the set path. // returning an empty string if IPC was explicitly disabled, or the set path.
func setIPC(ctx *cli.Context, cfg *node.Config) { func setIPC(ctx *cli.Context, cfg *node.Config) {
checkExclusive(ctx, IPCDisabledFlag, IPCPathFlag) CheckExclusive(ctx, IPCDisabledFlag, IPCPathFlag)
switch { switch {
case ctx.GlobalBool(IPCDisabledFlag.Name): case ctx.GlobalBool(IPCDisabledFlag.Name):
cfg.IPCPath = "" cfg.IPCPath = ""
@ -953,37 +978,38 @@ func setIPC(ctx *cli.Context, cfg *node.Config) {
} }
} }
// SetULC setup ULC config from file if given. // setLes configures the les server and ultra light client settings from the command line flags.
func SetULC(ctx *cli.Context, cfg *eth.Config) { func setLes(ctx *cli.Context, cfg *eth.Config) {
// ULC config isn't loaded from global config and ULC config and ULC trusted nodes are not defined. if ctx.GlobalIsSet(LightLegacyServFlag.Name) {
if cfg.ULC == nil && !(ctx.GlobalIsSet(ULCModeConfigFlag.Name) || ctx.GlobalIsSet(ULCTrustedNodesFlag.Name)) { cfg.LightServ = ctx.GlobalInt(LightLegacyServFlag.Name)
return
} }
cfg.ULC = &eth.ULCConfig{} if ctx.GlobalIsSet(LightServeFlag.Name) {
cfg.LightServ = ctx.GlobalInt(LightServeFlag.Name)
path := ctx.GlobalString(ULCModeConfigFlag.Name)
if path != "" {
cfgData, err := ioutil.ReadFile(path)
if err != nil {
Fatalf("Failed to unmarshal ULC configuration: %v", err)
}
err = json.Unmarshal(cfgData, &cfg.ULC)
if err != nil {
Fatalf("Failed to unmarshal ULC configuration: %s", err.Error())
}
} }
if ctx.GlobalIsSet(LightIngressFlag.Name) {
if trustedNodes := ctx.GlobalString(ULCTrustedNodesFlag.Name); trustedNodes != "" { cfg.LightIngress = ctx.GlobalInt(LightIngressFlag.Name)
cfg.ULC.TrustedServers = strings.Split(trustedNodes, ",")
} }
if ctx.GlobalIsSet(LightEgressFlag.Name) {
if trustedFraction := ctx.GlobalInt(ULCMinTrustedFractionFlag.Name); trustedFraction > 0 { cfg.LightEgress = ctx.GlobalInt(LightEgressFlag.Name)
cfg.ULC.MinTrustedFraction = trustedFraction
} }
if cfg.ULC.MinTrustedFraction <= 0 && cfg.ULC.MinTrustedFraction > 100 { if ctx.GlobalIsSet(LightLegacyPeersFlag.Name) {
log.Error("MinTrustedFraction is invalid", "MinTrustedFraction", cfg.ULC.MinTrustedFraction, "Changed to default", eth.DefaultULCMinTrustedFraction) cfg.LightPeers = ctx.GlobalInt(LightLegacyPeersFlag.Name)
cfg.ULC.MinTrustedFraction = eth.DefaultULCMinTrustedFraction }
if ctx.GlobalIsSet(LightMaxPeersFlag.Name) {
cfg.LightPeers = ctx.GlobalInt(LightMaxPeersFlag.Name)
}
if ctx.GlobalIsSet(UltraLightServersFlag.Name) {
cfg.UltraLightServers = strings.Split(ctx.GlobalString(UltraLightServersFlag.Name), ",")
}
if ctx.GlobalIsSet(UltraLightFractionFlag.Name) {
cfg.UltraLightFraction = ctx.GlobalInt(UltraLightFractionFlag.Name)
}
if cfg.UltraLightFraction <= 0 && cfg.UltraLightFraction > 100 {
log.Error("Ultra light fraction is invalid", "had", cfg.UltraLightFraction, "updated", eth.DefaultConfig.UltraLightFraction)
cfg.UltraLightFraction = eth.DefaultConfig.UltraLightFraction
}
if ctx.GlobalIsSet(UltraLightOnlyAnnounceFlag.Name) {
cfg.UltraLightOnlyAnnounce = ctx.GlobalBool(UltraLightOnlyAnnounceFlag.Name)
} }
} }
@ -1077,19 +1103,27 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
setBootstrapNodesV5(ctx, cfg) setBootstrapNodesV5(ctx, cfg)
lightClient := ctx.GlobalString(SyncModeFlag.Name) == "light" lightClient := ctx.GlobalString(SyncModeFlag.Name) == "light"
lightServer := ctx.GlobalInt(LightServFlag.Name) != 0 lightServer := (ctx.GlobalInt(LightLegacyServFlag.Name) != 0 || ctx.GlobalInt(LightServeFlag.Name) != 0)
lightPeers := ctx.GlobalInt(LightPeersFlag.Name)
lightPeers := ctx.GlobalInt(LightLegacyPeersFlag.Name)
if ctx.GlobalIsSet(LightMaxPeersFlag.Name) {
lightPeers = ctx.GlobalInt(LightMaxPeersFlag.Name)
}
if lightClient && !ctx.GlobalIsSet(LightLegacyPeersFlag.Name) && !ctx.GlobalIsSet(LightMaxPeersFlag.Name) {
// dynamic default - for clients we use 1/10th of the default for servers
lightPeers /= 10
}
if ctx.GlobalIsSet(MaxPeersFlag.Name) { if ctx.GlobalIsSet(MaxPeersFlag.Name) {
cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name) cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name)
if lightServer && !ctx.GlobalIsSet(LightPeersFlag.Name) { if lightServer && !ctx.GlobalIsSet(LightLegacyPeersFlag.Name) && !ctx.GlobalIsSet(LightMaxPeersFlag.Name) {
cfg.MaxPeers += lightPeers cfg.MaxPeers += lightPeers
} }
} else { } else {
if lightServer { if lightServer {
cfg.MaxPeers += lightPeers cfg.MaxPeers += lightPeers
} }
if lightClient && ctx.GlobalIsSet(LightPeersFlag.Name) && cfg.MaxPeers < lightPeers { if lightClient && (ctx.GlobalIsSet(LightLegacyPeersFlag.Name) || ctx.GlobalIsSet(LightMaxPeersFlag.Name)) && cfg.MaxPeers < lightPeers {
cfg.MaxPeers = lightPeers cfg.MaxPeers = lightPeers
} }
} }
@ -1191,11 +1225,11 @@ func setDataDir(ctx *cli.Context, cfg *node.Config) {
cfg.DataDir = ctx.GlobalString(DataDirFlag.Name) cfg.DataDir = ctx.GlobalString(DataDirFlag.Name)
case ctx.GlobalBool(DeveloperFlag.Name): case ctx.GlobalBool(DeveloperFlag.Name):
cfg.DataDir = "" // unless explicitly requested, use memory databases cfg.DataDir = "" // unless explicitly requested, use memory databases
case ctx.GlobalBool(TestnetFlag.Name): case ctx.GlobalBool(TestnetFlag.Name) && cfg.DataDir == node.DefaultDataDir():
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "testnet") cfg.DataDir = filepath.Join(node.DefaultDataDir(), "testnet")
case ctx.GlobalBool(RinkebyFlag.Name): case ctx.GlobalBool(RinkebyFlag.Name) && cfg.DataDir == node.DefaultDataDir():
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby") cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby")
case ctx.GlobalBool(GoerliFlag.Name): case ctx.GlobalBool(GoerliFlag.Name) && cfg.DataDir == node.DefaultDataDir():
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli") cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli")
} }
} }
@ -1329,10 +1363,10 @@ func setWhitelist(ctx *cli.Context, cfg *eth.Config) {
} }
} }
// checkExclusive verifies that only a single instance of the provided flags was // CheckExclusive verifies that only a single instance of the provided flags was
// set by the user. Each flag might optionally be followed by a string type to // set by the user. Each flag might optionally be followed by a string type to
// specialize it further. // specialize it further.
func checkExclusive(ctx *cli.Context, args ...interface{}) { func CheckExclusive(ctx *cli.Context, args ...interface{}) {
set := make([]string, 0, 1) set := make([]string, 0, 1)
for i := 0; i < len(args); i++ { for i := 0; i < len(args); i++ {
// Make sure the next argument is a flag and skip if not set // Make sure the next argument is a flag and skip if not set
@ -1386,10 +1420,10 @@ func SetShhConfig(ctx *cli.Context, stack *node.Node, cfg *whisper.Config) {
// SetEthConfig applies eth-related command line flags to the config. // SetEthConfig applies eth-related command line flags to the config.
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
// Avoid conflicting network flags // Avoid conflicting network flags
checkExclusive(ctx, DeveloperFlag, TestnetFlag, RinkebyFlag, GoerliFlag) CheckExclusive(ctx, DeveloperFlag, TestnetFlag, RinkebyFlag, GoerliFlag)
checkExclusive(ctx, LightServFlag, SyncModeFlag, "light") CheckExclusive(ctx, LightLegacyServFlag, LightServeFlag, SyncModeFlag, "light")
// Can't use both ephemeral unlocked and external signer CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
checkExclusive(ctx, DeveloperFlag, ExternalSignerFlag)
var ks *keystore.KeyStore var ks *keystore.KeyStore
if keystores := stack.AccountManager().Backends(keystore.KeyStoreType); len(keystores) > 0 { if keystores := stack.AccountManager().Backends(keystore.KeyStoreType); len(keystores) > 0 {
ks = keystores[0].(*keystore.KeyStore) ks = keystores[0].(*keystore.KeyStore)
@ -1400,21 +1434,11 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
setEthash(ctx, cfg) setEthash(ctx, cfg)
setMiner(ctx, &cfg.Miner) setMiner(ctx, &cfg.Miner)
setWhitelist(ctx, cfg) setWhitelist(ctx, cfg)
setLes(ctx, cfg)
if ctx.GlobalIsSet(SyncModeFlag.Name) { if ctx.GlobalIsSet(SyncModeFlag.Name) {
cfg.SyncMode = *GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode) cfg.SyncMode = *GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode)
} }
if ctx.GlobalIsSet(LightServFlag.Name) {
cfg.LightServ = ctx.GlobalInt(LightServFlag.Name)
}
cfg.LightBandwidthIn = ctx.GlobalInt(LightBandwidthInFlag.Name)
cfg.LightBandwidthOut = ctx.GlobalInt(LightBandwidthOutFlag.Name)
if ctx.GlobalIsSet(LightPeersFlag.Name) {
cfg.LightPeers = ctx.GlobalInt(LightPeersFlag.Name)
}
if ctx.GlobalIsSet(OnlyAnnounceModeFlag.Name) {
cfg.OnlyAnnounce = ctx.GlobalBool(OnlyAnnounceModeFlag.Name)
}
if ctx.GlobalIsSet(NetworkIdFlag.Name) { if ctx.GlobalIsSet(NetworkIdFlag.Name) {
cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name) cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name)
} }
@ -1429,9 +1453,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" { if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name) Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
} }
cfg.NoPruning = ctx.GlobalString(GCModeFlag.Name) == "archive" if ctx.GlobalIsSet(GCModeFlag.Name) {
cfg.NoPrefetch = ctx.GlobalBool(CacheNoPrefetchFlag.Name) cfg.NoPruning = ctx.GlobalString(GCModeFlag.Name) == "archive"
}
if ctx.GlobalIsSet(CacheNoPrefetchFlag.Name) {
cfg.NoPrefetch = ctx.GlobalBool(CacheNoPrefetchFlag.Name)
}
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheTrieFlag.Name) { if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheTrieFlag.Name) {
cfg.TrieCleanCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheTrieFlag.Name) / 100 cfg.TrieCleanCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheTrieFlag.Name) / 100
} }

View file

@ -356,7 +356,7 @@ func configureNode() {
if len(symPass) == 0 { if len(symPass) == 0 {
symPass, err = console.Stdin.PromptPassword("Please enter the password for symmetric encryption: ") symPass, err = console.Stdin.PromptPassword("Please enter the password for symmetric encryption: ")
if err != nil { if err != nil {
utils.Fatalf("Failed to read passphrase: %v", err) utils.Fatalf("Failed to read password: %v", err)
} }
} }

View file

@ -190,6 +190,8 @@ func benchmarkBaseOR(b *testing.B, size int) {
} }
} }
var GloBool bool // Exported global will not be dead-code eliminated, at least not yet.
// Benchmarks the potentially optimized bit testing performance. // Benchmarks the potentially optimized bit testing performance.
func BenchmarkFastTest1KB(b *testing.B) { benchmarkFastTest(b, 1024) } func BenchmarkFastTest1KB(b *testing.B) { benchmarkFastTest(b, 1024) }
func BenchmarkFastTest2KB(b *testing.B) { benchmarkFastTest(b, 2048) } func BenchmarkFastTest2KB(b *testing.B) { benchmarkFastTest(b, 2048) }
@ -197,9 +199,11 @@ func BenchmarkFastTest4KB(b *testing.B) { benchmarkFastTest(b, 4096) }
func benchmarkFastTest(b *testing.B, size int) { func benchmarkFastTest(b *testing.B, size int) {
p := make([]byte, size) p := make([]byte, size)
a := false
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
TestBytes(p) a = a != TestBytes(p)
} }
GloBool = a // Use of benchmark "result" to prevent total dead code elimination.
} }
// Benchmarks the baseline bit testing performance. // Benchmarks the baseline bit testing performance.
@ -209,7 +213,9 @@ func BenchmarkBaseTest4KB(b *testing.B) { benchmarkBaseTest(b, 4096) }
func benchmarkBaseTest(b *testing.B, size int) { func benchmarkBaseTest(b *testing.B, size int) {
p := make([]byte, size) p := make([]byte, size)
a := false
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
safeTestBytes(p) a = a != safeTestBytes(p)
} }
GloBool = a // Use of benchmark "result" to prevent total dead code elimination.
} }

View file

@ -43,10 +43,8 @@ func ToHexArray(b [][]byte) []string {
// FromHex returns the bytes represented by the hexadecimal string s. // FromHex returns the bytes represented by the hexadecimal string s.
// s may be prefixed with "0x". // s may be prefixed with "0x".
func FromHex(s string) []byte { func FromHex(s string) []byte {
if len(s) > 1 { if has0xPrefix(s) {
if s[0:2] == "0x" || s[0:2] == "0X" { s = s[2:]
s = s[2:]
}
} }
if len(s)%2 == 1 { if len(s)%2 == 1 {
s = "0" + s s = "0" + s
@ -65,8 +63,8 @@ func CopyBytes(b []byte) (copiedBytes []byte) {
return return
} }
// hasHexPrefix validates str begins with '0x' or '0X'. // has0xPrefix validates str begins with '0x' or '0X'.
func hasHexPrefix(str string) bool { func has0xPrefix(str string) bool {
return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X') return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')
} }
@ -136,3 +134,14 @@ func LeftPadBytes(slice []byte, l int) []byte {
return padded return padded
} }
// TrimLeftZeroes returns a subslice of s without leading zeroes
func TrimLeftZeroes(s []byte) []byte {
idx := 0
for ; idx < len(s); idx++ {
if s[idx] != 0 {
break
}
}
return s[idx:]
}

View file

@ -1,4 +1,4 @@
// Copyright 2019 The go-ethereum Authors // Copyright 2015 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
@ -47,7 +47,8 @@ type solcOutput struct {
func (s *Solidity) makeArgs() []string { func (s *Solidity) makeArgs() []string {
p := []string{ p := []string{
"--combined-json", "bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc", "--combined-json", "bin,bin-runtime,srcmap,srcmap-runtime,abi,userdoc,devdoc",
"--optimize", // code optimizer switched on "--optimize", // code optimizer switched on
"--allow-paths", "., ./, ../", // default to support relative paths
} }
if s.Major > 0 || s.Minor > 4 || s.Patch > 6 { if s.Major > 0 || s.Minor > 4 || s.Patch > 6 {
p[1] += ",metadata,hashes" p[1] += ",metadata,hashes"
@ -142,7 +143,6 @@ func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion strin
if err := json.Unmarshal(combinedJSON, &output); err != nil { if err := json.Unmarshal(combinedJSON, &output); err != nil {
return nil, err return nil, err
} }
// Compilation succeeded, assemble and return the contracts. // Compilation succeeded, assemble and return the contracts.
contracts := make(map[string]*Contract) contracts := make(map[string]*Contract)
for name, info := range output.Contracts { for name, info := range output.Contracts {
@ -151,14 +151,10 @@ func ParseCombinedJSON(combinedJSON []byte, source string, languageVersion strin
if err := json.Unmarshal([]byte(info.Abi), &abi); err != nil { if err := json.Unmarshal([]byte(info.Abi), &abi); err != nil {
return nil, fmt.Errorf("solc: error reading abi definition (%v)", err) return nil, fmt.Errorf("solc: error reading abi definition (%v)", err)
} }
var userdoc interface{} var userdoc, devdoc interface{}
if err := json.Unmarshal([]byte(info.Userdoc), &userdoc); err != nil { json.Unmarshal([]byte(info.Userdoc), &userdoc)
return nil, fmt.Errorf("solc: error reading user doc: %v", err) json.Unmarshal([]byte(info.Devdoc), &devdoc)
}
var devdoc interface{}
if err := json.Unmarshal([]byte(info.Devdoc), &devdoc); err != nil {
return nil, fmt.Errorf("solc: error reading dev doc: %v", err)
}
contracts[name] = &Contract{ contracts[name] = &Contract{
Code: "0x" + info.Bin, Code: "0x" + info.Bin,
RuntimeCode: "0x" + info.BinRuntime, RuntimeCode: "0x" + info.BinRuntime,

View file

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

View file

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

View file

@ -36,28 +36,39 @@ func (t AbsTime) Add(d time.Duration) AbsTime {
return t + AbsTime(d) return t + AbsTime(d)
} }
// Clock interface makes it possible to replace the monotonic system clock with // The Clock interface makes it possible to replace the monotonic system clock with
// a simulated clock. // a simulated clock.
type Clock interface { type Clock interface {
Now() AbsTime Now() AbsTime
Sleep(time.Duration) Sleep(time.Duration)
After(time.Duration) <-chan time.Time After(time.Duration) <-chan time.Time
AfterFunc(d time.Duration, f func()) Timer
}
// Timer represents a cancellable event returned by AfterFunc
type Timer interface {
Stop() bool
} }
// System implements Clock using the system clock. // System implements Clock using the system clock.
type System struct{} type System struct{}
// Now implements Clock. // Now returns the current monotonic time.
func (System) Now() AbsTime { func (System) Now() AbsTime {
return AbsTime(monotime.Now()) return AbsTime(monotime.Now())
} }
// Sleep implements Clock. // Sleep blocks for the given duration.
func (System) Sleep(d time.Duration) { func (System) Sleep(d time.Duration) {
time.Sleep(d) time.Sleep(d)
} }
// After implements Clock. // After returns a channel which receives the current time after d has elapsed.
func (System) After(d time.Duration) <-chan time.Time { func (System) After(d time.Duration) <-chan time.Time {
return time.After(d) return time.After(d)
} }
// AfterFunc runs f on a new goroutine after the duration has elapsed.
func (System) AfterFunc(d time.Duration, f func()) Timer {
return time.AfterFunc(d, f)
}

View file

@ -32,35 +32,45 @@ import (
// the timeout using a channel or semaphore. // the timeout using a channel or semaphore.
type Simulated struct { type Simulated struct {
now AbsTime now AbsTime
scheduled []event scheduled []*simTimer
mu sync.RWMutex mu sync.RWMutex
cond *sync.Cond cond *sync.Cond
lastId uint64
} }
type event struct { // simTimer implements Timer on the virtual clock.
type simTimer struct {
do func() do func()
at AbsTime at AbsTime
id uint64
s *Simulated
} }
// Run moves the clock by the given duration, executing all timers before that duration. // Run moves the clock by the given duration, executing all timers before that duration.
func (s *Simulated) Run(d time.Duration) { func (s *Simulated) Run(d time.Duration) {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock()
s.init() s.init()
end := s.now + AbsTime(d) end := s.now + AbsTime(d)
var do []func()
for len(s.scheduled) > 0 { for len(s.scheduled) > 0 {
ev := s.scheduled[0] ev := s.scheduled[0]
if ev.at > end { if ev.at > end {
break break
} }
s.now = ev.at s.now = ev.at
ev.do() do = append(do, ev.do)
s.scheduled = s.scheduled[1:] s.scheduled = s.scheduled[1:]
} }
s.now = end s.now = end
s.mu.Unlock()
for _, fn := range do {
fn()
}
} }
// ActiveTimers returns the number of timers that haven't fired.
func (s *Simulated) ActiveTimers() int { func (s *Simulated) ActiveTimers() int {
s.mu.RLock() s.mu.RLock()
defer s.mu.RUnlock() defer s.mu.RUnlock()
@ -68,6 +78,7 @@ func (s *Simulated) ActiveTimers() int {
return len(s.scheduled) return len(s.scheduled)
} }
// WaitForTimers waits until the clock has at least n scheduled timers.
func (s *Simulated) WaitForTimers(n int) { func (s *Simulated) WaitForTimers(n int) {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
@ -78,7 +89,7 @@ func (s *Simulated) WaitForTimers(n int) {
} }
} }
// Now implements Clock. // Now returns the current virtual time.
func (s *Simulated) Now() AbsTime { func (s *Simulated) Now() AbsTime {
s.mu.RLock() s.mu.RLock()
defer s.mu.RUnlock() defer s.mu.RUnlock()
@ -86,40 +97,62 @@ func (s *Simulated) Now() AbsTime {
return s.now return s.now
} }
// Sleep implements Clock. // Sleep blocks until the clock has advanced by d.
func (s *Simulated) Sleep(d time.Duration) { func (s *Simulated) Sleep(d time.Duration) {
<-s.After(d) <-s.After(d)
} }
// After implements Clock. // After returns a channel which receives the current time after the clock
// has advanced by d.
func (s *Simulated) After(d time.Duration) <-chan time.Time { func (s *Simulated) After(d time.Duration) <-chan time.Time {
after := make(chan time.Time, 1) after := make(chan time.Time, 1)
s.insert(d, func() { s.AfterFunc(d, func() {
after <- (time.Time{}).Add(time.Duration(s.now)) after <- (time.Time{}).Add(time.Duration(s.now))
}) })
return after return after
} }
func (s *Simulated) insert(d time.Duration, do func()) { // AfterFunc runs fn after the clock has advanced by d. Unlike with the system
// clock, fn runs on the goroutine that calls Run.
func (s *Simulated) AfterFunc(d time.Duration, fn func()) Timer {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
s.init() s.init()
at := s.now + AbsTime(d) at := s.now + AbsTime(d)
s.lastId++
id := s.lastId
l, h := 0, len(s.scheduled) l, h := 0, len(s.scheduled)
ll := h ll := h
for l != h { for l != h {
m := (l + h) / 2 m := (l + h) / 2
if at < s.scheduled[m].at { if (at < s.scheduled[m].at) || ((at == s.scheduled[m].at) && (id < s.scheduled[m].id)) {
h = m h = m
} else { } else {
l = m + 1 l = m + 1
} }
} }
s.scheduled = append(s.scheduled, event{}) ev := &simTimer{do: fn, at: at, s: s}
s.scheduled = append(s.scheduled, nil)
copy(s.scheduled[l+1:], s.scheduled[l:ll]) copy(s.scheduled[l+1:], s.scheduled[l:ll])
s.scheduled[l] = event{do: do, at: at} s.scheduled[l] = ev
s.cond.Broadcast() s.cond.Broadcast()
return ev
}
func (ev *simTimer) Stop() bool {
s := ev.s
s.mu.Lock()
defer s.mu.Unlock()
for i := 0; i < len(s.scheduled); i++ {
if s.scheduled[i] == ev {
s.scheduled = append(s.scheduled[:i], s.scheduled[i+1:]...)
s.cond.Broadcast()
return true
}
}
return false
} }
func (s *Simulated) init() { func (s *Simulated) init() {

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