mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
commit
db26bfd37c
584 changed files with 365262 additions and 9049 deletions
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
||||||
92
.travis.yml
92
.travis.yml
|
|
@ -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
247
AUTHORS
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
2
Makefile
2
Makefile
|
|
@ -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:
|
||||||
|
|
|
||||||
14
README.md
14
README.md
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"))
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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{}
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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())))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)...)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
22
accounts/external/backend.go
vendored
22
accounts/external/backend.go
vendored
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
2
accounts/keystore/testdata/keystore/README
vendored
2
accounts/keystore/testdata/keystore/README
vendored
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
77
build/ci.go
77
build/ci.go
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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, "", "", "", "")
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 != "":
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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`.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
152
cmd/devp2p/crawl.go
Normal 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)
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
163
cmd/devp2p/dns_cloudflare.go
Normal file
163
cmd/devp2p/dns_cloudflare.go
Normal 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
361
cmd/devp2p/dnscmd.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
102
cmd/devp2p/nodeset.go
Normal 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
193
cmd/devp2p/nodesetcmd.go
Normal 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", ð)) != 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
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
102
cmd/geth/main.go
102
cmd/geth/main.go
|
|
@ -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(ðService); err != nil {
|
if err := stack.Service(ðService); err != nil {
|
||||||
utils.Fatalf("Failed to retrieve ethereum service: %v", err)
|
utils.Fatalf("Failed to retrieve ethereum service: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -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: ¶ms.ChainConfig{
|
Config: ¶ms.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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
175
cmd/puppeth/testdata/stureby_aleth.json
vendored
175
cmd/puppeth/testdata/stureby_aleth.json
vendored
|
|
@ -1,111 +1,112 @@
|
||||||
{
|
{
|
||||||
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
31
cmd/puppeth/testdata/stureby_geth.json
vendored
31
cmd/puppeth/testdata/stureby_geth.json
vendored
|
|
@ -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"
|
||||||
}
|
}
|
||||||
287
cmd/puppeth/testdata/stureby_parity.json
vendored
287
cmd/puppeth/testdata/stureby_parity.json
vendored
|
|
@ -1,180 +1,185 @@
|
||||||
{
|
{
|
||||||
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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 = ""
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 = ð.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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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:]
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
Loading…
Reference in a new issue