mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
Merge pull request #949 from maticnetwork/mardizzone/upstream-merge
Upstream merge from go-ethereum/v1.11.6
This commit is contained in:
commit
b09b69cfb7
1401 changed files with 204627 additions and 181643 deletions
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
|
|
@ -35,6 +35,6 @@ and help.
|
||||||
|
|
||||||
## Configuration, dependencies, and tests
|
## Configuration, dependencies, and tests
|
||||||
|
|
||||||
Please see the [Developers' Guide](https://geth.ethereum.org/docs/developers/devguide)
|
Please see the [Developers' Guide](https://geth.ethereum.org/docs/developers/geth-developer/dev-guide)
|
||||||
for more details on configuring your environment, managing project dependencies
|
for more details on configuring your environment, managing project dependencies
|
||||||
and testing procedures.
|
and testing procedures.
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ linters-settings:
|
||||||
|
|
||||||
issues:
|
issues:
|
||||||
exclude-rules:
|
exclude-rules:
|
||||||
- path: crypto/blake2b/
|
- path: crypto/bn256/cloudflare/optate.go
|
||||||
linters:
|
linters:
|
||||||
- deadcode
|
- deadcode
|
||||||
- path: crypto/bn256/cloudflare
|
- path: crypto/bn256/cloudflare
|
||||||
|
|
|
||||||
294
.mailmap
294
.mailmap
|
|
@ -1,123 +1,237 @@
|
||||||
Jeffrey Wilcke <jeffrey@ethereum.org>
|
Aaron Buchwald <aaron.buchwald56@gmail.com>
|
||||||
Jeffrey Wilcke <jeffrey@ethereum.org> <geffobscura@gmail.com>
|
|
||||||
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@obscura.com>
|
|
||||||
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@users.noreply.github.com>
|
|
||||||
|
|
||||||
Viktor Trón <viktor.tron@gmail.com>
|
Aaron Kumavis <kumavis@users.noreply.github.com>
|
||||||
|
|
||||||
Joseph Goulden <joegoulden@gmail.com>
|
Abel Nieto <abel.nieto90@gmail.com>
|
||||||
|
Abel Nieto <abel.nieto90@gmail.com> <anietoro@uwaterloo.ca>
|
||||||
|
|
||||||
Nick Savers <nicksavers@gmail.com>
|
Afri Schoedon <58883403+q9f@users.noreply.github.com>
|
||||||
|
Afri Schoedon <5chdn@users.noreply.github.com> <58883403+q9f@users.noreply.github.com>
|
||||||
|
|
||||||
Maran Hidskes <maran.hidskes@gmail.com>
|
Alec Perseghin <aperseghin@gmail.com>
|
||||||
|
|
||||||
Taylor Gerring <taylor.gerring@gmail.com>
|
Aleksey Smyrnov <i@soar.name>
|
||||||
Taylor Gerring <taylor.gerring@gmail.com> <taylor.gerring@ethereum.org>
|
|
||||||
|
Alex Leverington <alex@ethdev.com>
|
||||||
|
Alex Leverington <alex@ethdev.com> <subtly@users.noreply.github.com>
|
||||||
|
|
||||||
|
Alex Pozhilenkov <alex_pozhilenkov@adoriasoft.com>
|
||||||
|
Alex Pozhilenkov <alex_pozhilenkov@adoriasoft.com> <leshiy12345678@gmail.com>
|
||||||
|
|
||||||
|
Alexey Akhunov <akhounov@gmail.com>
|
||||||
|
|
||||||
|
Alon Muroch <alonmuroch@gmail.com>
|
||||||
|
|
||||||
|
Andrey Petrov <shazow@gmail.com>
|
||||||
|
Andrey Petrov <shazow@gmail.com> <andrey.petrov@shazow.net>
|
||||||
|
|
||||||
|
Arkadiy Paronyan <arkadiy@ethdev.com>
|
||||||
|
|
||||||
|
Armin Braun <me@obrown.io>
|
||||||
|
|
||||||
|
Aron Fischer <github@aron.guru> <homotopycolimit@users.noreply.github.com>
|
||||||
|
|
||||||
|
Austin Roberts <code@ausiv.com>
|
||||||
|
Austin Roberts <code@ausiv.com> <git@ausiv.com>
|
||||||
|
|
||||||
Bas van Kervel <bas@ethdev.com>
|
Bas van Kervel <bas@ethdev.com>
|
||||||
Bas van Kervel <bas@ethdev.com> <basvankervel@ziggo.nl>
|
Bas van Kervel <bas@ethdev.com> <basvankervel@ziggo.nl>
|
||||||
Bas van Kervel <bas@ethdev.com> <basvankervel@gmail.com>
|
Bas van Kervel <bas@ethdev.com> <basvankervel@gmail.com>
|
||||||
Bas van Kervel <bas@ethdev.com> <bas-vk@users.noreply.github.com>
|
Bas van Kervel <bas@ethdev.com> <bas-vk@users.noreply.github.com>
|
||||||
|
|
||||||
Sven Ehlert <sven@ethdev.com>
|
Boqin Qin <bobbqqin@bupt.edu.cn>
|
||||||
|
Boqin Qin <bobbqqin@bupt.edu.cn> <Bobbqqin@gmail.com>
|
||||||
Vitalik Buterin <v@buterin.com>
|
|
||||||
|
|
||||||
Marian Oancea <contact@siteshop.ro>
|
|
||||||
|
|
||||||
Christoph Jentzsch <jentzsch.software@gmail.com>
|
|
||||||
|
|
||||||
Heiko Hees <heiko@heiko.org>
|
|
||||||
|
|
||||||
Alex Leverington <alex@ethdev.com>
|
|
||||||
Alex Leverington <alex@ethdev.com> <subtly@users.noreply.github.com>
|
|
||||||
|
|
||||||
Zsolt Felföldi <zsfelfoldi@gmail.com>
|
|
||||||
|
|
||||||
Gavin Wood <i@gavwood.com>
|
|
||||||
|
|
||||||
Martin Becze <mjbecze@gmail.com>
|
|
||||||
Martin Becze <mjbecze@gmail.com> <wanderer@users.noreply.github.com>
|
|
||||||
|
|
||||||
Dimitry Khokhlov <winsvega@mail.ru>
|
|
||||||
|
|
||||||
Roman Mandeleil <roman.mandeleil@gmail.com>
|
|
||||||
|
|
||||||
Alec Perseghin <aperseghin@gmail.com>
|
|
||||||
|
|
||||||
Alon Muroch <alonmuroch@gmail.com>
|
|
||||||
|
|
||||||
Arkadiy Paronyan <arkadiy@ethdev.com>
|
|
||||||
|
|
||||||
Jae Kwon <jkwon.work@gmail.com>
|
|
||||||
|
|
||||||
Aaron Kumavis <kumavis@users.noreply.github.com>
|
|
||||||
|
|
||||||
Nick Dodson <silentcicero@outlook.com>
|
|
||||||
|
|
||||||
Jason Carver <jacarver@linkedin.com>
|
|
||||||
Jason Carver <jacarver@linkedin.com> <ut96caarrs@snkmail.com>
|
|
||||||
|
|
||||||
Joseph Chow <ethereum@outlook.com>
|
|
||||||
Joseph Chow <ethereum@outlook.com> ethers <TODO>
|
|
||||||
|
|
||||||
Enrique Fynn <enriquefynn@gmail.com>
|
|
||||||
|
|
||||||
Vincent G <caktux@gmail.com>
|
|
||||||
|
|
||||||
RJ Catalano <catalanor0220@gmail.com>
|
|
||||||
RJ Catalano <catalanor0220@gmail.com> <rj@erisindustries.com>
|
|
||||||
|
|
||||||
Nchinda Nchinda <nchinda2@gmail.com>
|
|
||||||
|
|
||||||
Aron Fischer <github@aron.guru> <homotopycolimit@users.noreply.github.com>
|
|
||||||
|
|
||||||
Vlad Gluhovsky <gluk256@users.noreply.github.com>
|
|
||||||
|
|
||||||
Ville Sundell <github@solarius.fi>
|
|
||||||
|
|
||||||
Elliot Shepherd <elliot@identitii.com>
|
|
||||||
|
|
||||||
Yohann Léon <sybiload@gmail.com>
|
|
||||||
|
|
||||||
Gregg Dourgarian <greggd@tempworks.com>
|
|
||||||
|
|
||||||
Casey Detrio <cdetrio@gmail.com>
|
Casey Detrio <cdetrio@gmail.com>
|
||||||
|
|
||||||
Jens Agerberg <github@agerberg.me>
|
Cheng Li <lob4tt@gmail.com>
|
||||||
|
|
||||||
Nick Johnson <arachnid@notdot.net>
|
Chris Ziogas <ziogaschr@gmail.com>
|
||||||
|
Chris Ziogas <ziogaschr@gmail.com> <ziogas_chr@hotmail.com>
|
||||||
|
|
||||||
Henning Diedrich <hd@eonblast.com>
|
Christoph Jentzsch <jentzsch.software@gmail.com>
|
||||||
Henning Diedrich <hd@eonblast.com> Drake Burroughs <wildfyre@hotmail.com>
|
|
||||||
|
Diederik Loerakker <proto@protolambda.com>
|
||||||
|
|
||||||
|
Dimitry Khokhlov <winsvega@mail.ru>
|
||||||
|
|
||||||
|
Domino Valdano <dominoplural@gmail.com>
|
||||||
|
Domino Valdano <dominoplural@gmail.com> <jeff@okcupid.com>
|
||||||
|
|
||||||
|
Edgar Aroutiounian <edgar.factorial@gmail.com>
|
||||||
|
|
||||||
|
Elliot Shepherd <elliot@identitii.com>
|
||||||
|
|
||||||
|
Enrique Fynn <enriquefynn@gmail.com>
|
||||||
|
|
||||||
|
Enrique Fynn <me@enriquefynn.com>
|
||||||
|
Enrique Fynn <me@enriquefynn.com> <enriquefynn@gmail.com>
|
||||||
|
|
||||||
|
Ernesto del Toro <ernesto.deltoro@gmail.com>
|
||||||
|
Ernesto del Toro <ernesto.deltoro@gmail.com> <ernestodeltoro@users.noreply.github.com>
|
||||||
|
|
||||||
|
Everton Fraga <ev@ethereum.org>
|
||||||
|
|
||||||
Felix Lange <fjl@twurst.com>
|
Felix Lange <fjl@twurst.com>
|
||||||
Felix Lange <fjl@twurst.com> <fjl@users.noreply.github.com>
|
Felix Lange <fjl@twurst.com> <fjl@users.noreply.github.com>
|
||||||
|
|
||||||
Максим Чусовлянов <mchusovlianov@gmail.com>
|
|
||||||
|
|
||||||
Louis Holbrook <dev@holbrook.no>
|
|
||||||
Louis Holbrook <dev@holbrook.no> <nolash@users.noreply.github.com>
|
|
||||||
|
|
||||||
Thomas Bocek <tom@tomp2p.net>
|
|
||||||
|
|
||||||
Victor Tran <vu.tran54@gmail.com>
|
|
||||||
|
|
||||||
Justin Drake <drakefjustin@gmail.com>
|
|
||||||
|
|
||||||
Frank Wang <eternnoir@gmail.com>
|
Frank Wang <eternnoir@gmail.com>
|
||||||
|
|
||||||
Gary Rong <garyrong0905@gmail.com>
|
Gary Rong <garyrong0905@gmail.com>
|
||||||
|
|
||||||
|
Gavin Wood <i@gavwood.com>
|
||||||
|
|
||||||
|
Gregg Dourgarian <greggd@tempworks.com>
|
||||||
|
|
||||||
|
Guillaume Ballet <gballet@gmail.com>
|
||||||
|
Guillaume Ballet <gballet@gmail.com> <3272758+gballet@users.noreply.github.com>
|
||||||
|
|
||||||
Guillaume Nicolas <guin56@gmail.com>
|
Guillaume Nicolas <guin56@gmail.com>
|
||||||
|
|
||||||
|
Hanjiang Yu <delacroix.yu@gmail.com>
|
||||||
|
Hanjiang Yu <delacroix.yu@gmail.com> <42531996+de1acr0ix@users.noreply.github.com>
|
||||||
|
|
||||||
|
Heiko Hees <heiko@heiko.org>
|
||||||
|
|
||||||
|
Henning Diedrich <hd@eonblast.com>
|
||||||
|
Henning Diedrich <hd@eonblast.com> Drake Burroughs <wildfyre@hotmail.com>
|
||||||
|
|
||||||
|
Hwanjo Heo <34005989+hwanjo@users.noreply.github.com>
|
||||||
|
|
||||||
|
Iskander (Alex) Sharipov <quasilyte@gmail.com>
|
||||||
|
Iskander (Alex) Sharipov <quasilyte@gmail.com> <i.sharipov@corp.vk.com>
|
||||||
|
|
||||||
|
Jae Kwon <jkwon.work@gmail.com>
|
||||||
|
|
||||||
|
Janoš Guljaš <janos@resenje.org> <janos@users.noreply.github.com>
|
||||||
|
Janoš Guljaš <janos@resenje.org> Janos Guljas <janos@resenje.org>
|
||||||
|
|
||||||
|
Jared Wasinger <j-wasinger@hotmail.com>
|
||||||
|
|
||||||
|
Jason Carver <jacarver@linkedin.com>
|
||||||
|
Jason Carver <jacarver@linkedin.com> <ut96caarrs@snkmail.com>
|
||||||
|
|
||||||
|
Javier Peletier <jm@epiclabs.io>
|
||||||
|
Javier Peletier <jm@epiclabs.io> <jpeletier@users.noreply.github.com>
|
||||||
|
|
||||||
|
Jeffrey Wilcke <jeffrey@ethereum.org>
|
||||||
|
Jeffrey Wilcke <jeffrey@ethereum.org> <geffobscura@gmail.com>
|
||||||
|
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@obscura.com>
|
||||||
|
Jeffrey Wilcke <jeffrey@ethereum.org> <obscuren@users.noreply.github.com>
|
||||||
|
|
||||||
|
Jens Agerberg <github@agerberg.me>
|
||||||
|
|
||||||
|
Joseph Chow <ethereum@outlook.com>
|
||||||
|
Joseph Chow <ethereum@outlook.com> ethers <TODO>
|
||||||
|
|
||||||
|
|
||||||
|
Joseph Goulden <joegoulden@gmail.com>
|
||||||
|
|
||||||
|
Justin Drake <drakefjustin@gmail.com>
|
||||||
|
|
||||||
|
Kenso Trabing <ktrabing@acm.org>
|
||||||
|
Kenso Trabing <ktrabing@acm.org> <kenso.trabing@bloomwebsite.com>
|
||||||
|
|
||||||
|
Liang Ma <liangma@liangbit.com>
|
||||||
|
Liang Ma <liangma@liangbit.com> <liangma.ul@gmail.com>
|
||||||
|
|
||||||
|
Louis Holbrook <dev@holbrook.no>
|
||||||
|
Louis Holbrook <dev@holbrook.no> <nolash@users.noreply.github.com>
|
||||||
|
|
||||||
|
Maran Hidskes <maran.hidskes@gmail.com>
|
||||||
|
|
||||||
|
Marian Oancea <contact@siteshop.ro>
|
||||||
|
|
||||||
|
Martin Becze <mjbecze@gmail.com>
|
||||||
|
Martin Becze <mjbecze@gmail.com> <wanderer@users.noreply.github.com>
|
||||||
|
|
||||||
|
Martin Lundfall <martin.lundfall@protonmail.com>
|
||||||
|
|
||||||
|
Matt Garnett <14004106+lightclient@users.noreply.github.com>
|
||||||
|
|
||||||
|
Matthew Halpern <matthalp@gmail.com>
|
||||||
|
Matthew Halpern <matthalp@gmail.com> <matthalp@google.com>
|
||||||
|
|
||||||
|
Michael Riabzev <michael@starkware.co>
|
||||||
|
|
||||||
|
Nchinda Nchinda <nchinda2@gmail.com>
|
||||||
|
|
||||||
|
Nick Dodson <silentcicero@outlook.com>
|
||||||
|
|
||||||
|
Nick Johnson <arachnid@notdot.net>
|
||||||
|
|
||||||
|
Nick Savers <nicksavers@gmail.com>
|
||||||
|
|
||||||
|
Nishant Das <nishdas93@gmail.com>
|
||||||
|
Nishant Das <nishdas93@gmail.com> <nish1993@hotmail.com>
|
||||||
|
|
||||||
|
Olivier Hervieu <olivier.hervieu@gmail.com>
|
||||||
|
|
||||||
|
Pascal Dierich <pascal@merkleplant.xyz>
|
||||||
|
Pascal Dierich <pascal@merkleplant.xyz> <pascal@pascaldierich.com>
|
||||||
|
|
||||||
|
RJ Catalano <catalanor0220@gmail.com>
|
||||||
|
RJ Catalano <catalanor0220@gmail.com> <rj@erisindustries.com>
|
||||||
|
|
||||||
|
Ralph Caraveo <deckarep@gmail.com>
|
||||||
|
|
||||||
|
Rene Lubov <41963722+renaynay@users.noreply.github.com>
|
||||||
|
|
||||||
|
Robert Zaremba <robert@zaremba.ch>
|
||||||
|
Robert Zaremba <robert@zaremba.ch> <robert.zaremba@scale-it.pl>
|
||||||
|
|
||||||
|
Roman Mandeleil <roman.mandeleil@gmail.com>
|
||||||
|
|
||||||
Sorin Neacsu <sorin.neacsu@gmail.com>
|
Sorin Neacsu <sorin.neacsu@gmail.com>
|
||||||
Sorin Neacsu <sorin.neacsu@gmail.com> <sorin@users.noreply.github.com>
|
Sorin Neacsu <sorin.neacsu@gmail.com> <sorin@users.noreply.github.com>
|
||||||
|
|
||||||
|
Sven Ehlert <sven@ethdev.com>
|
||||||
|
|
||||||
|
Taylor Gerring <taylor.gerring@gmail.com>
|
||||||
|
Taylor Gerring <taylor.gerring@gmail.com> <taylor.gerring@ethereum.org>
|
||||||
|
|
||||||
|
Thomas Bocek <tom@tomp2p.net>
|
||||||
|
|
||||||
|
Tim Cooijmans <timcooijmans@gmail.com>
|
||||||
|
|
||||||
Valentin Wüstholz <wuestholz@gmail.com>
|
Valentin Wüstholz <wuestholz@gmail.com>
|
||||||
Valentin Wüstholz <wuestholz@gmail.com> <wuestholz@users.noreply.github.com>
|
Valentin Wüstholz <wuestholz@gmail.com> <wuestholz@users.noreply.github.com>
|
||||||
|
|
||||||
Armin Braun <me@obrown.io>
|
Victor Tran <vu.tran54@gmail.com>
|
||||||
|
|
||||||
Ernesto del Toro <ernesto.deltoro@gmail.com>
|
Viktor Trón <viktor.tron@gmail.com>
|
||||||
Ernesto del Toro <ernesto.deltoro@gmail.com> <ernestodeltoro@users.noreply.github.com>
|
|
||||||
|
Ville Sundell <github@solarius.fi>
|
||||||
|
|
||||||
|
Vincent G <caktux@gmail.com>
|
||||||
|
|
||||||
|
Vitalik Buterin <v@buterin.com>
|
||||||
|
|
||||||
|
Vlad Gluhovsky <gluk256@gmail.com>
|
||||||
|
Vlad Gluhovsky <gluk256@gmail.com> <gluk256@users.noreply.github.com>
|
||||||
|
|
||||||
|
Wenshao Zhong <wzhong20@uic.edu>
|
||||||
|
Wenshao Zhong <wzhong20@uic.edu> <11510383@mail.sustc.edu.cn>
|
||||||
|
Wenshao Zhong <wzhong20@uic.edu> <374662347@qq.com>
|
||||||
|
|
||||||
|
Will Villanueva <hello@willvillanueva.com>
|
||||||
|
|
||||||
|
Xiaobing Jiang <s7v7nislands@gmail.com>
|
||||||
|
|
||||||
|
Xudong Liu <33193253+r1cs@users.noreply.github.com>
|
||||||
|
|
||||||
|
Yohann Léon <sybiload@gmail.com>
|
||||||
|
|
||||||
|
Zachinquarantine <Zachinquarantine@protonmail.com>
|
||||||
|
Zachinquarantine <Zachinquarantine@protonmail.com> <zachinquarantine@yahoo.com>
|
||||||
|
|
||||||
|
Ziyuan Zhong <zzy.albert@163.com>
|
||||||
|
|
||||||
|
Zsolt Felföldi <zsfelfoldi@gmail.com>
|
||||||
|
|
||||||
|
meowsbits <b5c6@protonmail.com>
|
||||||
|
meowsbits <b5c6@protonmail.com> <45600330+meowsbits@users.noreply.github.com>
|
||||||
|
|
||||||
|
nedifi <103940716+nedifi@users.noreply.github.com>
|
||||||
|
|
||||||
|
Максим Чусовлянов <mchusovlianov@gmail.com>
|
||||||
|
|
|
||||||
45
.travis.yml
45
.travis.yml
|
|
@ -5,11 +5,8 @@ jobs:
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- stage: build
|
- stage: build
|
||||||
os: osx
|
os: osx
|
||||||
go: 1.17.x
|
|
||||||
env:
|
env:
|
||||||
- azure-osx
|
- azure-osx
|
||||||
- azure-ios
|
|
||||||
- cocoapods-ios
|
|
||||||
|
|
||||||
include:
|
include:
|
||||||
# This builder only tests code linters on latest version of Go
|
# This builder only tests code linters on latest version of Go
|
||||||
|
|
@ -165,8 +162,6 @@ jobs:
|
||||||
go: 1.20.x
|
go: 1.20.x
|
||||||
env:
|
env:
|
||||||
- azure-osx
|
- azure-osx
|
||||||
- azure-ios
|
|
||||||
- cocoapods-ios
|
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
git:
|
git:
|
||||||
submodules: false # avoid cloning ethereum/tests
|
submodules: false # avoid cloning ethereum/tests
|
||||||
|
|
@ -174,21 +169,6 @@ jobs:
|
||||||
- go run build/ci.go install -dlgo
|
- go run build/ci.go install -dlgo
|
||||||
- go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
- go run build/ci.go archive -type tar -signer OSX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
|
||||||
|
|
||||||
# Build the iOS framework and upload it to CocoaPods and Azure
|
|
||||||
- gem uninstall cocoapods -a -x
|
|
||||||
- gem install cocoapods
|
|
||||||
|
|
||||||
- mv ~/.cocoapods/repos/master ~/.cocoapods/repos/master.bak
|
|
||||||
- sed -i '.bak' 's/repo.join/!repo.join/g' $(dirname `gem which cocoapods`)/cocoapods/sources_manager.rb
|
|
||||||
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then git clone --depth=1 https://github.com/CocoaPods/Specs.git ~/.cocoapods/repos/master && pod setup --verbose; fi
|
|
||||||
|
|
||||||
- xctool -version
|
|
||||||
- xcrun simctl list
|
|
||||||
|
|
||||||
# Workaround for https://github.com/golang/go/issues/23749
|
|
||||||
- export CGO_CFLAGS_ALLOW='-fmodules|-fblocks|-fobjc-arc'
|
|
||||||
- go run build/ci.go xcode -signer IOS_SIGNING_KEY -signify SIGNIFY_KEY -deploy trunk -upload gethstore/builds
|
|
||||||
|
|
||||||
# These builders run the tests
|
# These builders run the tests
|
||||||
- stage: build
|
- stage: build
|
||||||
os: linux
|
os: linux
|
||||||
|
|
@ -198,7 +178,7 @@ jobs:
|
||||||
env:
|
env:
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
script:
|
script:
|
||||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
- go run build/ci.go test $TEST_PACKAGES
|
||||||
|
|
||||||
- stage: build
|
- stage: build
|
||||||
if: type = pull_request
|
if: type = pull_request
|
||||||
|
|
@ -209,16 +189,31 @@ jobs:
|
||||||
env:
|
env:
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
script:
|
script:
|
||||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
- go run build/ci.go test $TEST_PACKAGES
|
||||||
|
|
||||||
|
# This builder does the Ubuntu PPA nightly uploads
|
||||||
- stage: build
|
- stage: build
|
||||||
|
if: type = cron || (type = push && tag ~= /^v[0-9]/)
|
||||||
os: linux
|
os: linux
|
||||||
dist: bionic
|
dist: bionic
|
||||||
go: 1.17.x
|
go: 1.18.x
|
||||||
env:
|
env:
|
||||||
|
- ubuntu-ppa
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
|
git:
|
||||||
|
submodules: false # avoid cloning ethereum/tests
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
packages:
|
||||||
|
- devscripts
|
||||||
|
- debhelper
|
||||||
|
- dput
|
||||||
|
- fakeroot
|
||||||
|
- python-bzrlib
|
||||||
|
- python-paramiko
|
||||||
script:
|
script:
|
||||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
- 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>"
|
||||||
|
|
||||||
# This builder does the Azure archive purges to avoid accumulating junk
|
# This builder does the Azure archive purges to avoid accumulating junk
|
||||||
- stage: build
|
- stage: build
|
||||||
|
|
@ -243,5 +238,5 @@ jobs:
|
||||||
env:
|
env:
|
||||||
- GO111MODULE=on
|
- GO111MODULE=on
|
||||||
script:
|
script:
|
||||||
- go run build/ci.go test -race -coverage $TEST_PACKAGES
|
- go run build/ci.go test -race $TEST_PACKAGES
|
||||||
|
|
||||||
|
|
|
||||||
260
AUTHORS
260
AUTHORS
|
|
@ -1,27 +1,46 @@
|
||||||
# This is the official list of go-ethereum authors for copyright purposes.
|
# This is the official list of go-ethereum authors for copyright purposes.
|
||||||
|
|
||||||
|
6543 <6543@obermui.de>
|
||||||
a e r t h <aerth@users.noreply.github.com>
|
a e r t h <aerth@users.noreply.github.com>
|
||||||
|
Aaron Buchwald <aaron.buchwald56@gmail.com>
|
||||||
Abel Nieto <abel.nieto90@gmail.com>
|
Abel Nieto <abel.nieto90@gmail.com>
|
||||||
Abel Nieto <anietoro@uwaterloo.ca>
|
|
||||||
Adam Babik <a.babik@designfortress.com>
|
Adam Babik <a.babik@designfortress.com>
|
||||||
|
Adam Schmideg <adamschmideg@users.noreply.github.com>
|
||||||
Aditya <adityasripal@gmail.com>
|
Aditya <adityasripal@gmail.com>
|
||||||
|
Aditya Arora <arora.aditya520@gmail.com>
|
||||||
Adrià Cidre <adria.cidre@gmail.com>
|
Adrià Cidre <adria.cidre@gmail.com>
|
||||||
|
Afanasii Kurakin <afanasy@users.noreply.github.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>
|
||||||
|
Ahyun <urbanart2251@gmail.com>
|
||||||
Airead <fgh1987168@gmail.com>
|
Airead <fgh1987168@gmail.com>
|
||||||
Alan Chen <alanchchen@users.noreply.github.com>
|
Alan Chen <alanchchen@users.noreply.github.com>
|
||||||
Alejandro Isaza <alejandro.isaza@gmail.com>
|
Alejandro Isaza <alejandro.isaza@gmail.com>
|
||||||
|
Aleksey Smyrnov <i@soar.name>
|
||||||
Ales Katona <ales@coinbase.com>
|
Ales Katona <ales@coinbase.com>
|
||||||
|
Alex Beregszaszi <alex@rtfs.hu>
|
||||||
Alex Leverington <alex@ethdev.com>
|
Alex Leverington <alex@ethdev.com>
|
||||||
|
Alex Mazalov <mazalov@gmail.com>
|
||||||
|
Alex Pozhilenkov <alex_pozhilenkov@adoriasoft.com>
|
||||||
|
Alex Prut <1648497+alexprut@users.noreply.github.com>
|
||||||
Alex Wu <wuyiding@gmail.com>
|
Alex Wu <wuyiding@gmail.com>
|
||||||
|
Alexander van der Meij <alexandervdm@users.noreply.github.com>
|
||||||
|
Alexander Yastrebov <yastrebov.alex@gmail.com>
|
||||||
Alexandre Van de Sande <alex.vandesande@ethdev.com>
|
Alexandre Van de Sande <alex.vandesande@ethdev.com>
|
||||||
|
Alexey Akhunov <akhounov@gmail.com>
|
||||||
|
Alexey Shekhirin <a.shekhirin@gmail.com>
|
||||||
|
alexwang <39109351+dipingxian2@users.noreply.github.com>
|
||||||
|
Ali Atiia <42751398+aliatiia@users.noreply.github.com>
|
||||||
Ali Hajimirza <Ali92hm@users.noreply.github.com>
|
Ali Hajimirza <Ali92hm@users.noreply.github.com>
|
||||||
am2rican5 <am2rican5@gmail.com>
|
am2rican5 <am2rican5@gmail.com>
|
||||||
|
AmitBRD <60668103+AmitBRD@users.noreply.github.com>
|
||||||
|
Anatole <62328077+a2br@users.noreply.github.com>
|
||||||
Andrea Franz <andrea@gravityblast.com>
|
Andrea Franz <andrea@gravityblast.com>
|
||||||
Andrey Petrov <andrey.petrov@shazow.net>
|
Andrei Maiboroda <andrei@ethereum.org>
|
||||||
Andrey Petrov <shazow@gmail.com>
|
Andrey Petrov <shazow@gmail.com>
|
||||||
ANOTHEL <anothel1@naver.com>
|
ANOTHEL <anothel1@naver.com>
|
||||||
Antoine Rondelet <rondelet.antoine@gmail.com>
|
Antoine Rondelet <rondelet.antoine@gmail.com>
|
||||||
|
Antoine Toulme <atoulme@users.noreply.github.com>
|
||||||
Anton Evangelatov <anton.evangelatov@gmail.com>
|
Anton Evangelatov <anton.evangelatov@gmail.com>
|
||||||
Antonio Salazar Cardozo <savedfastcool@gmail.com>
|
Antonio Salazar Cardozo <savedfastcool@gmail.com>
|
||||||
Arba Sasmoyo <arba.sasmoyo@gmail.com>
|
Arba Sasmoyo <arba.sasmoyo@gmail.com>
|
||||||
|
|
@ -29,19 +48,26 @@ 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>
|
atsushi-ishibashi <atsushi.ishibashi@finatext.com>
|
||||||
|
Austin Roberts <code@ausiv.com>
|
||||||
ayeowch <ayeowch@gmail.com>
|
ayeowch <ayeowch@gmail.com>
|
||||||
b00ris <b00ris@mail.ru>
|
b00ris <b00ris@mail.ru>
|
||||||
|
b1ackd0t <blackd0t@protonmail.com>
|
||||||
bailantaotao <Edwin@maicoin.com>
|
bailantaotao <Edwin@maicoin.com>
|
||||||
baizhenxuan <nkbai@163.com>
|
baizhenxuan <nkbai@163.com>
|
||||||
|
Balaji Shetty Pachai <32358081+balajipachai@users.noreply.github.com>
|
||||||
Balint Gabor <balint.g@gmail.com>
|
Balint Gabor <balint.g@gmail.com>
|
||||||
|
baptiste-b-pegasys <85155432+baptiste-b-pegasys@users.noreply.github.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>
|
benma <mbencun@gmail.com>
|
||||||
Benoit Verkindt <benoit.verkindt@gmail.com>
|
Benoit Verkindt <benoit.verkindt@gmail.com>
|
||||||
|
Binacs <bin646891055@gmail.com>
|
||||||
bloonfield <bloonfield@163.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>
|
||||||
|
Boqin Qin <bobbqqin@bupt.edu.cn>
|
||||||
|
Brandon Harden <b.harden92@gmail.com>
|
||||||
Brent <bmperrea@gmail.com>
|
Brent <bmperrea@gmail.com>
|
||||||
Brian Schroeder <bts@gmail.com>
|
Brian Schroeder <bts@gmail.com>
|
||||||
Bruno Škvorc <bruno@skvorc.me>
|
Bruno Škvorc <bruno@skvorc.me>
|
||||||
|
|
@ -49,36 +75,58 @@ C. Brown <hackdom@majoolr.io>
|
||||||
Caesar Chad <BLUE.WEB.GEEK@gmail.com>
|
Caesar Chad <BLUE.WEB.GEEK@gmail.com>
|
||||||
Casey Detrio <cdetrio@gmail.com>
|
Casey Detrio <cdetrio@gmail.com>
|
||||||
CDsigma <cdsigma271@gmail.com>
|
CDsigma <cdsigma271@gmail.com>
|
||||||
|
Ceelog <chenwei@ceelog.org>
|
||||||
|
Ceyhun Onur <ceyhun.onur@avalabs.org>
|
||||||
|
chabashilah <doumodoumo@gmail.com>
|
||||||
changhong <changhong.yu@shanbay.com>
|
changhong <changhong.yu@shanbay.com>
|
||||||
Chase Wright <mysticryuujin@gmail.com>
|
Chase Wright <mysticryuujin@gmail.com>
|
||||||
Chen Quan <terasum@163.com>
|
Chen Quan <terasum@163.com>
|
||||||
|
Cheng Li <lob4tt@gmail.com>
|
||||||
|
chenglin <910372762@qq.com>
|
||||||
chenyufeng <yufengcode@gmail.com>
|
chenyufeng <yufengcode@gmail.com>
|
||||||
|
Chris Pacia <ctpacia@gmail.com>
|
||||||
|
Chris Ziogas <ziogaschr@gmail.com>
|
||||||
Christian Muehlhaeuser <muesli@gmail.com>
|
Christian Muehlhaeuser <muesli@gmail.com>
|
||||||
Christoph Jentzsch <jentzsch.software@gmail.com>
|
Christoph Jentzsch <jentzsch.software@gmail.com>
|
||||||
|
chuwt <weitaochu@gmail.com>
|
||||||
cong <ackratos@users.noreply.github.com>
|
cong <ackratos@users.noreply.github.com>
|
||||||
|
Connor Stein <connor.stein@mail.mcgill.ca>
|
||||||
Corey Lin <514971757@qq.com>
|
Corey Lin <514971757@qq.com>
|
||||||
|
courtier <derinilter@gmail.com>
|
||||||
cpusoft <cpusoft@live.com>
|
cpusoft <cpusoft@live.com>
|
||||||
Crispin Flowerday <crispin@bitso.com>
|
Crispin Flowerday <crispin@bitso.com>
|
||||||
croath <croathliu@gmail.com>
|
croath <croathliu@gmail.com>
|
||||||
cui <523516579@qq.com>
|
cui <523516579@qq.com>
|
||||||
|
Dan DeGreef <dan.degreef@gmail.com>
|
||||||
Dan Kinsley <dan@joincivil.com>
|
Dan Kinsley <dan@joincivil.com>
|
||||||
|
Dan Sosedoff <dan.sosedoff@gmail.com>
|
||||||
Daniel A. Nagy <nagy.da@gmail.com>
|
Daniel A. Nagy <nagy.da@gmail.com>
|
||||||
|
Daniel Perez <daniel@perez.sh>
|
||||||
Daniel Sloof <goapsychadelic@gmail.com>
|
Daniel Sloof <goapsychadelic@gmail.com>
|
||||||
|
Darioush Jalali <darioush.jalali@avalabs.org>
|
||||||
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>
|
Dave McGregor <dave.s.mcgregor@gmail.com>
|
||||||
|
David Cai <davidcai1993@yahoo.com>
|
||||||
David Huie <dahuie@gmail.com>
|
David Huie <dahuie@gmail.com>
|
||||||
|
Denver <aeharvlee@gmail.com>
|
||||||
|
Derek Chiang <me@derekchiang.com>
|
||||||
Derek Gottfrid <derek@codecubed.com>
|
Derek Gottfrid <derek@codecubed.com>
|
||||||
|
Di Peng <pendyaaa@gmail.com>
|
||||||
|
Diederik Loerakker <proto@protolambda.com>
|
||||||
Diego Siqueira <DiSiqueira@users.noreply.github.com>
|
Diego Siqueira <DiSiqueira@users.noreply.github.com>
|
||||||
Diep Pham <mrfavadi@gmail.com>
|
Diep Pham <mrfavadi@gmail.com>
|
||||||
dipingxian2 <39109351+dipingxian2@users.noreply.github.com>
|
dipingxian2 <39109351+dipingxian2@users.noreply.github.com>
|
||||||
|
divergencetech <94644849+divergencetech@users.noreply.github.com>
|
||||||
dm4 <sunrisedm4@gmail.com>
|
dm4 <sunrisedm4@gmail.com>
|
||||||
Dmitrij Koniajev <dimchansky@gmail.com>
|
Dmitrij Koniajev <dimchansky@gmail.com>
|
||||||
Dmitry Shulyak <yashulyak@gmail.com>
|
Dmitry Shulyak <yashulyak@gmail.com>
|
||||||
|
Dmitry Zenovich <dzenovich@gmail.com>
|
||||||
Domino Valdano <dominoplural@gmail.com>
|
Domino Valdano <dominoplural@gmail.com>
|
||||||
Domino Valdano <jeff@okcupid.com>
|
|
||||||
Dragan Milic <dragan@netice9.com>
|
Dragan Milic <dragan@netice9.com>
|
||||||
dragonvslinux <35779158+dragononcrypto@users.noreply.github.com>
|
dragonvslinux <35779158+dragononcrypto@users.noreply.github.com>
|
||||||
|
Edgar Aroutiounian <edgar.factorial@gmail.com>
|
||||||
|
Eduard S <eduardsanou@posteo.net>
|
||||||
Egon Elbre <egonelbre@gmail.com>
|
Egon Elbre <egonelbre@gmail.com>
|
||||||
Elad <theman@elad.im>
|
Elad <theman@elad.im>
|
||||||
Eli <elihanover@yahoo.com>
|
Eli <elihanover@yahoo.com>
|
||||||
|
|
@ -86,131 +134,189 @@ Elias Naur <elias.naur@gmail.com>
|
||||||
Elliot Shepherd <elliot@identitii.com>
|
Elliot Shepherd <elliot@identitii.com>
|
||||||
Emil <mursalimovemeel@gmail.com>
|
Emil <mursalimovemeel@gmail.com>
|
||||||
emile <emile@users.noreply.github.com>
|
emile <emile@users.noreply.github.com>
|
||||||
Enrique Fynn <enriquefynn@gmail.com>
|
Emmanuel T Odeke <odeke@ualberta.ca>
|
||||||
|
Eng Zer Jun <engzerjun@gmail.com>
|
||||||
Enrique Fynn <me@enriquefynn.com>
|
Enrique Fynn <me@enriquefynn.com>
|
||||||
|
Enrique Ortiz <hi@enriqueortiz.dev>
|
||||||
EOS Classic <info@eos-classic.io>
|
EOS Classic <info@eos-classic.io>
|
||||||
Erichin <erichinbato@gmail.com>
|
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>
|
ethersphere <thesw@rm.eth>
|
||||||
|
Eugene Lepeico <eugenelepeico@gmail.com>
|
||||||
Eugene Valeyev <evgen.povt@gmail.com>
|
Eugene Valeyev <evgen.povt@gmail.com>
|
||||||
Evangelos Pappas <epappas@evalonlabs.com>
|
Evangelos Pappas <epappas@evalonlabs.com>
|
||||||
|
Everton Fraga <ev@ethereum.org>
|
||||||
Evgeny <awesome.observer@yandex.com>
|
Evgeny <awesome.observer@yandex.com>
|
||||||
Evgeny Danilenko <6655321@bk.ru>
|
Evgeny Danilenko <6655321@bk.ru>
|
||||||
evgk <evgeniy.kamyshev@gmail.com>
|
evgk <evgeniy.kamyshev@gmail.com>
|
||||||
|
Evolution404 <35091674+Evolution404@users.noreply.github.com>
|
||||||
|
EXEC <execvy@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>
|
||||||
|
Felipe Strozberg <48066928+FelStroz@users.noreply.github.com>
|
||||||
Felix Lange <fjl@twurst.com>
|
Felix Lange <fjl@twurst.com>
|
||||||
Ferenc Szabo <frncmx@gmail.com>
|
Ferenc Szabo <frncmx@gmail.com>
|
||||||
ferhat elmas <elmas.ferhat@gmail.com>
|
ferhat elmas <elmas.ferhat@gmail.com>
|
||||||
|
Ferran Borreguero <ferranbt@protonmail.com>
|
||||||
Fiisio <liangcszzu@163.com>
|
Fiisio <liangcszzu@163.com>
|
||||||
|
Fire Man <55934298+basdevelop@users.noreply.github.com>
|
||||||
|
flowerofdream <775654398@qq.com>
|
||||||
|
fomotrader <82184770+fomotrader@users.noreply.github.com>
|
||||||
|
ForLina <471133417@qq.com>
|
||||||
Frank Szendzielarz <33515470+FrankSzendzielarz@users.noreply.github.com>
|
Frank Szendzielarz <33515470+FrankSzendzielarz@users.noreply.github.com>
|
||||||
Frank Wang <eternnoir@gmail.com>
|
Frank Wang <eternnoir@gmail.com>
|
||||||
Franklin <mr_franklin@126.com>
|
Franklin <mr_franklin@126.com>
|
||||||
Furkan KAMACI <furkankamaci@gmail.com>
|
Furkan KAMACI <furkankamaci@gmail.com>
|
||||||
|
Fuyang Deng <dengfuyang@outlook.com>
|
||||||
GagziW <leon.stanko@rwth-aachen.de>
|
GagziW <leon.stanko@rwth-aachen.de>
|
||||||
Gary Rong <garyrong0905@gmail.com>
|
Gary Rong <garyrong0905@gmail.com>
|
||||||
|
Gautam Botrel <gautam.botrel@gmail.com>
|
||||||
George Ornbo <george@shapeshed.com>
|
George Ornbo <george@shapeshed.com>
|
||||||
|
Giuseppe Bertone <bertone.giuseppe@gmail.com>
|
||||||
|
Greg Colvin <greg@colvin.org>
|
||||||
Gregg Dourgarian <greggd@tempworks.com>
|
Gregg Dourgarian <greggd@tempworks.com>
|
||||||
|
Gregory Markou <16929357+GregTheGreek@users.noreply.github.com>
|
||||||
|
Guifel <toowik@gmail.com>
|
||||||
Guilherme Salgado <gsalgado@gmail.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>
|
GuiltyMorishita <morilliantblue@gmail.com>
|
||||||
|
Guruprasad Kamath <48196632+gurukamath@users.noreply.github.com>
|
||||||
Gus <yo@soygus.com>
|
Gus <yo@soygus.com>
|
||||||
Gustav Simonsson <gustav.simonsson@gmail.com>
|
Gustav Simonsson <gustav.simonsson@gmail.com>
|
||||||
Gísli Kristjánsson <gislik@hamstur.is>
|
Gísli Kristjánsson <gislik@hamstur.is>
|
||||||
Ha ĐANG <dvietha@gmail.com>
|
Ha ĐANG <dvietha@gmail.com>
|
||||||
HackyMiner <hackyminer@gmail.com>
|
HackyMiner <hackyminer@gmail.com>
|
||||||
hadv <dvietha@gmail.com>
|
hadv <dvietha@gmail.com>
|
||||||
|
Hanjiang Yu <delacroix.yu@gmail.com>
|
||||||
Hao Bryan Cheng <haobcheng@gmail.com>
|
Hao Bryan Cheng <haobcheng@gmail.com>
|
||||||
|
Hao Duan <duanhao0814@gmail.com>
|
||||||
HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
|
HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
|
||||||
|
Harry Dutton <me@bytejedi.com>
|
||||||
|
haryu703 <34744512+haryu703@users.noreply.github.com>
|
||||||
|
Hendrik Hofstadt <hendrik@nexantic.com>
|
||||||
Henning Diedrich <hd@eonblast.com>
|
Henning Diedrich <hd@eonblast.com>
|
||||||
|
henopied <13500516+henopied@users.noreply.github.com>
|
||||||
|
hero5512 <lvshuaino@gmail.com>
|
||||||
holisticode <holistic.computing@gmail.com>
|
holisticode <holistic.computing@gmail.com>
|
||||||
Hongbin Mao <hello2mao@gmail.com>
|
Hongbin Mao <hello2mao@gmail.com>
|
||||||
Hsien-Tang Kao <htkao@pm.me>
|
Hsien-Tang Kao <htkao@pm.me>
|
||||||
|
hsyodyssey <47173566+hsyodyssey@users.noreply.github.com>
|
||||||
Husam Ibrahim <39692071+HusamIbrahim@users.noreply.github.com>
|
Husam Ibrahim <39692071+HusamIbrahim@users.noreply.github.com>
|
||||||
|
Hwanjo Heo <34005989+hwanjo@users.noreply.github.com>
|
||||||
hydai <z54981220@gmail.com>
|
hydai <z54981220@gmail.com>
|
||||||
Hyung-Kyu Hqueue Choi <hyungkyu.choi@gmail.com>
|
Hyung-Kyu Hqueue Choi <hyungkyu.choi@gmail.com>
|
||||||
|
Håvard Anda Estensen <haavard.ae@gmail.com>
|
||||||
Ian Macalinao <me@ian.pw>
|
Ian Macalinao <me@ian.pw>
|
||||||
Ian Norden <iannordenn@gmail.com>
|
Ian Norden <iannordenn@gmail.com>
|
||||||
|
icodezjb <icodezjb@163.com>
|
||||||
|
Ikko Ashimine <eltociear@gmail.com>
|
||||||
|
Ilan Gitter <8359193+gitteri@users.noreply.github.com>
|
||||||
|
ImanSharaf <78227895+ImanSharaf@users.noreply.github.com>
|
||||||
Isidoro Ghezzi <isidoro.ghezzi@icloud.com>
|
Isidoro Ghezzi <isidoro.ghezzi@icloud.com>
|
||||||
Iskander (Alex) Sharipov <quasilyte@gmail.com>
|
Iskander (Alex) Sharipov <quasilyte@gmail.com>
|
||||||
|
Ivan Bogatyy <bogatyi@gmail.com>
|
||||||
Ivan Daniluk <ivan.daniluk@gmail.com>
|
Ivan Daniluk <ivan.daniluk@gmail.com>
|
||||||
Ivo Georgiev <ivo@strem.io>
|
Ivo Georgiev <ivo@strem.io>
|
||||||
|
jacksoom <lifengliu1994@gmail.com>
|
||||||
Jae Kwon <jkwon.work@gmail.com>
|
Jae Kwon <jkwon.work@gmail.com>
|
||||||
|
James Prestwich <10149425+prestwich@users.noreply.github.com>
|
||||||
Jamie Pitts <james.pitts@gmail.com>
|
Jamie Pitts <james.pitts@gmail.com>
|
||||||
Janos Guljas <janos@resenje.org>
|
Janoš Guljaš <janos@resenje.org>
|
||||||
Janoš Guljaš <janos@users.noreply.github.com>
|
Jared Wasinger <j-wasinger@hotmail.com>
|
||||||
Jason Carver <jacarver@linkedin.com>
|
Jason Carver <jacarver@linkedin.com>
|
||||||
Javier Peletier <jm@epiclabs.io>
|
Javier Peletier <jm@epiclabs.io>
|
||||||
Javier Peletier <jpeletier@users.noreply.github.com>
|
|
||||||
Javier Sagredo <jasataco@gmail.com>
|
Javier Sagredo <jasataco@gmail.com>
|
||||||
Jay <codeholic.arena@gmail.com>
|
Jay <codeholic.arena@gmail.com>
|
||||||
Jay Guo <guojiannan1101@gmail.com>
|
Jay Guo <guojiannan1101@gmail.com>
|
||||||
Jaynti Kanani <jdkanani@gmail.com>
|
Jaynti Kanani <jdkanani@gmail.com>
|
||||||
Jeff Prestes <jeffprestes@gmail.com>
|
Jeff Prestes <jeffprestes@gmail.com>
|
||||||
Jeff R. Allen <jra@nella.org>
|
Jeff R. Allen <jra@nella.org>
|
||||||
|
Jeff Wentworth <jeff@curvegrid.com>
|
||||||
Jeffery Robert Walsh <rlxrlps@gmail.com>
|
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 McNevin <jeremy.mcnevin@optum.com>
|
||||||
Jeremy Schlatter <jeremy.schlatter@gmail.com>
|
Jeremy Schlatter <jeremy.schlatter@gmail.com>
|
||||||
Jerzy Lasyk <jerzylasyk@gmail.com>
|
Jerzy Lasyk <jerzylasyk@gmail.com>
|
||||||
|
Jesse Tane <jesse.tane@gmail.com>
|
||||||
Jia Chenhui <jiachenhui1989@gmail.com>
|
Jia Chenhui <jiachenhui1989@gmail.com>
|
||||||
Jim McDonald <Jim@mcdee.net>
|
Jim McDonald <Jim@mcdee.net>
|
||||||
|
jk-jeongkyun <45347815+jeongkyun-oh@users.noreply.github.com>
|
||||||
jkcomment <jkcomment@gmail.com>
|
jkcomment <jkcomment@gmail.com>
|
||||||
|
JoeGruffins <34998433+JoeGruffins@users.noreply.github.com>
|
||||||
Joel Burget <joelburget@gmail.com>
|
Joel Burget <joelburget@gmail.com>
|
||||||
John C. Vernaleo <john@netpurgatory.com>
|
John C. Vernaleo <john@netpurgatory.com>
|
||||||
|
John Difool <johndifoolpi@gmail.com>
|
||||||
Johns Beharry <johns@peakshift.com>
|
Johns Beharry <johns@peakshift.com>
|
||||||
Jonas <felberj@users.noreply.github.com>
|
Jonas <felberj@users.noreply.github.com>
|
||||||
Jonathan Brown <jbrown@bluedroplet.com>
|
Jonathan Brown <jbrown@bluedroplet.com>
|
||||||
|
Jonathan Chappelow <chappjc@users.noreply.github.com>
|
||||||
|
Jonathan Gimeno <jgimeno@gmail.com>
|
||||||
JoranHonig <JoranHonig@users.noreply.github.com>
|
JoranHonig <JoranHonig@users.noreply.github.com>
|
||||||
Jordan Krage <jmank88@gmail.com>
|
Jordan Krage <jmank88@gmail.com>
|
||||||
|
Jorropo <jorropo.pgm@gmail.com>
|
||||||
Joseph Chow <ethereum@outlook.com>
|
Joseph Chow <ethereum@outlook.com>
|
||||||
|
Joshua Colvin <jcolvin@offchainlabs.com>
|
||||||
|
Joshua Gutow <jbgutow@gmail.com>
|
||||||
|
jovijovi <mageyul@hotmail.com>
|
||||||
jtakalai <juuso.takalainen@streamr.com>
|
jtakalai <juuso.takalainen@streamr.com>
|
||||||
JU HYEONG PARK <dkdkajej@gmail.com>
|
JU HYEONG PARK <dkdkajej@gmail.com>
|
||||||
|
Julian Y <jyap808@users.noreply.github.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>
|
Justus <jus@gtsbr.org>
|
||||||
|
Kawashima <91420903+sscodereth@users.noreply.github.com>
|
||||||
ken10100147 <sunhongping@kanjian.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>
|
Kenso Trabing <ktrabing@acm.org>
|
||||||
Kevin <denk.kevin@web.de>
|
Kevin <denk.kevin@web.de>
|
||||||
kevin.xu <cming.xu@gmail.com>
|
kevin.xu <cming.xu@gmail.com>
|
||||||
|
KibGzr <kibgzr@gmail.com>
|
||||||
kiel barry <kiel.j.barry@gmail.com>
|
kiel barry <kiel.j.barry@gmail.com>
|
||||||
|
kilic <onurkilic1004@gmail.com>
|
||||||
kimmylin <30611210+kimmylin@users.noreply.github.com>
|
kimmylin <30611210+kimmylin@users.noreply.github.com>
|
||||||
Kitten King <53072918+kittenking@users.noreply.github.com>
|
Kitten King <53072918+kittenking@users.noreply.github.com>
|
||||||
knarfeh <hejun1874@gmail.com>
|
knarfeh <hejun1874@gmail.com>
|
||||||
Kobi Gurkan <kobigurk@gmail.com>
|
Kobi Gurkan <kobigurk@gmail.com>
|
||||||
|
komika <komika@komika.org>
|
||||||
Konrad Feldmeier <konrad@brainbot.com>
|
Konrad Feldmeier <konrad@brainbot.com>
|
||||||
Kris Shinn <raggamuffin.music@gmail.com>
|
Kris Shinn <raggamuffin.music@gmail.com>
|
||||||
|
Kristofer Peterson <svenski123@users.noreply.github.com>
|
||||||
|
Kumar Anirudha <mail@anirudha.dev>
|
||||||
Kurkó Mihály <kurkomisi@users.noreply.github.com>
|
Kurkó Mihály <kurkomisi@users.noreply.github.com>
|
||||||
Kushagra Sharma <ksharm01@gmail.com>
|
Kushagra Sharma <ksharm01@gmail.com>
|
||||||
Kwuaint <34888408+kwuaint@users.noreply.github.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>
|
Lee Bousfield <ljbousfield@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>
|
LeoLiao <leofantast@gmail.com>
|
||||||
Lewis Marshall <lewis@lmars.net>
|
Lewis Marshall <lewis@lmars.net>
|
||||||
lhendre <lhendre2@gmail.com>
|
lhendre <lhendre2@gmail.com>
|
||||||
Liang Ma <liangma.ul@gmail.com>
|
Li Dongwei <lidw1988@126.com>
|
||||||
Liang Ma <liangma@liangbit.com>
|
Liang Ma <liangma@liangbit.com>
|
||||||
Liang ZOU <liang.d.zou@gmail.com>
|
Liang ZOU <liang.d.zou@gmail.com>
|
||||||
|
libby kent <viskovitzzz@gmail.com>
|
||||||
libotony <liboliqi@gmail.com>
|
libotony <liboliqi@gmail.com>
|
||||||
|
LieutenantRoger <dijsky_2015@hotmail.com>
|
||||||
ligi <ligi@ligi.de>
|
ligi <ligi@ligi.de>
|
||||||
Lio李欧 <lionello@users.noreply.github.com>
|
Lio李欧 <lionello@users.noreply.github.com>
|
||||||
|
lmittmann <lmittmann@users.noreply.github.com>
|
||||||
Lorenzo Manacorda <lorenzo@kinvolk.io>
|
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>
|
||||||
|
Lucas Hendren <lhendre2@gmail.com>
|
||||||
|
lzhfromustc <43191155+lzhfromustc@users.noreply.github.com>
|
||||||
Magicking <s@6120.eu>
|
Magicking <s@6120.eu>
|
||||||
manlio <manlio.poltronieri@gmail.com>
|
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>
|
||||||
|
Mariano Cortesi <mcortesi@gmail.com>
|
||||||
Marius van der Wijden <m.vanderwijden@live.de>
|
Marius van der Wijden <m.vanderwijden@live.de>
|
||||||
Mark <markya0616@gmail.com>
|
Mark <markya0616@gmail.com>
|
||||||
Mark Rushakoff <mark.rushakoff@gmail.com>
|
Mark Rushakoff <mark.rushakoff@gmail.com>
|
||||||
|
|
@ -218,108 +324,193 @@ mark.lin <mark@maicoin.com>
|
||||||
Martin Alex Philip Dawson <u1356770@gmail.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>
|
Martin Klepsch <martinklepsch@googlemail.com>
|
||||||
|
Martin Lundfall <martin.lundfall@protonmail.com>
|
||||||
|
Martin Michlmayr <tbm@cyrius.com>
|
||||||
|
Martin Redmond <21436+reds@users.noreply.github.com>
|
||||||
|
Mason Fischer <mason@kissr.co>
|
||||||
|
Mateusz Morusiewicz <11313015+Ruteri@users.noreply.github.com>
|
||||||
Mats Julian Olsen <mats@plysjbyen.net>
|
Mats Julian Olsen <mats@plysjbyen.net>
|
||||||
|
Matt Garnett <14004106+lightclient@users.noreply.github.com>
|
||||||
Matt K <1036969+mkrump@users.noreply.github.com>
|
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@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>
|
Max Sistemich <mafrasi2@googlemail.com>
|
||||||
|
Maxim Zhiburt <zhiburt@gmail.com>
|
||||||
Maximilian Meister <mmeister@suse.de>
|
Maximilian Meister <mmeister@suse.de>
|
||||||
|
me020523 <me020523@gmail.com>
|
||||||
|
Melvin Junhee Woo <melvin.woo@groundx.xyz>
|
||||||
|
meowsbits <b5c6@protonmail.com>
|
||||||
Micah Zoltu <micah@zoltu.net>
|
Micah Zoltu <micah@zoltu.net>
|
||||||
|
Michael Forney <mforney@mforney.org>
|
||||||
|
Michael Riabzev <michael@starkware.co>
|
||||||
Michael Ruminer <michael.ruminer+github@gmail.com>
|
Michael Ruminer <michael.ruminer+github@gmail.com>
|
||||||
|
michael1011 <me@michael1011.at>
|
||||||
Miguel Mota <miguelmota2@gmail.com>
|
Miguel Mota <miguelmota2@gmail.com>
|
||||||
|
Mike Burr <mburr@nightmare.com>
|
||||||
|
Mikhail Mikheev <mmvsha73@gmail.com>
|
||||||
|
milesvant <milesvant@gmail.com>
|
||||||
|
Miro <mirokuratczyk@users.noreply.github.com>
|
||||||
Miya Chen <miyatlchen@gmail.com>
|
Miya Chen <miyatlchen@gmail.com>
|
||||||
Mohanson <mohanson@outlook.com>
|
Mohanson <mohanson@outlook.com>
|
||||||
mr_franklin <mr_franklin@126.com>
|
mr_franklin <mr_franklin@126.com>
|
||||||
|
Mudit Gupta <guptamudit@ymail.com>
|
||||||
Mymskmkt <1847234666@qq.com>
|
Mymskmkt <1847234666@qq.com>
|
||||||
Nalin Bhardwaj <nalinbhardwaj@nibnalin.me>
|
Nalin Bhardwaj <nalinbhardwaj@nibnalin.me>
|
||||||
|
Natsu Kagami <natsukagami@gmail.com>
|
||||||
Nchinda Nchinda <nchinda2@gmail.com>
|
Nchinda Nchinda <nchinda2@gmail.com>
|
||||||
|
nebojsa94 <nebojsa94@users.noreply.github.com>
|
||||||
necaremus <necaremus@gmail.com>
|
necaremus <necaremus@gmail.com>
|
||||||
|
nedifi <103940716+nedifi@users.noreply.github.com>
|
||||||
needkane <604476380@qq.com>
|
needkane <604476380@qq.com>
|
||||||
Nguyen Kien Trung <trung.n.k@gmail.com>
|
Nguyen Kien Trung <trung.n.k@gmail.com>
|
||||||
Nguyen Sy Thanh Son <thanhson1085@gmail.com>
|
Nguyen Sy Thanh Son <thanhson1085@gmail.com>
|
||||||
|
Nic Jansma <nic@nicj.net>
|
||||||
Nick Dodson <silentcicero@outlook.com>
|
Nick Dodson <silentcicero@outlook.com>
|
||||||
Nick Johnson <arachnid@notdot.net>
|
Nick Johnson <arachnid@notdot.net>
|
||||||
|
Nicolas Feignon <nfeignon@gmail.com>
|
||||||
Nicolas Guillaume <gunicolas@sqli.com>
|
Nicolas Guillaume <gunicolas@sqli.com>
|
||||||
|
Nikita Kozhemyakin <enginegl.ec@gmail.com>
|
||||||
|
Nikola Madjarevic <nikola.madjarevic@gmail.com>
|
||||||
Nilesh Trivedi <nilesh@hypertrack.io>
|
Nilesh Trivedi <nilesh@hypertrack.io>
|
||||||
Nimrod Gutman <nimrod.gutman@gmail.com>
|
Nimrod Gutman <nimrod.gutman@gmail.com>
|
||||||
|
Nishant Das <nishdas93@gmail.com>
|
||||||
njupt-moon <1015041018@njupt.edu.cn>
|
njupt-moon <1015041018@njupt.edu.cn>
|
||||||
nkbai <nkbai@163.com>
|
nkbai <nkbai@163.com>
|
||||||
|
noam-alchemy <76969113+noam-alchemy@users.noreply.github.com>
|
||||||
nobody <ddean2009@163.com>
|
nobody <ddean2009@163.com>
|
||||||
Noman <noman@noman.land>
|
Noman <noman@noman.land>
|
||||||
|
nujabes403 <nujabes403@gmail.com>
|
||||||
|
Nye Liu <nyet@nyet.org>
|
||||||
Oleg Kovalov <iamolegkovalov@gmail.com>
|
Oleg Kovalov <iamolegkovalov@gmail.com>
|
||||||
Oli Bye <olibye@users.noreply.github.com>
|
Oli Bye <olibye@users.noreply.github.com>
|
||||||
|
Oliver Tale-Yazdi <oliver@perun.network>
|
||||||
|
Olivier Hervieu <olivier.hervieu@gmail.com>
|
||||||
|
Or Neeman <oneeman@gmail.com>
|
||||||
|
Osoro Bironga <fanosoro@gmail.com>
|
||||||
Osuke <arget-fee.free.dgm@hotmail.co.jp>
|
Osuke <arget-fee.free.dgm@hotmail.co.jp>
|
||||||
|
Pantelis Peslis <pespantelis@gmail.com>
|
||||||
|
Pascal Dierich <pascal@merkleplant.xyz>
|
||||||
|
Patrick O'Grady <prohb125@gmail.com>
|
||||||
|
Pau <pau@dabax.net>
|
||||||
Paul Berg <hello@paulrberg.com>
|
Paul Berg <hello@paulrberg.com>
|
||||||
Paul Litvak <litvakpol@012.net.il>
|
Paul Litvak <litvakpol@012.net.il>
|
||||||
|
Paul-Armand Verhaegen <paularmand.verhaegen@gmail.com>
|
||||||
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 Gomes <otherview@gmail.com>
|
||||||
Pedro Pombeiro <PombeirP@users.noreply.github.com>
|
Pedro Pombeiro <PombeirP@users.noreply.github.com>
|
||||||
Peter Broadhurst <peter@themumbles.net>
|
Peter Broadhurst <peter@themumbles.net>
|
||||||
|
peter cresswell <pcresswell@gmail.com>
|
||||||
Peter Pratscher <pratscher@gmail.com>
|
Peter Pratscher <pratscher@gmail.com>
|
||||||
|
Peter Simard <petesimard56@gmail.com>
|
||||||
Petr Mikusek <petr@mikusek.info>
|
Petr Mikusek <petr@mikusek.info>
|
||||||
Philip Schlump <pschlump@gmail.com>
|
Philip Schlump <pschlump@gmail.com>
|
||||||
Pierre Neter <pierreneter@gmail.com>
|
Pierre Neter <pierreneter@gmail.com>
|
||||||
|
Pierre R <p.rousset@gmail.com>
|
||||||
|
piersy <pierspowlesland@gmail.com>
|
||||||
PilkyuJung <anothel1@naver.com>
|
PilkyuJung <anothel1@naver.com>
|
||||||
protolambda <proto@protolambda.com>
|
Piotr Dyraga <piotr.dyraga@keep.network>
|
||||||
|
ploui <64719999+ploui@users.noreply.github.com>
|
||||||
|
Preston Van Loon <preston@prysmaticlabs.com>
|
||||||
|
Prince Sinha <sinhaprince013@gmail.com>
|
||||||
Péter Szilágyi <peterke@gmail.com>
|
Péter Szilágyi <peterke@gmail.com>
|
||||||
qd-ethan <31876119+qdgogogo@users.noreply.github.com>
|
qd-ethan <31876119+qdgogogo@users.noreply.github.com>
|
||||||
|
Qian Bin <cola.tin.com@gmail.com>
|
||||||
|
Quest Henkart <qhenkart@gmail.com>
|
||||||
|
Rachel Franks <nfranks@protonmail.com>
|
||||||
|
Rafael Matias <rafael@skyle.net>
|
||||||
Raghav Sood <raghavsood@gmail.com>
|
Raghav Sood <raghavsood@gmail.com>
|
||||||
Ralph Caraveo <deckarep@gmail.com>
|
Ralph Caraveo <deckarep@gmail.com>
|
||||||
Ralph Caraveo III <deckarep@gmail.com>
|
|
||||||
Ramesh Nair <ram@hiddentao.com>
|
Ramesh Nair <ram@hiddentao.com>
|
||||||
|
rangzen <public@l-homme.com>
|
||||||
reinerRubin <tolstov.georgij@gmail.com>
|
reinerRubin <tolstov.georgij@gmail.com>
|
||||||
|
Rene Lubov <41963722+renaynay@users.noreply.github.com>
|
||||||
rhaps107 <dod-source@yandex.ru>
|
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>
|
||||||
|
Rick <rick.no@groundx.xyz>
|
||||||
RJ Catalano <catalanor0220@gmail.com>
|
RJ Catalano <catalanor0220@gmail.com>
|
||||||
Rob <robert@rojotek.com>
|
Rob <robert@rojotek.com>
|
||||||
Rob Mulholand <rmulholand@8thlight.com>
|
Rob Mulholand <rmulholand@8thlight.com>
|
||||||
Robert Zaremba <robert.zaremba@scale-it.pl>
|
Robert Zaremba <robert@zaremba.ch>
|
||||||
Roc Yu <rociiu0112@gmail.com>
|
Roc Yu <rociiu0112@gmail.com>
|
||||||
|
Roman Mazalov <83914728+gopherxyz@users.noreply.github.com>
|
||||||
|
Ross <9055337+Chadsr@users.noreply.github.com>
|
||||||
Runchao Han <elvisage941102@gmail.com>
|
Runchao Han <elvisage941102@gmail.com>
|
||||||
Russ Cox <rsc@golang.org>
|
Russ Cox <rsc@golang.org>
|
||||||
Ryan Schneider <ryanleeschneider@gmail.com>
|
Ryan Schneider <ryanleeschneider@gmail.com>
|
||||||
|
ryanc414 <ryan@tokencard.io>
|
||||||
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>
|
salanfe <salanfe@users.noreply.github.com>
|
||||||
|
Sam <39165351+Xia-Sam@users.noreply.github.com>
|
||||||
|
Sammy Libre <7374093+sammy007@users.noreply.github.com>
|
||||||
Samuel Marks <samuelmarks@gmail.com>
|
Samuel Marks <samuelmarks@gmail.com>
|
||||||
|
sanskarkhare <sanskarkhare47@gmail.com>
|
||||||
Sarlor <kinsleer@outlook.com>
|
Sarlor <kinsleer@outlook.com>
|
||||||
Sasuke1964 <neilperry1964@gmail.com>
|
Sasuke1964 <neilperry1964@gmail.com>
|
||||||
|
Satpal <28562234+SatpalSandhu61@users.noreply.github.com>
|
||||||
Saulius Grigaitis <saulius@necolt.com>
|
Saulius Grigaitis <saulius@necolt.com>
|
||||||
Sean <darcys22@gmail.com>
|
Sean <darcys22@gmail.com>
|
||||||
Sheldon <11510383@mail.sustc.edu.cn>
|
Serhat Şevki Dinçer <jfcgauss@gmail.com>
|
||||||
Sheldon <374662347@qq.com>
|
Shane Bammel <sjb933@gmail.com>
|
||||||
|
shawn <36943337+lxex@users.noreply.github.com>
|
||||||
|
shigeyuki azuchi <azuchi@chaintope.com>
|
||||||
|
Shihao Xia <charlesxsh@hotmail.com>
|
||||||
|
Shiming <codingmylife@gmail.com>
|
||||||
Shintaro Kaneko <kaneshin0120@gmail.com>
|
Shintaro Kaneko <kaneshin0120@gmail.com>
|
||||||
|
shiqinfeng1 <150627601@qq.com>
|
||||||
Shuai Qi <qishuai231@gmail.com>
|
Shuai Qi <qishuai231@gmail.com>
|
||||||
|
Shude Li <islishude@gmail.com>
|
||||||
Shunsuke Watanabe <ww.shunsuke@gmail.com>
|
Shunsuke Watanabe <ww.shunsuke@gmail.com>
|
||||||
silence <wangsai.silence@qq.com>
|
silence <wangsai.silence@qq.com>
|
||||||
Simon Jentzsch <simon@slock.it>
|
Simon Jentzsch <simon@slock.it>
|
||||||
|
Sina Mahmoodi <1591639+s1na@users.noreply.github.com>
|
||||||
|
sixdays <lj491685571@126.com>
|
||||||
|
SjonHortensius <SjonHortensius@users.noreply.github.com>
|
||||||
|
Slava Karpenko <slavikus@gmail.com>
|
||||||
slumber1122 <slumber1122@gmail.com>
|
slumber1122 <slumber1122@gmail.com>
|
||||||
Smilenator <yurivanenko@yandex.ru>
|
Smilenator <yurivanenko@yandex.ru>
|
||||||
|
soc1c <soc1c@users.noreply.github.com>
|
||||||
Sorin Neacsu <sorin.neacsu@gmail.com>
|
Sorin Neacsu <sorin.neacsu@gmail.com>
|
||||||
|
Sparty <vignesh.crysis@gmail.com>
|
||||||
Stein Dekker <dekker.stein@gmail.com>
|
Stein Dekker <dekker.stein@gmail.com>
|
||||||
Steve Gattuso <steve@stevegattuso.me>
|
Steve Gattuso <steve@stevegattuso.me>
|
||||||
Steve Ruckdashel <steve.ruckdashel@gmail.com>
|
Steve Ruckdashel <steve.ruckdashel@gmail.com>
|
||||||
Steve Waldman <swaldman@mchange.com>
|
Steve Waldman <swaldman@mchange.com>
|
||||||
|
Steven E. Harris <seh@panix.com>
|
||||||
Steven Roose <stevenroose@gmail.com>
|
Steven Roose <stevenroose@gmail.com>
|
||||||
stompesi <stompesi@gmail.com>
|
stompesi <stompesi@gmail.com>
|
||||||
stormpang <jialinpeng@vip.qq.com>
|
stormpang <jialinpeng@vip.qq.com>
|
||||||
sunxiaojun2014 <sunxiaojun-xy@360.cn>
|
sunxiaojun2014 <sunxiaojun-xy@360.cn>
|
||||||
|
Suriyaa Sundararuban <isc.suriyaa@gmail.com>
|
||||||
|
Sylvain Laurent <s@6120.eu>
|
||||||
|
Taeik Lim <sibera21@gmail.com>
|
||||||
tamirms <tamir@trello.com>
|
tamirms <tamir@trello.com>
|
||||||
|
Tangui Clairet <tangui.clairet@gmail.com>
|
||||||
|
Tatsuya Shimoda <tacoo@users.noreply.github.com>
|
||||||
Taylor Gerring <taylor.gerring@gmail.com>
|
Taylor Gerring <taylor.gerring@gmail.com>
|
||||||
TColl <38299499+TColl@users.noreply.github.com>
|
TColl <38299499+TColl@users.noreply.github.com>
|
||||||
terasum <terasum@163.com>
|
terasum <terasum@163.com>
|
||||||
|
tgyKomgo <52910426+tgyKomgo@users.noreply.github.com>
|
||||||
|
Thad Guidry <thadguidry@gmail.com>
|
||||||
Thomas Bocek <tom@tomp2p.net>
|
Thomas Bocek <tom@tomp2p.net>
|
||||||
thomasmodeneis <thomas.modeneis@gmail.com>
|
thomasmodeneis <thomas.modeneis@gmail.com>
|
||||||
thumb8432 <thumb8432@gmail.com>
|
thumb8432 <thumb8432@gmail.com>
|
||||||
Ti Zhou <tizhou1986@gmail.com>
|
Ti Zhou <tizhou1986@gmail.com>
|
||||||
|
tia-99 <67107070+tia-99@users.noreply.github.com>
|
||||||
|
Tim Cooijmans <timcooijmans@gmail.com>
|
||||||
|
Tobias Hildebrandt <79341166+tobias-hildebrandt@users.noreply.github.com>
|
||||||
Tosh Camille <tochecamille@gmail.com>
|
Tosh Camille <tochecamille@gmail.com>
|
||||||
tsarpaul <Litvakpol@012.net.il>
|
tsarpaul <Litvakpol@012.net.il>
|
||||||
|
Tyler Chambers <2775339+tylerchambers@users.noreply.github.com>
|
||||||
tzapu <alex@tzapu.com>
|
tzapu <alex@tzapu.com>
|
||||||
|
ucwong <ucwong@126.com>
|
||||||
|
uji <49834542+uji@users.noreply.github.com>
|
||||||
ult-bobonovski <alex@ultiledger.io>
|
ult-bobonovski <alex@ultiledger.io>
|
||||||
|
Valentin Trinqué <ValentinTrinque@users.noreply.github.com>
|
||||||
Valentin Wüstholz <wuestholz@gmail.com>
|
Valentin Wüstholz <wuestholz@gmail.com>
|
||||||
Vedhavyas Singareddi <vedhavyas.singareddi@gmail.com>
|
Vedhavyas Singareddi <vedhavyas.singareddi@gmail.com>
|
||||||
Victor Farazdagi <simple.square@gmail.com>
|
Victor Farazdagi <simple.square@gmail.com>
|
||||||
|
|
@ -330,40 +521,71 @@ Ville Sundell <github@solarius.fi>
|
||||||
vim88 <vim88vim88@gmail.com>
|
vim88 <vim88vim88@gmail.com>
|
||||||
Vincent G <caktux@gmail.com>
|
Vincent G <caktux@gmail.com>
|
||||||
Vincent Serpoul <vincent@serpoul.com>
|
Vincent Serpoul <vincent@serpoul.com>
|
||||||
|
Vinod Damle <vdamle@users.noreply.github.com>
|
||||||
Vitalik Buterin <v@buterin.com>
|
Vitalik Buterin <v@buterin.com>
|
||||||
Vitaly Bogdanov <vsbogd@gmail.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 Bokov <razum2um@mail.ru>
|
||||||
Vlad Gluhovsky <gluk256@users.noreply.github.com>
|
Vlad Gluhovsky <gluk256@gmail.com>
|
||||||
|
Ward Bradt <wardbradt5@gmail.com>
|
||||||
|
Water <44689567+codeoneline@users.noreply.github.com>
|
||||||
|
wbt <wbt@users.noreply.github.com>
|
||||||
weimumu <934657014@qq.com>
|
weimumu <934657014@qq.com>
|
||||||
Wenbiao Zheng <delweng@gmail.com>
|
Wenbiao Zheng <delweng@gmail.com>
|
||||||
|
Wenshao Zhong <wzhong20@uic.edu>
|
||||||
|
Will Villanueva <hello@willvillanueva.com>
|
||||||
|
William Morriss <wjmelements@gmail.com>
|
||||||
William Setzer <bootstrapsetzer@gmail.com>
|
William Setzer <bootstrapsetzer@gmail.com>
|
||||||
williambannas <wrschwartz@wpi.edu>
|
williambannas <wrschwartz@wpi.edu>
|
||||||
|
wuff1996 <33193253+wuff1996@users.noreply.github.com>
|
||||||
Wuxiang <wuxiangzhou2010@gmail.com>
|
Wuxiang <wuxiangzhou2010@gmail.com>
|
||||||
|
Xiaobing Jiang <s7v7nislands@gmail.com>
|
||||||
xiekeyang <xiekeyang@users.noreply.github.com>
|
xiekeyang <xiekeyang@users.noreply.github.com>
|
||||||
xincaosu <xincaosu@126.com>
|
xincaosu <xincaosu@126.com>
|
||||||
|
xinluyin <31590468+xinluyin@users.noreply.github.com>
|
||||||
|
Xudong Liu <33193253+r1cs@users.noreply.github.com>
|
||||||
|
xwjack <XWJACK@users.noreply.github.com>
|
||||||
yahtoo <yahtoo.ma@gmail.com>
|
yahtoo <yahtoo.ma@gmail.com>
|
||||||
|
Yang Hau <vulxj0j8j8@gmail.com>
|
||||||
YaoZengzeng <yaozengzeng@zju.edu.cn>
|
YaoZengzeng <yaozengzeng@zju.edu.cn>
|
||||||
YH-Zhou <yanhong.zhou05@gmail.com>
|
YH-Zhou <yanhong.zhou05@gmail.com>
|
||||||
|
Yihau Chen <a122092487@gmail.com>
|
||||||
Yohann Léon <sybiload@gmail.com>
|
Yohann Léon <sybiload@gmail.com>
|
||||||
Yoichi Hirai <i@yoichihirai.com>
|
Yoichi Hirai <i@yoichihirai.com>
|
||||||
|
Yole <007yuyue@gmail.com>
|
||||||
Yondon Fu <yondon.fu@gmail.com>
|
Yondon Fu <yondon.fu@gmail.com>
|
||||||
YOSHIDA Masanori <masanori.yoshida@gmail.com>
|
YOSHIDA Masanori <masanori.yoshida@gmail.com>
|
||||||
yoza <yoza.is12s@gmail.com>
|
yoza <yoza.is12s@gmail.com>
|
||||||
|
yumiel yoomee1313 <yumiel.ko@groundx.xyz>
|
||||||
Yusup <awklsgrep@gmail.com>
|
Yusup <awklsgrep@gmail.com>
|
||||||
|
yutianwu <wzxingbupt@gmail.com>
|
||||||
|
ywzqwwt <39263032+ywzqwwt@users.noreply.github.com>
|
||||||
|
zaccoding <zaccoding725@gmail.com>
|
||||||
Zach <zach.ramsay@gmail.com>
|
Zach <zach.ramsay@gmail.com>
|
||||||
|
Zachinquarantine <Zachinquarantine@protonmail.com>
|
||||||
zah <zahary@gmail.com>
|
zah <zahary@gmail.com>
|
||||||
Zahoor Mohamed <zahoor@zahoor.in>
|
Zahoor Mohamed <zahoor@zahoor.in>
|
||||||
Zak Cole <zak@beattiecole.com>
|
Zak Cole <zak@beattiecole.com>
|
||||||
|
zcheng9 <zcheng9@hawk.iit.edu>
|
||||||
zer0to0ne <36526113+zer0to0ne@users.noreply.github.com>
|
zer0to0ne <36526113+zer0to0ne@users.noreply.github.com>
|
||||||
|
zgfzgf <48779939+zgfzgf@users.noreply.github.com>
|
||||||
|
Zhang Zhuo <mycinbrin@gmail.com>
|
||||||
|
zhangsoledad <787953403@qq.com>
|
||||||
|
zhaochonghe <41711151+zhaochonghe@users.noreply.github.com>
|
||||||
Zhenguo Niu <Niu.ZGlinux@gmail.com>
|
Zhenguo Niu <Niu.ZGlinux@gmail.com>
|
||||||
|
zhiqiangxu <652732310@qq.com>
|
||||||
|
Zhou Zhiyao <ZHOU0250@e.ntu.edu.sg>
|
||||||
|
Ziyuan Zhong <zzy.albert@163.com>
|
||||||
Zoe Nolan <github@zoenolan.org>
|
Zoe Nolan <github@zoenolan.org>
|
||||||
|
Zou Guangxian <zouguangxian@gmail.com>
|
||||||
Zsolt Felföldi <zsfelfoldi@gmail.com>
|
Zsolt Felföldi <zsfelfoldi@gmail.com>
|
||||||
Łukasz Kurowski <crackcomm@users.noreply.github.com>
|
Łukasz Kurowski <crackcomm@users.noreply.github.com>
|
||||||
|
Łukasz Zimnoch <lukaszzimnoch1994@gmail.com>
|
||||||
ΞTHΞЯSPHΞЯΞ <{viktor.tron,nagydani,zsfelfoldi}@gmail.com>
|
ΞTHΞЯSPHΞЯΞ <{viktor.tron,nagydani,zsfelfoldi}@gmail.com>
|
||||||
Максим Чусовлянов <mchusovlianov@gmail.com>
|
Максим Чусовлянов <mchusovlianov@gmail.com>
|
||||||
大彬 <hz_stb@163.com>
|
大彬 <hz_stb@163.com>
|
||||||
|
沉风 <myself659@users.noreply.github.com>
|
||||||
贺鹏飞 <hpf@hackerful.cn>
|
贺鹏飞 <hpf@hackerful.cn>
|
||||||
|
陈佳 <chenjiablog@gmail.com>
|
||||||
유용환 <33824408+eric-yoo@users.noreply.github.com>
|
유용환 <33824408+eric-yoo@users.noreply.github.com>
|
||||||
|
|
|
||||||
1
Makefile
1
Makefile
|
|
@ -35,6 +35,7 @@ protoc:
|
||||||
generate-mocks:
|
generate-mocks:
|
||||||
go generate mockgen -destination=./tests/bor/mocks/IHeimdallClient.go -package=mocks ./consensus/bor IHeimdallClient
|
go generate mockgen -destination=./tests/bor/mocks/IHeimdallClient.go -package=mocks ./consensus/bor IHeimdallClient
|
||||||
go generate mockgen -destination=./eth/filters/IBackend.go -package=filters ./eth/filters Backend
|
go generate mockgen -destination=./eth/filters/IBackend.go -package=filters ./eth/filters Backend
|
||||||
|
go generate mockgen -destination=../eth/filters/IDatabase.go -package=filters . Database
|
||||||
|
|
||||||
geth:
|
geth:
|
||||||
$(GORUN) build/ci.go install ./cmd/geth
|
$(GORUN) build/ci.go install ./cmd/geth
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ The go-ethereum library (i.e. all code outside of the `cmd` directory) is licens
|
||||||
[GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html),
|
[GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html),
|
||||||
also included in our repository in the `COPYING.LESSER` file.
|
also included in our repository in the `COPYING.LESSER` file.
|
||||||
|
|
||||||
The go-ethereum binaries (i.e. all code inside of the `cmd` directory) is licensed under the
|
The go-ethereum binaries (i.e. all code inside of the `cmd` directory) are licensed under the
|
||||||
[GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html), also
|
[GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html), also
|
||||||
included in our repository in the `COPYING` file.
|
included in our repository in the `COPYING` file.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ func JSON(reader io.Reader) (ABI, error) {
|
||||||
if err := dec.Decode(&abi); err != nil {
|
if err := dec.Decode(&abi); err != nil {
|
||||||
return ABI{}, err
|
return ABI{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return abi, nil
|
return abi, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,12 +68,15 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return arguments, nil
|
return arguments, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
method, exist := abi.Methods[name]
|
method, exist := abi.Methods[name]
|
||||||
if !exist {
|
if !exist {
|
||||||
return nil, fmt.Errorf("method '%s' not found", name)
|
return nil, fmt.Errorf("method '%s' not found", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
arguments, err := method.Inputs.Pack(args...)
|
arguments, err := method.Inputs.Pack(args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -85,18 +89,23 @@ func (abi ABI) getArguments(name string, data []byte) (Arguments, error) {
|
||||||
// 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
|
||||||
var args Arguments
|
var args Arguments
|
||||||
|
|
||||||
if method, ok := abi.Methods[name]; ok {
|
if method, ok := abi.Methods[name]; ok {
|
||||||
if len(data)%32 != 0 {
|
if len(data)%32 != 0 {
|
||||||
return nil, fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(data), data)
|
return nil, fmt.Errorf("abi: improperly formatted output: %q - Bytes: %+v", data, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
args = method.Outputs
|
args = method.Outputs
|
||||||
}
|
}
|
||||||
|
|
||||||
if event, ok := abi.Events[name]; ok {
|
if event, ok := abi.Events[name]; ok {
|
||||||
args = event.Inputs
|
args = event.Inputs
|
||||||
}
|
}
|
||||||
|
|
||||||
if args == nil {
|
if args == nil {
|
||||||
return nil, errors.New("abi: could not locate named method or event")
|
return nil, fmt.Errorf("abi: could not locate named method or event: %s", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,6 +115,7 @@ func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return args.Unpack(data)
|
return args.Unpack(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -117,10 +127,12 @@ func (abi ABI) UnpackIntoInterface(v interface{}, name string, data []byte) erro
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
unpacked, err := args.Unpack(data)
|
unpacked, err := args.Unpack(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return args.Copy(v, unpacked)
|
return args.Copy(v, unpacked)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,6 +142,7 @@ func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return args.UnpackIntoMap(v, data)
|
return args.UnpackIntoMap(v, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,18 +166,21 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
|
||||||
// declared as anonymous.
|
// declared as anonymous.
|
||||||
Anonymous bool
|
Anonymous bool
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
abi.Errors = make(map[string]Error)
|
abi.Errors = make(map[string]Error)
|
||||||
|
|
||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
switch field.Type {
|
switch field.Type {
|
||||||
case "constructor":
|
case "constructor":
|
||||||
abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
|
abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
|
||||||
case "function":
|
case "function":
|
||||||
name := overloadedName(field.Name, func(s string) bool { _, ok := abi.Methods[s]; return ok })
|
name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Methods[s]; return ok })
|
||||||
abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
|
abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
|
||||||
case "fallback":
|
case "fallback":
|
||||||
// New introduced function type in v0.6.0, check more detail
|
// New introduced function type in v0.6.0, check more detail
|
||||||
|
|
@ -172,6 +188,7 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
|
||||||
if abi.HasFallback() {
|
if abi.HasFallback() {
|
||||||
return errors.New("only single fallback is allowed")
|
return errors.New("only single fallback is allowed")
|
||||||
}
|
}
|
||||||
|
|
||||||
abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil)
|
abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil)
|
||||||
case "receive":
|
case "receive":
|
||||||
// New introduced function type in v0.6.0, check more detail
|
// New introduced function type in v0.6.0, check more detail
|
||||||
|
|
@ -179,19 +196,24 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
|
||||||
if abi.HasReceive() {
|
if abi.HasReceive() {
|
||||||
return errors.New("only single receive is allowed")
|
return errors.New("only single receive is allowed")
|
||||||
}
|
}
|
||||||
|
|
||||||
if field.StateMutability != "payable" {
|
if field.StateMutability != "payable" {
|
||||||
return errors.New("the statemutability of receive can only be payable")
|
return errors.New("the statemutability of receive can only be payable")
|
||||||
}
|
}
|
||||||
|
|
||||||
abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
|
abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
|
||||||
case "event":
|
case "event":
|
||||||
name := overloadedName(field.Name, func(s string) bool { _, ok := abi.Events[s]; return ok })
|
name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Events[s]; return ok })
|
||||||
abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
|
abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
|
||||||
case "error":
|
case "error":
|
||||||
|
// Errors cannot be overloaded or overridden but are inherited,
|
||||||
|
// no need to resolve the name conflict here.
|
||||||
abi.Errors[field.Name] = NewError(field.Name, field.Inputs)
|
abi.Errors[field.Name] = NewError(field.Name, field.Inputs)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
|
return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,11 +223,13 @@ func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
|
||||||
if len(sigdata) < 4 {
|
if len(sigdata) < 4 {
|
||||||
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
|
return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -217,6 +241,7 @@ func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
|
||||||
return &event, nil
|
return &event, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
|
return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -241,30 +266,21 @@ func UnpackRevert(data []byte) (string, error) {
|
||||||
if len(data) < 4 {
|
if len(data) < 4 {
|
||||||
return "", errors.New("invalid data for unpacking")
|
return "", errors.New("invalid data for unpacking")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(data[:4], revertSelector) {
|
if !bytes.Equal(data[:4], revertSelector) {
|
||||||
return "", errors.New("invalid data for unpacking")
|
return "", errors.New("invalid data for unpacking")
|
||||||
}
|
}
|
||||||
typ, _ := NewType("string", "", nil)
|
|
||||||
|
typ, err := NewType("string", "", nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
|
unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return unpacked[0].(string), nil
|
return unpacked[0].(string), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// overloadedName returns the next available name for a given thing.
|
|
||||||
// Needed since solidity allows for overloading.
|
|
||||||
//
|
|
||||||
// e.g. if the abi contains Methods send, send1
|
|
||||||
// overloadedName would return send2 for input send.
|
|
||||||
//
|
|
||||||
// overloadedName works for methods, events and errors.
|
|
||||||
func overloadedName(rawName string, isAvail func(string) bool) string {
|
|
||||||
name := rawName
|
|
||||||
ok := isAvail(name)
|
|
||||||
for idx := 0; ok; idx++ {
|
|
||||||
name = fmt.Sprintf("%s%d", rawName, idx)
|
|
||||||
ok = isAvail(name)
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,7 @@ func TestReader(t *testing.T) {
|
||||||
if !exist {
|
if !exist {
|
||||||
t.Errorf("Missing expected method %v", name)
|
t.Errorf("Missing expected method %v", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(gotM, expM) {
|
if !reflect.DeepEqual(gotM, expM) {
|
||||||
t.Errorf("\nGot abi method: \n%v\ndoes not match expected method\n%v", gotM, expM)
|
t.Errorf("\nGot abi method: \n%v\ndoes not match expected method\n%v", gotM, expM)
|
||||||
}
|
}
|
||||||
|
|
@ -144,6 +145,7 @@ func TestReader(t *testing.T) {
|
||||||
if !exist {
|
if !exist {
|
||||||
t.Errorf("Found extra method %v", name)
|
t.Errorf("Found extra method %v", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(gotM, expM) {
|
if !reflect.DeepEqual(gotM, expM) {
|
||||||
t.Errorf("\nGot abi method: \n%v\ndoes not match expected method\n%v", gotM, expM)
|
t.Errorf("\nGot abi method: \n%v\ndoes not match expected method\n%v", gotM, expM)
|
||||||
}
|
}
|
||||||
|
|
@ -152,11 +154,14 @@ func TestReader(t *testing.T) {
|
||||||
|
|
||||||
func TestInvalidABI(t *testing.T) {
|
func TestInvalidABI(t *testing.T) {
|
||||||
json := `[{ "type" : "function", "name" : "", "constant" : fals }]`
|
json := `[{ "type" : "function", "name" : "", "constant" : fals }]`
|
||||||
|
|
||||||
_, err := JSON(strings.NewReader(json))
|
_, err := JSON(strings.NewReader(json))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("invalid json should produce error")
|
t.Fatal("invalid json should produce error")
|
||||||
}
|
}
|
||||||
|
|
||||||
json2 := `[{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "typ" : "uint256" } ] }]`
|
json2 := `[{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "typ" : "uint256" } ] }]`
|
||||||
|
|
||||||
_, err = JSON(strings.NewReader(json2))
|
_, err = JSON(strings.NewReader(json2))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatal("invalid json should produce error")
|
t.Fatal("invalid json should produce error")
|
||||||
|
|
@ -165,6 +170,7 @@ func TestInvalidABI(t *testing.T) {
|
||||||
|
|
||||||
// TestConstructor tests a constructor function.
|
// TestConstructor tests a constructor function.
|
||||||
// The test is based on the following contract:
|
// The test is based on the following contract:
|
||||||
|
//
|
||||||
// contract TestConstructor {
|
// contract TestConstructor {
|
||||||
// constructor(uint256 a, uint256 b) public{}
|
// constructor(uint256 a, uint256 b) public{}
|
||||||
// }
|
// }
|
||||||
|
|
@ -176,6 +182,7 @@ func TestConstructor(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(abi.Constructor, method) {
|
if !reflect.DeepEqual(abi.Constructor, method) {
|
||||||
t.Error("Missing expected constructor")
|
t.Error("Missing expected constructor")
|
||||||
}
|
}
|
||||||
|
|
@ -184,6 +191,7 @@ func TestConstructor(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
unpacked, err := abi.Constructor.Inputs.Unpack(packed)
|
unpacked, err := abi.Constructor.Inputs.Unpack(packed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -192,6 +200,7 @@ func TestConstructor(t *testing.T) {
|
||||||
if !reflect.DeepEqual(unpacked[0], big.NewInt(1)) {
|
if !reflect.DeepEqual(unpacked[0], big.NewInt(1)) {
|
||||||
t.Error("Unable to pack/unpack from constructor")
|
t.Error("Unable to pack/unpack from constructor")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(unpacked[1], big.NewInt(2)) {
|
if !reflect.DeepEqual(unpacked[1], big.NewInt(2)) {
|
||||||
t.Error("Unable to pack/unpack from constructor")
|
t.Error("Unable to pack/unpack from constructor")
|
||||||
}
|
}
|
||||||
|
|
@ -225,6 +234,7 @@ func TestTestNumbers(t *testing.T) {
|
||||||
|
|
||||||
i := new(int)
|
i := new(int)
|
||||||
*i = 1000
|
*i = 1000
|
||||||
|
|
||||||
if _, err := abi.Pack("send", i); err == nil {
|
if _, err := abi.Pack("send", i); err == nil {
|
||||||
t.Errorf("expected send( ptr ) to throw, requires *big.Int instead of *int")
|
t.Errorf("expected send( ptr ) to throw, requires *big.Int instead of *int")
|
||||||
}
|
}
|
||||||
|
|
@ -237,6 +247,7 @@ func TestTestNumbers(t *testing.T) {
|
||||||
func TestMethodSignature(t *testing.T) {
|
func TestMethodSignature(t *testing.T) {
|
||||||
m := NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil)
|
m := NewMethod("foo", "foo", Function, "", false, 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)
|
||||||
}
|
}
|
||||||
|
|
@ -248,6 +259,7 @@ func TestMethodSignature(t *testing.T) {
|
||||||
|
|
||||||
m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", Uint256, false}}, nil)
|
m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", Uint256, 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)
|
||||||
}
|
}
|
||||||
|
|
@ -267,6 +279,7 @@ func TestMethodSignature(t *testing.T) {
|
||||||
})
|
})
|
||||||
m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"s", s, false}, {"bar", String, false}}, nil)
|
m = NewMethod("foo", "foo", Function, "", false, 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)
|
||||||
}
|
}
|
||||||
|
|
@ -274,10 +287,12 @@ func TestMethodSignature(t *testing.T) {
|
||||||
|
|
||||||
func TestOverloadedMethodSignature(t *testing.T) {
|
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"}]`
|
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))
|
abi, err := JSON(strings.NewReader(json))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
check := func(name string, expect string, method bool) {
|
check := func(name string, expect string, method bool) {
|
||||||
if method {
|
if method {
|
||||||
if abi.Methods[name].Sig != expect {
|
if abi.Methods[name].Sig != expect {
|
||||||
|
|
@ -297,10 +312,12 @@ func TestOverloadedMethodSignature(t *testing.T) {
|
||||||
|
|
||||||
func TestCustomErrors(t *testing.T) {
|
func TestCustomErrors(t *testing.T) {
|
||||||
json := `[{ "inputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ],"name": "MyError", "type": "error"} ]`
|
json := `[{ "inputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ],"name": "MyError", "type": "error"} ]`
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(json))
|
abi, err := JSON(strings.NewReader(json))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
check := func(name string, expect string) {
|
check := func(name string, expect string) {
|
||||||
if abi.Errors[name].Sig != expect {
|
if abi.Errors[name].Sig != expect {
|
||||||
t.Fatalf("The signature of overloaded method mismatch, want %s, have %s", expect, abi.Methods[name].Sig)
|
t.Fatalf("The signature of overloaded method mismatch, want %s, have %s", expect, abi.Methods[name].Sig)
|
||||||
|
|
@ -324,6 +341,7 @@ func TestMultiPack(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
@ -336,6 +354,7 @@ func ExampleJSON() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(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 {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
@ -360,6 +379,7 @@ func TestInputVariableInputLength(t *testing.T) {
|
||||||
|
|
||||||
// test one string
|
// test one string
|
||||||
strin := "hello world"
|
strin := "hello world"
|
||||||
|
|
||||||
strpack, err := abi.Pack("strOne", strin)
|
strpack, err := abi.Pack("strOne", strin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -392,6 +412,7 @@ func TestInputVariableInputLength(t *testing.T) {
|
||||||
// test two strings
|
// test two strings
|
||||||
str1 := "hello"
|
str1 := "hello"
|
||||||
str2 := "world"
|
str2 := "world"
|
||||||
|
|
||||||
str2pack, err := abi.Pack("strTwo", str1, str2)
|
str2pack, err := abi.Pack("strTwo", str1, str2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -421,6 +442,7 @@ func TestInputVariableInputLength(t *testing.T) {
|
||||||
|
|
||||||
// test two strings, first > 32, second < 32
|
// test two strings, first > 32, second < 32
|
||||||
str1 = strings.Repeat("a", 33)
|
str1 = strings.Repeat("a", 33)
|
||||||
|
|
||||||
str2pack, err = abi.Pack("strTwo", str1, str2)
|
str2pack, err = abi.Pack("strTwo", str1, str2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -446,6 +468,7 @@ func TestInputVariableInputLength(t *testing.T) {
|
||||||
// test two strings, first > 32, second >32
|
// test two strings, first > 32, second >32
|
||||||
str1 = strings.Repeat("a", 33)
|
str1 = strings.Repeat("a", 33)
|
||||||
str2 = strings.Repeat("a", 33)
|
str2 = strings.Repeat("a", 33)
|
||||||
|
|
||||||
str2pack, err = abi.Pack("strTwo", str1, str2)
|
str2pack, err = abi.Pack("strTwo", str1, str2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -483,6 +506,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
||||||
// test string, fixed array uint256[2]
|
// test string, fixed array uint256[2]
|
||||||
strin := "hello world"
|
strin := "hello world"
|
||||||
arrin := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
|
arrin := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
|
||||||
|
|
||||||
fixedArrStrPack, err := abi.Pack("fixedArrStr", strin, arrin)
|
fixedArrStrPack, err := abi.Pack("fixedArrStr", strin, arrin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -496,6 +520,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
||||||
strvalue := common.RightPadBytes([]byte(strin), 32)
|
strvalue := common.RightPadBytes([]byte(strin), 32)
|
||||||
arrinvalue1 := common.LeftPadBytes(arrin[0].Bytes(), 32)
|
arrinvalue1 := common.LeftPadBytes(arrin[0].Bytes(), 32)
|
||||||
arrinvalue2 := common.LeftPadBytes(arrin[1].Bytes(), 32)
|
arrinvalue2 := common.LeftPadBytes(arrin[1].Bytes(), 32)
|
||||||
|
|
||||||
exp := append(offset, arrinvalue1...)
|
exp := append(offset, arrinvalue1...)
|
||||||
exp = append(exp, arrinvalue2...)
|
exp = append(exp, arrinvalue2...)
|
||||||
exp = append(exp, append(length, strvalue...)...)
|
exp = append(exp, append(length, strvalue...)...)
|
||||||
|
|
@ -509,6 +534,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
||||||
// test byte array, fixed array uint256[2]
|
// test byte array, fixed array uint256[2]
|
||||||
bytesin := []byte(strin)
|
bytesin := []byte(strin)
|
||||||
arrin = [2]*big.Int{big.NewInt(1), big.NewInt(2)}
|
arrin = [2]*big.Int{big.NewInt(1), big.NewInt(2)}
|
||||||
|
|
||||||
fixedArrBytesPack, err := abi.Pack("fixedArrBytes", bytesin, arrin)
|
fixedArrBytesPack, err := abi.Pack("fixedArrBytes", bytesin, arrin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -522,6 +548,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
||||||
strvalue = common.RightPadBytes([]byte(strin), 32)
|
strvalue = common.RightPadBytes([]byte(strin), 32)
|
||||||
arrinvalue1 = common.LeftPadBytes(arrin[0].Bytes(), 32)
|
arrinvalue1 = common.LeftPadBytes(arrin[0].Bytes(), 32)
|
||||||
arrinvalue2 = common.LeftPadBytes(arrin[1].Bytes(), 32)
|
arrinvalue2 = common.LeftPadBytes(arrin[1].Bytes(), 32)
|
||||||
|
|
||||||
exp = append(offset, arrinvalue1...)
|
exp = append(offset, arrinvalue1...)
|
||||||
exp = append(exp, arrinvalue2...)
|
exp = append(exp, arrinvalue2...)
|
||||||
exp = append(exp, append(length, strvalue...)...)
|
exp = append(exp, append(length, strvalue...)...)
|
||||||
|
|
@ -536,6 +563,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
||||||
strin = "hello world"
|
strin = "hello world"
|
||||||
fixedarrin := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
|
fixedarrin := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
|
||||||
dynarrin := []*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}
|
dynarrin := []*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}
|
||||||
|
|
||||||
mixedArrStrPack, err := abi.Pack("mixedArrStr", strin, fixedarrin, dynarrin)
|
mixedArrStrPack, err := abi.Pack("mixedArrStr", strin, fixedarrin, dynarrin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -556,6 +584,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
||||||
dynarrinvalue1 := common.LeftPadBytes(dynarrin[0].Bytes(), 32)
|
dynarrinvalue1 := common.LeftPadBytes(dynarrin[0].Bytes(), 32)
|
||||||
dynarrinvalue2 := common.LeftPadBytes(dynarrin[1].Bytes(), 32)
|
dynarrinvalue2 := common.LeftPadBytes(dynarrin[1].Bytes(), 32)
|
||||||
dynarrinvalue3 := common.LeftPadBytes(dynarrin[2].Bytes(), 32)
|
dynarrinvalue3 := common.LeftPadBytes(dynarrin[2].Bytes(), 32)
|
||||||
|
|
||||||
exp = append(stroffset, fixedarrinvalue1...)
|
exp = append(stroffset, fixedarrinvalue1...)
|
||||||
exp = append(exp, fixedarrinvalue2...)
|
exp = append(exp, fixedarrinvalue2...)
|
||||||
exp = append(exp, dynarroffset...)
|
exp = append(exp, dynarroffset...)
|
||||||
|
|
@ -575,6 +604,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
||||||
strin = "hello world"
|
strin = "hello world"
|
||||||
fixedarrin1 := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
|
fixedarrin1 := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
|
||||||
fixedarrin2 := [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}
|
fixedarrin2 := [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}
|
||||||
|
|
||||||
doubleFixedArrStrPack, err := abi.Pack("doubleFixedArrStr", strin, fixedarrin1, fixedarrin2)
|
doubleFixedArrStrPack, err := abi.Pack("doubleFixedArrStr", strin, fixedarrin1, fixedarrin2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -591,6 +621,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
||||||
fixedarrin2value1 := common.LeftPadBytes(fixedarrin2[0].Bytes(), 32)
|
fixedarrin2value1 := common.LeftPadBytes(fixedarrin2[0].Bytes(), 32)
|
||||||
fixedarrin2value2 := common.LeftPadBytes(fixedarrin2[1].Bytes(), 32)
|
fixedarrin2value2 := common.LeftPadBytes(fixedarrin2[1].Bytes(), 32)
|
||||||
fixedarrin2value3 := common.LeftPadBytes(fixedarrin2[2].Bytes(), 32)
|
fixedarrin2value3 := common.LeftPadBytes(fixedarrin2[2].Bytes(), 32)
|
||||||
|
|
||||||
exp = append(stroffset, fixedarrin1value1...)
|
exp = append(stroffset, fixedarrin1value1...)
|
||||||
exp = append(exp, fixedarrin1value2...)
|
exp = append(exp, fixedarrin1value2...)
|
||||||
exp = append(exp, fixedarrin2value1...)
|
exp = append(exp, fixedarrin2value1...)
|
||||||
|
|
@ -609,6 +640,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
||||||
fixedarrin1 = [2]*big.Int{big.NewInt(1), big.NewInt(2)}
|
fixedarrin1 = [2]*big.Int{big.NewInt(1), big.NewInt(2)}
|
||||||
dynarrin = []*big.Int{big.NewInt(1), big.NewInt(2)}
|
dynarrin = []*big.Int{big.NewInt(1), big.NewInt(2)}
|
||||||
fixedarrin2 = [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}
|
fixedarrin2 = [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}
|
||||||
|
|
||||||
multipleMixedArrStrPack, err := abi.Pack("multipleMixedArrStr", strin, fixedarrin1, dynarrin, fixedarrin2)
|
multipleMixedArrStrPack, err := abi.Pack("multipleMixedArrStr", strin, fixedarrin1, dynarrin, fixedarrin2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -630,6 +662,7 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
||||||
fixedarrin2value1 = common.LeftPadBytes(fixedarrin2[0].Bytes(), 32)
|
fixedarrin2value1 = common.LeftPadBytes(fixedarrin2[0].Bytes(), 32)
|
||||||
fixedarrin2value2 = common.LeftPadBytes(fixedarrin2[1].Bytes(), 32)
|
fixedarrin2value2 = common.LeftPadBytes(fixedarrin2[1].Bytes(), 32)
|
||||||
fixedarrin2value3 = common.LeftPadBytes(fixedarrin2[2].Bytes(), 32)
|
fixedarrin2value3 = common.LeftPadBytes(fixedarrin2[2].Bytes(), 32)
|
||||||
|
|
||||||
exp = append(stroffset, fixedarrin1value1...)
|
exp = append(stroffset, fixedarrin1value1...)
|
||||||
exp = append(exp, fixedarrin1value2...)
|
exp = append(exp, fixedarrin1value2...)
|
||||||
exp = append(exp, dynarroffset...)
|
exp = append(exp, dynarroffset...)
|
||||||
|
|
@ -702,20 +735,25 @@ func TestBareEvents(t *testing.T) {
|
||||||
t.Errorf("could not found event %s", name)
|
t.Errorf("could not found event %s", name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if got.Anonymous != exp.Anonymous {
|
if got.Anonymous != exp.Anonymous {
|
||||||
t.Errorf("invalid anonymous indication for event %s, want %v, got %v", name, exp.Anonymous, got.Anonymous)
|
t.Errorf("invalid anonymous indication for event %s, want %v, got %v", name, exp.Anonymous, got.Anonymous)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(got.Inputs) != len(exp.Args) {
|
if len(got.Inputs) != len(exp.Args) {
|
||||||
t.Errorf("invalid number of args, want %d, got %d", len(exp.Args), len(got.Inputs))
|
t.Errorf("invalid number of args, want %d, got %d", len(exp.Args), len(got.Inputs))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, arg := range exp.Args {
|
for i, arg := range exp.Args {
|
||||||
if arg.Name != got.Inputs[i].Name {
|
if arg.Name != got.Inputs[i].Name {
|
||||||
t.Errorf("events[%s].Input[%d] has an invalid name, want %s, got %s", name, i, arg.Name, got.Inputs[i].Name)
|
t.Errorf("events[%s].Input[%d] has an invalid name, want %s, got %s", name, i, arg.Name, got.Inputs[i].Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if arg.Indexed != got.Inputs[i].Indexed {
|
if arg.Indexed != got.Inputs[i].Indexed {
|
||||||
t.Errorf("events[%s].Input[%d] has an invalid indexed indication, want %v, got %v", name, i, arg.Indexed, got.Inputs[i].Indexed)
|
t.Errorf("events[%s].Input[%d] has an invalid indexed indication, want %v, got %v", name, i, arg.Indexed, got.Inputs[i].Indexed)
|
||||||
}
|
}
|
||||||
|
|
||||||
if arg.Type.T != got.Inputs[i].Type.T {
|
if arg.Type.T != got.Inputs[i].Type.T {
|
||||||
t.Errorf("events[%s].Input[%d] has an invalid type, want %x, got %x", name, i, arg.Type.T, got.Inputs[i].Type.T)
|
t.Errorf("events[%s].Input[%d] has an invalid type, want %x, got %x", name, i, arg.Type.T, got.Inputs[i].Type.T)
|
||||||
}
|
}
|
||||||
|
|
@ -724,6 +762,7 @@ func TestBareEvents(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestUnpackEvent is based on this contract:
|
// TestUnpackEvent is based on this contract:
|
||||||
|
//
|
||||||
// contract T {
|
// contract T {
|
||||||
// event received(address sender, uint amount, bytes memo);
|
// event received(address sender, uint amount, bytes memo);
|
||||||
// event receivedAddr(address sender);
|
// event receivedAddr(address sender);
|
||||||
|
|
@ -732,20 +771,25 @@ func TestBareEvents(t *testing.T) {
|
||||||
// receivedAddr(msg.sender);
|
// receivedAddr(msg.sender);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
//
|
||||||
// When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt:
|
// When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt:
|
||||||
|
//
|
||||||
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
|
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
|
||||||
func TestUnpackEvent(t *testing.T) {
|
func TestUnpackEvent(t *testing.T) {
|
||||||
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
|
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(abiJSON))
|
abi, err := JSON(strings.NewReader(abiJSON))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
const hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
|
const hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
|
||||||
|
|
||||||
data, err := hex.DecodeString(hexdata)
|
data, err := hex.DecodeString(hexdata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data)%32 == 0 {
|
if len(data)%32 == 0 {
|
||||||
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
||||||
}
|
}
|
||||||
|
|
@ -755,6 +799,7 @@ func TestUnpackEvent(t *testing.T) {
|
||||||
Amount *big.Int
|
Amount *big.Int
|
||||||
Memo []byte
|
Memo []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
var ev ReceivedEvent
|
var ev ReceivedEvent
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&ev, "received", data)
|
err = abi.UnpackIntoInterface(&ev, "received", data)
|
||||||
|
|
@ -765,7 +810,9 @@ func TestUnpackEvent(t *testing.T) {
|
||||||
type ReceivedAddrEvent struct {
|
type ReceivedAddrEvent struct {
|
||||||
Sender common.Address
|
Sender common.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
var receivedAddrEv ReceivedAddrEvent
|
var receivedAddrEv ReceivedAddrEvent
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&receivedAddrEv, "receivedAddr", data)
|
err = abi.UnpackIntoInterface(&receivedAddrEv, "receivedAddr", data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -774,16 +821,19 @@ func TestUnpackEvent(t *testing.T) {
|
||||||
|
|
||||||
func TestUnpackEventIntoMap(t *testing.T) {
|
func TestUnpackEventIntoMap(t *testing.T) {
|
||||||
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
|
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(abiJSON))
|
abi, err := JSON(strings.NewReader(abiJSON))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
const hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
|
const hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
|
||||||
|
|
||||||
data, err := hex.DecodeString(hexdata)
|
data, err := hex.DecodeString(hexdata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data)%32 == 0 {
|
if len(data)%32 == 0 {
|
||||||
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
||||||
}
|
}
|
||||||
|
|
@ -794,18 +844,23 @@ func TestUnpackEventIntoMap(t *testing.T) {
|
||||||
"amount": big.NewInt(1),
|
"amount": big.NewInt(1),
|
||||||
"memo": []byte{88},
|
"memo": []byte{88},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := abi.UnpackIntoMap(receivedMap, "received", data); err != nil {
|
if err := abi.UnpackIntoMap(receivedMap, "received", data); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(receivedMap) != 3 {
|
if len(receivedMap) != 3 {
|
||||||
t.Error("unpacked `received` map expected to have length 3")
|
t.Error("unpacked `received` map expected to have length 3")
|
||||||
}
|
}
|
||||||
|
|
||||||
if receivedMap["sender"] != expectedReceivedMap["sender"] {
|
if receivedMap["sender"] != expectedReceivedMap["sender"] {
|
||||||
t.Error("unpacked `received` map does not match expected map")
|
t.Error("unpacked `received` map does not match expected map")
|
||||||
}
|
}
|
||||||
|
|
||||||
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
|
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
|
||||||
t.Error("unpacked `received` map does not match expected map")
|
t.Error("unpacked `received` map does not match expected map")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
|
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
|
||||||
t.Error("unpacked `received` map does not match expected map")
|
t.Error("unpacked `received` map does not match expected map")
|
||||||
}
|
}
|
||||||
|
|
@ -814,9 +869,11 @@ func TestUnpackEventIntoMap(t *testing.T) {
|
||||||
if err = abi.UnpackIntoMap(receivedAddrMap, "receivedAddr", data); err != nil {
|
if err = abi.UnpackIntoMap(receivedAddrMap, "receivedAddr", data); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(receivedAddrMap) != 1 {
|
if len(receivedAddrMap) != 1 {
|
||||||
t.Error("unpacked `receivedAddr` map expected to have length 1")
|
t.Error("unpacked `receivedAddr` map expected to have length 1")
|
||||||
}
|
}
|
||||||
|
|
||||||
if receivedAddrMap["sender"] != expectedReceivedMap["sender"] {
|
if receivedAddrMap["sender"] != expectedReceivedMap["sender"] {
|
||||||
t.Error("unpacked `receivedAddr` map does not match expected map")
|
t.Error("unpacked `receivedAddr` map does not match expected map")
|
||||||
}
|
}
|
||||||
|
|
@ -824,15 +881,19 @@ func TestUnpackEventIntoMap(t *testing.T) {
|
||||||
|
|
||||||
func TestUnpackMethodIntoMap(t *testing.T) {
|
func TestUnpackMethodIntoMap(t *testing.T) {
|
||||||
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"send","outputs":[{"name":"amount","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"get","outputs":[{"name":"hash","type":"bytes"}],"payable":true,"stateMutability":"payable","type":"function"}]`
|
const abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"send","outputs":[{"name":"amount","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"get","outputs":[{"name":"hash","type":"bytes"}],"payable":true,"stateMutability":"payable","type":"function"}]`
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(abiJSON))
|
abi, err := JSON(strings.NewReader(abiJSON))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
const hexdata = `00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000015800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000158000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001580000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000015800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000158`
|
const hexdata = `00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000015800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000158000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001580000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000015800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000158`
|
||||||
|
|
||||||
data, err := hex.DecodeString(hexdata)
|
data, err := hex.DecodeString(hexdata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data)%32 != 0 {
|
if len(data)%32 != 0 {
|
||||||
t.Errorf("len(data) is %d, want a multiple of 32", len(data))
|
t.Errorf("len(data) is %d, want a multiple of 32", len(data))
|
||||||
}
|
}
|
||||||
|
|
@ -842,6 +903,7 @@ func TestUnpackMethodIntoMap(t *testing.T) {
|
||||||
if err = abi.UnpackIntoMap(receiveMap, "receive", data); err != nil {
|
if err = abi.UnpackIntoMap(receiveMap, "receive", data); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(receiveMap) > 0 {
|
if len(receiveMap) > 0 {
|
||||||
t.Error("unpacked `receive` map expected to have length 0")
|
t.Error("unpacked `receive` map expected to have length 0")
|
||||||
}
|
}
|
||||||
|
|
@ -851,9 +913,11 @@ func TestUnpackMethodIntoMap(t *testing.T) {
|
||||||
if err = abi.UnpackIntoMap(sendMap, "send", data); err != nil {
|
if err = abi.UnpackIntoMap(sendMap, "send", data); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(sendMap) != 1 {
|
if len(sendMap) != 1 {
|
||||||
t.Error("unpacked `send` map expected to have length 1")
|
t.Error("unpacked `send` map expected to have length 1")
|
||||||
}
|
}
|
||||||
|
|
||||||
if sendMap["amount"].(*big.Int).Cmp(big.NewInt(1)) != 0 {
|
if sendMap["amount"].(*big.Int).Cmp(big.NewInt(1)) != 0 {
|
||||||
t.Error("unpacked `send` map expected `amount` value of 1")
|
t.Error("unpacked `send` map expected `amount` value of 1")
|
||||||
}
|
}
|
||||||
|
|
@ -863,9 +927,11 @@ func TestUnpackMethodIntoMap(t *testing.T) {
|
||||||
if err = abi.UnpackIntoMap(getMap, "get", data); err != nil {
|
if err = abi.UnpackIntoMap(getMap, "get", data); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(getMap) != 1 {
|
if len(getMap) != 1 {
|
||||||
t.Error("unpacked `get` map expected to have length 1")
|
t.Error("unpacked `get` map expected to have length 1")
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedBytes := []byte{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, 96, 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, 1, 88, 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, 96, 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, 1, 88, 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, 96, 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, 1, 88, 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, 96, 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, 1, 88, 0}
|
expectedBytes := []byte{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, 96, 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, 1, 88, 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, 96, 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, 1, 88, 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, 96, 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, 1, 88, 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, 96, 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, 1, 88, 0}
|
||||||
if !bytes.Equal(getMap["hash"].([]byte), expectedBytes) {
|
if !bytes.Equal(getMap["hash"].([]byte), expectedBytes) {
|
||||||
t.Errorf("unpacked `get` map expected `hash` value of %v", expectedBytes)
|
t.Errorf("unpacked `get` map expected `hash` value of %v", expectedBytes)
|
||||||
|
|
@ -875,18 +941,23 @@ func TestUnpackMethodIntoMap(t *testing.T) {
|
||||||
func TestUnpackIntoMapNamingConflict(t *testing.T) {
|
func TestUnpackIntoMapNamingConflict(t *testing.T) {
|
||||||
// Two methods have the same name
|
// Two methods have the same name
|
||||||
var abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"get","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"send","outputs":[{"name":"amount","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"get","outputs":[{"name":"hash","type":"bytes"}],"payable":true,"stateMutability":"payable","type":"function"}]`
|
var abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"get","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[],"name":"send","outputs":[{"name":"amount","type":"uint256"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"get","outputs":[{"name":"hash","type":"bytes"}],"payable":true,"stateMutability":"payable","type":"function"}]`
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(abiJSON))
|
abi, err := JSON(strings.NewReader(abiJSON))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var hexdata = `00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
|
var hexdata = `00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
|
||||||
|
|
||||||
data, err := hex.DecodeString(hexdata)
|
data, err := hex.DecodeString(hexdata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data)%32 == 0 {
|
if len(data)%32 == 0 {
|
||||||
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
getMap := map[string]interface{}{}
|
getMap := map[string]interface{}{}
|
||||||
if err = abi.UnpackIntoMap(getMap, "get", data); err == nil {
|
if err = abi.UnpackIntoMap(getMap, "get", data); err == nil {
|
||||||
t.Error("naming conflict between two methods; error expected")
|
t.Error("naming conflict between two methods; error expected")
|
||||||
|
|
@ -894,18 +965,23 @@ func TestUnpackIntoMapNamingConflict(t *testing.T) {
|
||||||
|
|
||||||
// Two events have the same name
|
// Two events have the same name
|
||||||
abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"received","type":"event"}]`
|
abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"receive","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"received","type":"event"}]`
|
||||||
|
|
||||||
abi, err = JSON(strings.NewReader(abiJSON))
|
abi, err = JSON(strings.NewReader(abiJSON))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
|
hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
|
||||||
|
|
||||||
data, err = hex.DecodeString(hexdata)
|
data, err = hex.DecodeString(hexdata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data)%32 == 0 {
|
if len(data)%32 == 0 {
|
||||||
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
receivedMap := map[string]interface{}{}
|
receivedMap := map[string]interface{}{}
|
||||||
if err = abi.UnpackIntoMap(receivedMap, "received", data); err != nil {
|
if err = abi.UnpackIntoMap(receivedMap, "received", data); err != nil {
|
||||||
t.Error("naming conflict between two events; no error expected")
|
t.Error("naming conflict between two events; no error expected")
|
||||||
|
|
@ -913,43 +989,54 @@ func TestUnpackIntoMapNamingConflict(t *testing.T) {
|
||||||
|
|
||||||
// Method and event have the same name
|
// Method and event have the same name
|
||||||
abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"received","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
|
abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"received","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
|
||||||
|
|
||||||
abi, err = JSON(strings.NewReader(abiJSON))
|
abi, err = JSON(strings.NewReader(abiJSON))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data)%32 == 0 {
|
if len(data)%32 == 0 {
|
||||||
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = abi.UnpackIntoMap(receivedMap, "received", data); err == nil {
|
if err = abi.UnpackIntoMap(receivedMap, "received", data); err == nil {
|
||||||
t.Error("naming conflict between an event and a method; error expected")
|
t.Error("naming conflict between an event and a method; error expected")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conflict is case sensitive
|
// Conflict is case sensitive
|
||||||
abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"received","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"Received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
|
abiJSON = `[{"constant":false,"inputs":[{"name":"memo","type":"bytes"}],"name":"received","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"},{"indexed":false,"name":"amount","type":"uint256"},{"indexed":false,"name":"memo","type":"bytes"}],"name":"Received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"sender","type":"address"}],"name":"receivedAddr","type":"event"}]`
|
||||||
|
|
||||||
abi, err = JSON(strings.NewReader(abiJSON))
|
abi, err = JSON(strings.NewReader(abiJSON))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data)%32 == 0 {
|
if len(data)%32 == 0 {
|
||||||
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedReceivedMap := map[string]interface{}{
|
expectedReceivedMap := map[string]interface{}{
|
||||||
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
||||||
"amount": big.NewInt(1),
|
"amount": big.NewInt(1),
|
||||||
"memo": []byte{88},
|
"memo": []byte{88},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = abi.UnpackIntoMap(receivedMap, "Received", data); err != nil {
|
if err = abi.UnpackIntoMap(receivedMap, "Received", data); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(receivedMap) != 3 {
|
if len(receivedMap) != 3 {
|
||||||
t.Error("unpacked `received` map expected to have length 3")
|
t.Error("unpacked `received` map expected to have length 3")
|
||||||
}
|
}
|
||||||
|
|
||||||
if receivedMap["sender"] != expectedReceivedMap["sender"] {
|
if receivedMap["sender"] != expectedReceivedMap["sender"] {
|
||||||
t.Error("unpacked `received` map does not match expected map")
|
t.Error("unpacked `received` map does not match expected map")
|
||||||
}
|
}
|
||||||
|
|
||||||
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
|
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
|
||||||
t.Error("unpacked `received` map does not match expected map")
|
t.Error("unpacked `received` map does not match expected map")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
|
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
|
||||||
t.Error("unpacked `received` map does not match expected map")
|
t.Error("unpacked `received` map does not match expected map")
|
||||||
}
|
}
|
||||||
|
|
@ -960,12 +1047,15 @@ func TestABI_MethodById(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
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 %x) not 'findable' by id in ABI", name, m.ID)
|
t.Errorf("Method %v (id %x) not 'findable' by id in ABI", name, m.ID)
|
||||||
|
|
@ -979,9 +1069,11 @@ func TestABI_MethodById(t *testing.T) {
|
||||||
if _, err := abi.MethodById([]byte{0x00}); err == nil {
|
if _, err := abi.MethodById([]byte{0x00}); err == nil {
|
||||||
t.Errorf("Expected error, too short to decode data")
|
t.Errorf("Expected error, too short to decode data")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := abi.MethodById([]byte{}); err == nil {
|
if _, err := abi.MethodById([]byte{}); err == nil {
|
||||||
t.Errorf("Expected error, too short to decode data")
|
t.Errorf("Expected error, too short to decode data")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := abi.MethodById(nil); err == nil {
|
if _, err := abi.MethodById(nil); err == nil {
|
||||||
t.Errorf("Expected error, nil is short to decode data")
|
t.Errorf("Expected error, nil is short to decode data")
|
||||||
}
|
}
|
||||||
|
|
@ -1036,19 +1128,20 @@ func TestABI_EventById(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to look up ABI method: %v, test #%d", err, testnum)
|
t.Fatalf("Failed to look up ABI method: %v, test #%d", err, testnum)
|
||||||
}
|
}
|
||||||
|
|
||||||
if event == nil {
|
if event == nil {
|
||||||
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)
|
||||||
}
|
} else 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"))
|
||||||
|
|
||||||
unknownEvent, err := abi.EventByID(unknowntopicID)
|
unknownEvent, err := abi.EventByID(unknowntopicID)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("EventByID should return an error if a topic is not found, test #%d", testnum)
|
t.Errorf("EventByID should return an error if a topic is not found, test #%d", testnum)
|
||||||
}
|
}
|
||||||
|
|
||||||
if unknownEvent != nil {
|
if unknownEvent != nil {
|
||||||
t.Errorf("We should not find any event for topic %s, test #%d", unknowntopicID.Hex(), testnum)
|
t.Errorf("We should not find any event for topic %s, test #%d", unknowntopicID.Hex(), testnum)
|
||||||
}
|
}
|
||||||
|
|
@ -1059,19 +1152,24 @@ func TestABI_EventById(t *testing.T) {
|
||||||
// conflict and that the second transfer method will be renamed transfer1.
|
// conflict and that the second transfer method will be renamed transfer1.
|
||||||
func TestDoubleDuplicateMethodNames(t *testing.T) {
|
func TestDoubleDuplicateMethodNames(t *testing.T) {
|
||||||
abiJSON := `[{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"}],"name":"transfer0","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"},{"name":"customFallback","type":"string"}],"name":"transfer","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]`
|
abiJSON := `[{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"}],"name":"transfer0","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"},{"name":"customFallback","type":"string"}],"name":"transfer","outputs":[{"name":"ok","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]`
|
||||||
|
|
||||||
contractAbi, err := JSON(strings.NewReader(abiJSON))
|
contractAbi, err := JSON(strings.NewReader(abiJSON))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := contractAbi.Methods["transfer"]; !ok {
|
if _, ok := contractAbi.Methods["transfer"]; !ok {
|
||||||
t.Fatalf("Could not find original method")
|
t.Fatalf("Could not find original method")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := contractAbi.Methods["transfer0"]; !ok {
|
if _, ok := contractAbi.Methods["transfer0"]; !ok {
|
||||||
t.Fatalf("Could not find duplicate method")
|
t.Fatalf("Could not find duplicate method")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := contractAbi.Methods["transfer1"]; !ok {
|
if _, ok := contractAbi.Methods["transfer1"]; !ok {
|
||||||
t.Fatalf("Could not find duplicate method")
|
t.Fatalf("Could not find duplicate method")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := contractAbi.Methods["transfer2"]; ok {
|
if _, ok := contractAbi.Methods["transfer2"]; ok {
|
||||||
t.Fatalf("Should not have found extra method")
|
t.Fatalf("Should not have found extra method")
|
||||||
}
|
}
|
||||||
|
|
@ -1080,6 +1178,7 @@ func TestDoubleDuplicateMethodNames(t *testing.T) {
|
||||||
// TestDoubleDuplicateEventNames checks that if send0 already exists, there won't be a name
|
// TestDoubleDuplicateEventNames checks that if send0 already exists, there won't be a name
|
||||||
// conflict and that the second send event will be renamed send1.
|
// conflict and that the second send event will be renamed send1.
|
||||||
// The test runs the abi of the following contract.
|
// The test runs the abi of the following contract.
|
||||||
|
//
|
||||||
// contract DuplicateEvent {
|
// contract DuplicateEvent {
|
||||||
// event send(uint256 a);
|
// event send(uint256 a);
|
||||||
// event send0();
|
// event send0();
|
||||||
|
|
@ -1087,19 +1186,24 @@ func TestDoubleDuplicateMethodNames(t *testing.T) {
|
||||||
// }
|
// }
|
||||||
func TestDoubleDuplicateEventNames(t *testing.T) {
|
func TestDoubleDuplicateEventNames(t *testing.T) {
|
||||||
abiJSON := `[{"anonymous": false,"inputs": [{"indexed": false,"internalType": "uint256","name": "a","type": "uint256"}],"name": "send","type": "event"},{"anonymous": false,"inputs": [],"name": "send0","type": "event"},{ "anonymous": false, "inputs": [],"name": "send","type": "event"}]`
|
abiJSON := `[{"anonymous": false,"inputs": [{"indexed": false,"internalType": "uint256","name": "a","type": "uint256"}],"name": "send","type": "event"},{"anonymous": false,"inputs": [],"name": "send0","type": "event"},{ "anonymous": false, "inputs": [],"name": "send","type": "event"}]`
|
||||||
|
|
||||||
contractAbi, err := JSON(strings.NewReader(abiJSON))
|
contractAbi, err := JSON(strings.NewReader(abiJSON))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := contractAbi.Events["send"]; !ok {
|
if _, ok := contractAbi.Events["send"]; !ok {
|
||||||
t.Fatalf("Could not find original event")
|
t.Fatalf("Could not find original event")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := contractAbi.Events["send0"]; !ok {
|
if _, ok := contractAbi.Events["send0"]; !ok {
|
||||||
t.Fatalf("Could not find duplicate event")
|
t.Fatalf("Could not find duplicate event")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := contractAbi.Events["send1"]; !ok {
|
if _, ok := contractAbi.Events["send1"]; !ok {
|
||||||
t.Fatalf("Could not find duplicate event")
|
t.Fatalf("Could not find duplicate event")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := contractAbi.Events["send2"]; ok {
|
if _, ok := contractAbi.Events["send2"]; ok {
|
||||||
t.Fatalf("Should not have found extra event")
|
t.Fatalf("Should not have found extra event")
|
||||||
}
|
}
|
||||||
|
|
@ -1108,11 +1212,13 @@ func TestDoubleDuplicateEventNames(t *testing.T) {
|
||||||
// TestUnnamedEventParam checks that an event with unnamed parameters is
|
// TestUnnamedEventParam checks that an event with unnamed parameters is
|
||||||
// correctly handled.
|
// correctly handled.
|
||||||
// The test runs the abi of the following contract.
|
// The test runs the abi of the following contract.
|
||||||
|
//
|
||||||
// contract TestEvent {
|
// contract TestEvent {
|
||||||
// event send(uint256, uint256);
|
// event send(uint256, uint256);
|
||||||
// }
|
// }
|
||||||
func TestUnnamedEventParam(t *testing.T) {
|
func TestUnnamedEventParam(t *testing.T) {
|
||||||
abiJSON := `[{ "anonymous": false, "inputs": [{ "indexed": false,"internalType": "uint256", "name": "","type": "uint256"},{"indexed": false,"internalType": "uint256","name": "","type": "uint256"}],"name": "send","type": "event"}]`
|
abiJSON := `[{ "anonymous": false, "inputs": [{ "indexed": false,"internalType": "uint256", "name": "","type": "uint256"},{"indexed": false,"internalType": "uint256","name": "","type": "uint256"}],"name": "send","type": "event"}]`
|
||||||
|
|
||||||
contractAbi, err := JSON(strings.NewReader(abiJSON))
|
contractAbi, err := JSON(strings.NewReader(abiJSON))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -1122,9 +1228,11 @@ func TestUnnamedEventParam(t *testing.T) {
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("Could not find event")
|
t.Fatalf("Could not find event")
|
||||||
}
|
}
|
||||||
|
|
||||||
if event.Inputs[0].Name != "arg0" {
|
if event.Inputs[0].Name != "arg0" {
|
||||||
t.Fatalf("Could not find input")
|
t.Fatalf("Could not find input")
|
||||||
}
|
}
|
||||||
|
|
||||||
if event.Inputs[1].Name != "arg1" {
|
if event.Inputs[1].Name != "arg1" {
|
||||||
t.Fatalf("Could not find input")
|
t.Fatalf("Could not find input")
|
||||||
}
|
}
|
||||||
|
|
@ -1142,6 +1250,7 @@ func TestUnpackRevert(t *testing.T) {
|
||||||
{"08c379a1", "", errors.New("invalid data for unpacking")},
|
{"08c379a1", "", errors.New("invalid data for unpacking")},
|
||||||
{"08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d72657665727420726561736f6e00000000000000000000000000000000000000", "revert reason", nil},
|
{"08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d72657665727420726561736f6e00000000000000000000000000000000000000", "revert reason", nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
for index, c := range cases {
|
for index, c := range cases {
|
||||||
t.Run(fmt.Sprintf("case %d", index), func(t *testing.T) {
|
t.Run(fmt.Sprintf("case %d", index), func(t *testing.T) {
|
||||||
got, err := UnpackRevert(common.Hex2Bytes(c.input))
|
got, err := UnpackRevert(common.Hex2Bytes(c.input))
|
||||||
|
|
@ -1149,11 +1258,14 @@ func TestUnpackRevert(t *testing.T) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("Expected non-nil error")
|
t.Fatalf("Expected non-nil error")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err.Error() != c.expectErr.Error() {
|
if err.Error() != c.expectErr.Error() {
|
||||||
t.Fatalf("Expected error mismatch, want %v, got %v", c.expectErr, err)
|
t.Fatalf("Expected error mismatch, want %v, got %v", c.expectErr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.expect != got {
|
if c.expect != got {
|
||||||
t.Fatalf("Output mismatch, want %v, got %v", c.expect, got)
|
t.Fatalf("Output mismatch, want %v, got %v", c.expect, got)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -44,6 +45,7 @@ type ArgumentMarshaling struct {
|
||||||
// UnmarshalJSON implements json.Unmarshaler interface.
|
// UnmarshalJSON implements json.Unmarshaler interface.
|
||||||
func (argument *Argument) UnmarshalJSON(data []byte) error {
|
func (argument *Argument) UnmarshalJSON(data []byte) error {
|
||||||
var arg ArgumentMarshaling
|
var arg ArgumentMarshaling
|
||||||
|
|
||||||
err := json.Unmarshal(data, &arg)
|
err := json.Unmarshal(data, &arg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("argument json err: %v", err)
|
return fmt.Errorf("argument json err: %v", err)
|
||||||
|
|
@ -53,6 +55,7 @@ func (argument *Argument) UnmarshalJSON(data []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
argument.Name = arg.Name
|
argument.Name = arg.Name
|
||||||
argument.Indexed = arg.Indexed
|
argument.Indexed = arg.Indexed
|
||||||
|
|
||||||
|
|
@ -62,11 +65,13 @@ func (argument *Argument) UnmarshalJSON(data []byte) error {
|
||||||
// NonIndexed returns the arguments with indexed arguments filtered out.
|
// NonIndexed returns the arguments with indexed arguments filtered out.
|
||||||
func (arguments Arguments) NonIndexed() Arguments {
|
func (arguments Arguments) NonIndexed() Arguments {
|
||||||
var ret []Argument
|
var ret []Argument
|
||||||
|
|
||||||
for _, arg := range arguments {
|
for _, arg := range arguments {
|
||||||
if !arg.Indexed {
|
if !arg.Indexed {
|
||||||
ret = append(ret, arg)
|
ret = append(ret, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -78,11 +83,13 @@ func (arguments Arguments) isTuple() bool {
|
||||||
// Unpack performs the operation hexdata -> Go format.
|
// Unpack performs the operation hexdata -> Go format.
|
||||||
func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) {
|
func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) {
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
if len(arguments) != 0 {
|
if len(arguments.NonIndexed()) != 0 {
|
||||||
return nil, fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
|
return nil, errors.New("abi: attempting to unmarshall an empty string while arguments are expected")
|
||||||
}
|
}
|
||||||
|
|
||||||
return make([]interface{}, 0), nil
|
return make([]interface{}, 0), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return arguments.UnpackValues(data)
|
return arguments.UnpackValues(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,21 +97,26 @@ func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) {
|
||||||
func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
|
func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
|
||||||
// Make sure map is not nil
|
// Make sure map is not nil
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return fmt.Errorf("abi: cannot unpack into a nil map")
|
return errors.New("abi: cannot unpack into a nil map")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
if len(arguments) != 0 {
|
if len(arguments.NonIndexed()) != 0 {
|
||||||
return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
|
return errors.New("abi: attempting to unmarshall an empty string while arguments are expected")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil // Nothing to unmarshal, return
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, arg := range arguments.NonIndexed() {
|
for i, arg := range arguments.NonIndexed() {
|
||||||
v[arg.Name] = marshalledValues[i]
|
v[arg.Name] = marshalledValues[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,15 +126,19 @@ func (arguments Arguments) Copy(v interface{}, values []interface{}) error {
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(values) == 0 {
|
if len(values) == 0 {
|
||||||
if len(arguments) != 0 {
|
if len(arguments.NonIndexed()) != 0 {
|
||||||
return fmt.Errorf("abi: attempting to copy no values while %d arguments are expected", len(arguments))
|
return errors.New("abi: attempting to copy no values while arguments are expected")
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil // Nothing to copy, return
|
return nil // Nothing to copy, return
|
||||||
}
|
}
|
||||||
|
|
||||||
if arguments.isTuple() {
|
if arguments.isTuple() {
|
||||||
return arguments.copyTuple(v, values)
|
return arguments.copyTuple(v, values)
|
||||||
}
|
}
|
||||||
|
|
||||||
return arguments.copyAtomic(v, values[0])
|
return arguments.copyAtomic(v, values[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,6 +150,7 @@ func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{
|
||||||
if dst.Kind() == reflect.Struct {
|
if dst.Kind() == reflect.Struct {
|
||||||
return set(dst.Field(0), src)
|
return set(dst.Field(0), src)
|
||||||
}
|
}
|
||||||
|
|
||||||
return set(dst, src)
|
return set(dst, src)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,16 +165,20 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
|
||||||
for i, arg := range nonIndexedArgs {
|
for i, arg := range nonIndexedArgs {
|
||||||
argNames[i] = arg.Name
|
argNames[i] = arg.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
abi2struct, err := mapArgNamesToStructFields(argNames, value)
|
abi2struct, err := mapArgNamesToStructFields(argNames, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, arg := range nonIndexedArgs {
|
for i, arg := range nonIndexedArgs {
|
||||||
field := value.FieldByName(abi2struct[arg.Name])
|
field := value.FieldByName(abi2struct[arg.Name])
|
||||||
if !field.IsValid() {
|
if !field.IsValid() {
|
||||||
return fmt.Errorf("abi: field %s can't be found in the given value", arg.Name)
|
return fmt.Errorf("abi: field %s can't be found in the given value", arg.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := set(field, reflect.ValueOf(marshalledValues[i])); err != nil {
|
if err := set(field, reflect.ValueOf(marshalledValues[i])); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -166,6 +187,7 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
|
||||||
if value.Len() < len(marshalledValues) {
|
if value.Len() < len(marshalledValues) {
|
||||||
return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
|
return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range nonIndexedArgs {
|
for i := range nonIndexedArgs {
|
||||||
if err := set(value.Index(i), reflect.ValueOf(marshalledValues[i])); err != nil {
|
if err := set(value.Index(i), reflect.ValueOf(marshalledValues[i])); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -174,6 +196,7 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", value.Type())
|
return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", value.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -183,9 +206,14 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
|
||||||
func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
|
func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
|
||||||
nonIndexedArgs := arguments.NonIndexed()
|
nonIndexedArgs := arguments.NonIndexed()
|
||||||
retval := make([]interface{}, 0, len(nonIndexedArgs))
|
retval := make([]interface{}, 0, len(nonIndexedArgs))
|
||||||
|
|
||||||
virtualArgs := 0
|
virtualArgs := 0
|
||||||
for index, arg := range nonIndexedArgs {
|
for index, arg := range nonIndexedArgs {
|
||||||
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
|
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) {
|
if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) {
|
||||||
// If we have a static array, like [3]uint256, these are coded as
|
// If we have a static array, like [3]uint256, these are coded as
|
||||||
// just like uint256,uint256,uint256.
|
// just like uint256,uint256,uint256.
|
||||||
|
|
@ -203,11 +231,10 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
|
||||||
// coded as just like uint256,bool,uint256
|
// coded as just like uint256,bool,uint256
|
||||||
virtualArgs += getTypeSize(arg.Type)/32 - 1
|
virtualArgs += getTypeSize(arg.Type)/32 - 1
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
retval = append(retval, marshalledValue)
|
retval = append(retval, marshalledValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval, nil
|
return retval, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,7 +260,9 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
|
||||||
for _, abiArg := range abiArgs {
|
for _, abiArg := range abiArgs {
|
||||||
inputOffset += getTypeSize(abiArg.Type)
|
inputOffset += getTypeSize(abiArg.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret []byte
|
var ret []byte
|
||||||
|
|
||||||
for i, a := range args {
|
for i, a := range args {
|
||||||
input := abiArgs[i]
|
input := abiArgs[i]
|
||||||
// pack the input
|
// pack the input
|
||||||
|
|
@ -268,5 +297,6 @@ func ToCamelCase(input string) string {
|
||||||
parts[i] = strings.ToUpper(s[:1]) + s[1:]
|
parts[i] = strings.ToUpper(s[:1]) + s[1:]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Join(parts, "")
|
return strings.Join(parts, "")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,9 @@ func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
|
||||||
// Deprecated: Use NewKeyStoreTransactorWithChainID instead.
|
// Deprecated: Use NewKeyStoreTransactorWithChainID instead.
|
||||||
func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account) (*TransactOpts, error) {
|
func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account) (*TransactOpts, error) {
|
||||||
log.Warn("WARNING: NewKeyStoreTransactor has been deprecated in favour of NewTransactorWithChainID")
|
log.Warn("WARNING: NewKeyStoreTransactor has been deprecated in favour of NewTransactorWithChainID")
|
||||||
|
|
||||||
signer := types.HomesteadSigner{}
|
signer := types.HomesteadSigner{}
|
||||||
|
|
||||||
return &TransactOpts{
|
return &TransactOpts{
|
||||||
From: account.Address,
|
From: account.Address,
|
||||||
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
|
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
|
||||||
|
|
@ -87,8 +89,10 @@ func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account
|
||||||
// Deprecated: Use NewKeyedTransactorWithChainID instead.
|
// Deprecated: Use NewKeyedTransactorWithChainID instead.
|
||||||
func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
|
func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
|
||||||
log.Warn("WARNING: NewKeyedTransactor has been deprecated in favour of NewKeyedTransactorWithChainID")
|
log.Warn("WARNING: NewKeyedTransactor has been deprecated in favour of NewKeyedTransactorWithChainID")
|
||||||
|
|
||||||
keyAddr := crypto.PubkeyToAddress(key.PublicKey)
|
keyAddr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
signer := types.HomesteadSigner{}
|
signer := types.HomesteadSigner{}
|
||||||
|
|
||||||
return &TransactOpts{
|
return &TransactOpts{
|
||||||
From: keyAddr,
|
From: keyAddr,
|
||||||
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
|
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
|
||||||
|
|
@ -112,10 +116,12 @@ func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.I
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := keystore.DecryptKey(json, passphrase)
|
key, err := keystore.DecryptKey(json, passphrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewKeyedTransactorWithChainID(key.PrivateKey, chainID)
|
return NewKeyedTransactorWithChainID(key.PrivateKey, chainID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,7 +131,9 @@ func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accou
|
||||||
if chainID == nil {
|
if chainID == nil {
|
||||||
return nil, ErrNoChainID
|
return nil, ErrNoChainID
|
||||||
}
|
}
|
||||||
|
|
||||||
signer := types.LatestSignerForChainID(chainID)
|
signer := types.LatestSignerForChainID(chainID)
|
||||||
|
|
||||||
return &TransactOpts{
|
return &TransactOpts{
|
||||||
From: account.Address,
|
From: account.Address,
|
||||||
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
|
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
|
||||||
|
|
@ -146,10 +154,13 @@ func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accou
|
||||||
// from a single private key.
|
// from a single private key.
|
||||||
func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*TransactOpts, error) {
|
func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*TransactOpts, error) {
|
||||||
keyAddr := crypto.PubkeyToAddress(key.PublicKey)
|
keyAddr := crypto.PubkeyToAddress(key.PublicKey)
|
||||||
|
|
||||||
if chainID == nil {
|
if chainID == nil {
|
||||||
return nil, ErrNoChainID
|
return nil, ErrNoChainID
|
||||||
}
|
}
|
||||||
|
|
||||||
signer := types.LatestSignerForChainID(chainID)
|
signer := types.LatestSignerForChainID(chainID)
|
||||||
|
|
||||||
return &TransactOpts{
|
return &TransactOpts{
|
||||||
From: keyAddr,
|
From: keyAddr,
|
||||||
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
|
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ func (fb *filterBackend) GetBorBlockReceipt(ctx context.Context, hash common.Has
|
||||||
if number == nil {
|
if number == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
receipt := rawdb.ReadRawBorReceipt(fb.db, hash, *number)
|
receipt := rawdb.ReadRawBorReceipt(fb.db, hash, *number)
|
||||||
if receipt == nil {
|
if receipt == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
|
||||||
|
|
@ -66,8 +66,10 @@ type SimulatedBackend struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
pendingBlock *types.Block // Currently pending block that will be imported on request
|
pendingBlock *types.Block // Currently pending block that will be imported on request
|
||||||
pendingState *state.StateDB // Currently pending state that will be the active on request
|
pendingState *state.StateDB // Currently pending state that will be the active on request
|
||||||
|
pendingReceipts types.Receipts // Currently receipts for the pending block
|
||||||
|
|
||||||
events *filters.EventSystem // Event system for filtering log events live
|
events *filters.EventSystem // for filtering log events live
|
||||||
|
filterSystem *filters.FilterSystem // for filtering database logs
|
||||||
|
|
||||||
config *params.ChainConfig
|
config *params.ChainConfig
|
||||||
}
|
}
|
||||||
|
|
@ -76,17 +78,28 @@ type SimulatedBackend struct {
|
||||||
// and uses a simulated blockchain for testing purposes.
|
// and uses a simulated blockchain for testing purposes.
|
||||||
// A simulated backend always uses chainID 1337.
|
// A simulated backend always uses chainID 1337.
|
||||||
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
||||||
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
|
genesis := core.Genesis{
|
||||||
genesis.MustCommit(database)
|
Config: params.AllEthashProtocolChanges,
|
||||||
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
GasLimit: gasLimit,
|
||||||
|
Alloc: alloc,
|
||||||
|
}
|
||||||
|
blockchain, _ := core.NewBlockChain(database, nil, &genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||||
|
|
||||||
backend := &SimulatedBackend{
|
backend := &SimulatedBackend{
|
||||||
database: database,
|
database: database,
|
||||||
blockchain: blockchain,
|
blockchain: blockchain,
|
||||||
config: genesis.Config,
|
config: genesis.Config,
|
||||||
events: filters.NewEventSystem(&filterBackend{database, blockchain}, false),
|
|
||||||
}
|
}
|
||||||
backend.rollback(blockchain.CurrentBlock())
|
|
||||||
|
filterBackend := &filterBackend{database, blockchain, backend}
|
||||||
|
backend.filterSystem = filters.NewFilterSystem(filterBackend, filters.Config{})
|
||||||
|
backend.events = filters.NewEventSystem(backend.filterSystem, false)
|
||||||
|
|
||||||
|
header := backend.blockchain.CurrentBlock()
|
||||||
|
block := backend.blockchain.GetBlock(header.Hash(), header.Number.Uint64())
|
||||||
|
|
||||||
|
backend.rollback(block)
|
||||||
|
|
||||||
return backend
|
return backend
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,16 +118,21 @@ func (b *SimulatedBackend) Close() error {
|
||||||
|
|
||||||
// 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() common.Hash {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil {
|
if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil {
|
||||||
panic(err) // This cannot happen unless the simulator is wrong, fail in that case
|
panic(err) // This cannot happen unless the simulator is wrong, fail in that case
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blockHash := b.pendingBlock.Hash()
|
||||||
|
|
||||||
// Using the last inserted block here makes it possible to build on a side
|
// Using the last inserted block here makes it possible to build on a side
|
||||||
// chain after a fork.
|
// chain after a fork.
|
||||||
b.rollback(b.pendingBlock)
|
b.rollback(b.pendingBlock)
|
||||||
|
|
||||||
|
return blockHash
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rollback aborts all pending transactions, reverting to the last committed state.
|
// Rollback aborts all pending transactions, reverting to the last committed state.
|
||||||
|
|
@ -122,7 +140,10 @@ func (b *SimulatedBackend) Rollback() {
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
b.rollback(b.blockchain.CurrentBlock())
|
header := b.blockchain.CurrentBlock()
|
||||||
|
block := b.blockchain.GetBlock(header.Hash(), header.Number.Uint64())
|
||||||
|
|
||||||
|
b.rollback(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *SimulatedBackend) rollback(parent *types.Block) {
|
func (b *SimulatedBackend) rollback(parent *types.Block) {
|
||||||
|
|
@ -151,23 +172,28 @@ func (b *SimulatedBackend) Fork(ctx context.Context, parent common.Hash) error {
|
||||||
if len(b.pendingBlock.Transactions()) != 0 {
|
if len(b.pendingBlock.Transactions()) != 0 {
|
||||||
return errors.New("pending block dirty")
|
return errors.New("pending block dirty")
|
||||||
}
|
}
|
||||||
|
|
||||||
block, err := b.blockByHash(ctx, parent)
|
block, err := b.blockByHash(ctx, parent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
b.rollback(block)
|
b.rollback(block)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// stateByBlockNumber retrieves a state by a given blocknumber.
|
// stateByBlockNumber retrieves a state by a given blocknumber.
|
||||||
func (b *SimulatedBackend) stateByBlockNumber(ctx context.Context, blockNumber *big.Int) (*state.StateDB, error) {
|
func (b *SimulatedBackend) stateByBlockNumber(ctx context.Context, blockNumber *big.Int) (*state.StateDB, error) {
|
||||||
if blockNumber == nil || blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) == 0 {
|
if blockNumber == nil || blockNumber.Cmp(b.blockchain.CurrentBlock().Number) == 0 {
|
||||||
return b.blockchain.State()
|
return b.blockchain.State()
|
||||||
}
|
}
|
||||||
|
|
||||||
block, err := b.blockByNumber(ctx, blockNumber)
|
block, err := b.blockByNumber(ctx, blockNumber)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.blockchain.StateAt(block.Root())
|
return b.blockchain.StateAt(block.Root())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -221,6 +247,7 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres
|
||||||
}
|
}
|
||||||
|
|
||||||
val := stateDB.GetState(contract, key)
|
val := stateDB.GetState(contract, key)
|
||||||
|
|
||||||
return val[:], nil
|
return val[:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,6 +260,7 @@ func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common
|
||||||
if receipt == nil {
|
if receipt == nil {
|
||||||
return nil, ethereum.NotFound
|
return nil, ethereum.NotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
return receipt, nil
|
return receipt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -248,10 +276,12 @@ func (b *SimulatedBackend) TransactionByHash(ctx context.Context, txHash common.
|
||||||
if tx != nil {
|
if tx != nil {
|
||||||
return tx, true, nil
|
return tx, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
tx, _, _, _ = rawdb.ReadTransaction(b.database, txHash)
|
tx, _, _, _ = rawdb.ReadTransaction(b.database, txHash)
|
||||||
if tx != nil {
|
if tx != nil {
|
||||||
return tx, false, nil
|
return tx, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, false, ethereum.NotFound
|
return nil, false, ethereum.NotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,7 +320,7 @@ func (b *SimulatedBackend) BlockByNumber(ctx context.Context, number *big.Int) (
|
||||||
// (associated with its hash) if found without Lock.
|
// (associated with its hash) if found without Lock.
|
||||||
func (b *SimulatedBackend) blockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
|
func (b *SimulatedBackend) blockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
|
||||||
if number == nil || number.Cmp(b.pendingBlock.Number()) == 0 {
|
if number == nil || number.Cmp(b.pendingBlock.Number()) == 0 {
|
||||||
return b.blockchain.CurrentBlock(), nil
|
return b.blockByHash(ctx, b.blockchain.CurrentBlock().Hash())
|
||||||
}
|
}
|
||||||
|
|
||||||
block := b.blockchain.GetBlockByNumber(uint64(number.Int64()))
|
block := b.blockchain.GetBlockByNumber(uint64(number.Int64()))
|
||||||
|
|
@ -386,9 +416,11 @@ func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Ad
|
||||||
func newRevertError(result *core.ExecutionResult) *revertError {
|
func newRevertError(result *core.ExecutionResult) *revertError {
|
||||||
reason, errUnpack := abi.UnpackRevert(result.Revert())
|
reason, errUnpack := abi.UnpackRevert(result.Revert())
|
||||||
err := errors.New("execution reverted")
|
err := errors.New("execution reverted")
|
||||||
|
|
||||||
if errUnpack == nil {
|
if errUnpack == nil {
|
||||||
err = fmt.Errorf("execution reverted: %v", reason)
|
err = fmt.Errorf("execution reverted: %v", reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &revertError{
|
return &revertError{
|
||||||
error: err,
|
error: err,
|
||||||
reason: hexutil.Encode(result.Revert()),
|
reason: hexutil.Encode(result.Revert()),
|
||||||
|
|
@ -418,13 +450,15 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
|
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number) != 0 {
|
||||||
return nil, errBlockNumberUnsupported
|
return nil, errBlockNumberUnsupported
|
||||||
}
|
}
|
||||||
|
|
||||||
stateDB, err := b.blockchain.State()
|
stateDB, err := b.blockchain.State()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), stateDB)
|
res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), stateDB)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -433,6 +467,7 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
|
||||||
if len(res.Revert()) > 0 {
|
if len(res.Revert()) > 0 {
|
||||||
return nil, newRevertError(res)
|
return nil, newRevertError(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.Return(), res.Err
|
return res.Return(), res.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -442,7 +477,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
|
||||||
defer b.mu.Unlock()
|
defer b.mu.Unlock()
|
||||||
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
|
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
|
||||||
|
|
||||||
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
res, err := b.callContract(ctx, call, b.pendingBlock.Header(), b.pendingState)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -450,6 +485,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
|
||||||
if len(res.Revert()) > 0 {
|
if len(res.Revert()) > 0 {
|
||||||
return nil, newRevertError(res)
|
return nil, newRevertError(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.Return(), res.Err
|
return res.Return(), res.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -471,6 +507,7 @@ func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error
|
||||||
if b.pendingBlock.Header().BaseFee != nil {
|
if b.pendingBlock.Header().BaseFee != nil {
|
||||||
return b.pendingBlock.Header().BaseFee, nil
|
return b.pendingBlock.Header().BaseFee, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return big.NewInt(1), nil
|
return big.NewInt(1), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -492,6 +529,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
||||||
hi uint64
|
hi uint64
|
||||||
cap uint64
|
cap uint64
|
||||||
)
|
)
|
||||||
|
|
||||||
if call.Gas >= params.TxGas {
|
if call.Gas >= params.TxGas {
|
||||||
hi = call.Gas
|
hi = call.Gas
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -499,6 +537,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
||||||
}
|
}
|
||||||
// Normalize the max fee per gas the call is willing to spend.
|
// Normalize the max fee per gas the call is willing to spend.
|
||||||
var feeCap *big.Int
|
var feeCap *big.Int
|
||||||
|
|
||||||
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
|
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
|
||||||
return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
||||||
} else if call.GasPrice != nil {
|
} else if call.GasPrice != nil {
|
||||||
|
|
@ -511,24 +550,30 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
||||||
// Recap the highest gas allowance with account's balance.
|
// Recap the highest gas allowance with account's balance.
|
||||||
if feeCap.BitLen() != 0 {
|
if feeCap.BitLen() != 0 {
|
||||||
balance := b.pendingState.GetBalance(call.From) // from can't be nil
|
balance := b.pendingState.GetBalance(call.From) // from can't be nil
|
||||||
|
|
||||||
available := new(big.Int).Set(balance)
|
available := new(big.Int).Set(balance)
|
||||||
if call.Value != nil {
|
if call.Value != nil {
|
||||||
if call.Value.Cmp(available) >= 0 {
|
if call.Value.Cmp(available) >= 0 {
|
||||||
return 0, errors.New("insufficient funds for transfer")
|
return 0, core.ErrInsufficientFundsForTransfer
|
||||||
}
|
}
|
||||||
|
|
||||||
available.Sub(available, call.Value)
|
available.Sub(available, call.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
allowance := new(big.Int).Div(available, feeCap)
|
allowance := new(big.Int).Div(available, feeCap)
|
||||||
if allowance.IsUint64() && hi > allowance.Uint64() {
|
if allowance.IsUint64() && hi > allowance.Uint64() {
|
||||||
transfer := call.Value
|
transfer := call.Value
|
||||||
if transfer == nil {
|
if transfer == nil {
|
||||||
transfer = new(big.Int)
|
transfer = new(big.Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
|
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
|
||||||
"sent", transfer, "feecap", feeCap, "fundable", allowance)
|
"sent", transfer, "feecap", feeCap, "fundable", allowance)
|
||||||
|
|
||||||
hi = allowance.Uint64()
|
hi = allowance.Uint64()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cap = hi
|
cap = hi
|
||||||
|
|
||||||
// Create a helper to check if a gas allowance results in an executable transaction
|
// Create a helper to check if a gas allowance results in an executable transaction
|
||||||
|
|
@ -536,15 +581,17 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
||||||
call.Gas = gas
|
call.Gas = gas
|
||||||
|
|
||||||
snapshot := b.pendingState.Snapshot()
|
snapshot := b.pendingState.Snapshot()
|
||||||
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
res, err := b.callContract(ctx, call, b.pendingBlock.Header(), b.pendingState)
|
||||||
b.pendingState.RevertToSnapshot(snapshot)
|
b.pendingState.RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, core.ErrIntrinsicGas) {
|
if errors.Is(err, core.ErrIntrinsicGas) {
|
||||||
return true, nil, nil // Special case, raise gas limit
|
return true, nil, nil // Special case, raise gas limit
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, nil, err // Bail out
|
return true, nil, err // Bail out
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.Failed(), res, nil
|
return res.Failed(), res, nil
|
||||||
}
|
}
|
||||||
// Execute the binary search and hone in on an executable gas limit
|
// Execute the binary search and hone in on an executable gas limit
|
||||||
|
|
@ -558,6 +605,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if failed {
|
if failed {
|
||||||
lo = mid
|
lo = mid
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -570,33 +618,38 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if failed {
|
if failed {
|
||||||
if result != nil && result.Err != vm.ErrOutOfGas {
|
if result != nil && result.Err != vm.ErrOutOfGas {
|
||||||
if len(result.Revert()) > 0 {
|
if len(result.Revert()) > 0 {
|
||||||
return 0, newRevertError(result)
|
return 0, newRevertError(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0, result.Err
|
return 0, result.Err
|
||||||
}
|
}
|
||||||
// Otherwise, the specified gas cap is too low
|
// Otherwise, the specified gas cap is too low
|
||||||
return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
|
return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hi, nil
|
return hi, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// callContract implements common code between normal and pending contract calls.
|
// callContract implements common code between normal and pending contract calls.
|
||||||
// state is modified during execution, make sure to copy it if necessary.
|
// state is modified during execution, make sure to copy it if necessary.
|
||||||
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, stateDB *state.StateDB) (*core.ExecutionResult, error) {
|
func (b *SimulatedBackend) callContract(_ context.Context, call ethereum.CallMsg, header *types.Header, stateDB *state.StateDB) (*core.ExecutionResult, error) {
|
||||||
// Gas prices post 1559 need to be initialized
|
// Gas prices post 1559 need to be initialized
|
||||||
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
|
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
|
||||||
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
head := b.blockchain.CurrentHeader()
|
head := b.blockchain.CurrentHeader()
|
||||||
if !b.blockchain.Config().IsLondon(head.Number) {
|
if !b.blockchain.Config().IsLondon(head.Number) {
|
||||||
// If there's no basefee, then it must be a non-1559 execution
|
// If there's no basefee, then it must be a non-1559 execution
|
||||||
if call.GasPrice == nil {
|
if call.GasPrice == nil {
|
||||||
call.GasPrice = new(big.Int)
|
call.GasPrice = new(big.Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
|
call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
|
||||||
} else {
|
} else {
|
||||||
// A basefee is provided, necessitating 1559-type execution
|
// A basefee is provided, necessitating 1559-type execution
|
||||||
|
|
@ -604,10 +657,11 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
|
||||||
// User specified the legacy gas field, convert to 1559 gas typing
|
// User specified the legacy gas field, convert to 1559 gas typing
|
||||||
call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
|
call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
|
||||||
} else {
|
} else {
|
||||||
// User specified 1559 gas feilds (or none), use those
|
// User specified 1559 gas fields (or none), use those
|
||||||
if call.GasFeeCap == nil {
|
if call.GasFeeCap == nil {
|
||||||
call.GasFeeCap = new(big.Int)
|
call.GasFeeCap = new(big.Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
if call.GasTipCap == nil {
|
if call.GasTipCap == nil {
|
||||||
call.GasTipCap = new(big.Int)
|
call.GasTipCap = new(big.Int)
|
||||||
}
|
}
|
||||||
|
|
@ -622,23 +676,38 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
|
||||||
if call.Gas == 0 {
|
if call.Gas == 0 {
|
||||||
call.Gas = 50000000
|
call.Gas = 50000000
|
||||||
}
|
}
|
||||||
|
|
||||||
if call.Value == nil {
|
if call.Value == nil {
|
||||||
call.Value = new(big.Int)
|
call.Value = new(big.Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set infinite balance to the fake caller account.
|
// Set infinite balance to the fake caller account.
|
||||||
from := stateDB.GetOrNewStateObject(call.From)
|
from := stateDB.GetOrNewStateObject(call.From)
|
||||||
from.SetBalance(math.MaxBig256)
|
from.SetBalance(math.MaxBig256)
|
||||||
// Execute the call.
|
|
||||||
msg := callMsg{call}
|
|
||||||
|
|
||||||
txContext := core.NewEVMTxContext(msg)
|
// Execute the call.
|
||||||
evmContext := core.NewEVMBlockContext(block.Header(), b.blockchain, nil)
|
msg := &core.Message{
|
||||||
|
From: call.From,
|
||||||
|
To: call.To,
|
||||||
|
Value: call.Value,
|
||||||
|
GasLimit: call.Gas,
|
||||||
|
GasPrice: call.GasPrice,
|
||||||
|
GasFeeCap: call.GasFeeCap,
|
||||||
|
GasTipCap: call.GasTipCap,
|
||||||
|
Data: call.Data,
|
||||||
|
AccessList: call.AccessList,
|
||||||
|
SkipAccountChecks: true,
|
||||||
|
}
|
||||||
|
|
||||||
// Create a new environment which holds all relevant information
|
// Create a new environment which holds all relevant information
|
||||||
// about the transaction and calling mechanisms.
|
// about the transaction and calling mechanisms.
|
||||||
|
txContext := core.NewEVMTxContext(msg)
|
||||||
|
evmContext := core.NewEVMBlockContext(header, b.blockchain, nil)
|
||||||
vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true})
|
vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true})
|
||||||
gasPool := new(core.GasPool).AddGas(math.MaxUint64)
|
gasPool := new(core.GasPool).AddGas(math.MaxUint64)
|
||||||
|
|
||||||
//nolint:contextcheck
|
//nolint:contextcheck
|
||||||
return core.NewStateTransition(vmEnv, msg, gasPool).TransitionDb(context.Background())
|
return core.ApplyMessage(vmEnv, msg, gasPool, context.Background())
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendTransaction updates the pending block to include the given transaction.
|
// SendTransaction updates the pending block to include the given transaction.
|
||||||
|
|
@ -653,25 +722,31 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
|
||||||
}
|
}
|
||||||
// Check transaction validity
|
// Check transaction validity
|
||||||
signer := types.MakeSigner(b.blockchain.Config(), block.Number())
|
signer := types.MakeSigner(b.blockchain.Config(), block.Number())
|
||||||
|
|
||||||
sender, err := types.Sender(signer, tx)
|
sender, err := types.Sender(signer, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid transaction: %v", err)
|
return fmt.Errorf("invalid transaction: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
nonce := b.pendingState.GetNonce(sender)
|
nonce := b.pendingState.GetNonce(sender)
|
||||||
if tx.Nonce() != nonce {
|
if tx.Nonce() != nonce {
|
||||||
return fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)
|
return fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)
|
||||||
}
|
}
|
||||||
// Include tx in chain
|
// Include tx in chain
|
||||||
blocks, _ := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
// nolint : contextcheck
|
||||||
|
blocks, receipts := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||||
for _, tx := range b.pendingBlock.Transactions() {
|
for _, tx := range b.pendingBlock.Transactions() {
|
||||||
block.AddTxWithChain(b.blockchain, tx)
|
block.AddTxWithChain(b.blockchain, tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
block.AddTxWithChain(b.blockchain, tx)
|
block.AddTxWithChain(b.blockchain, tx)
|
||||||
})
|
})
|
||||||
stateDB, _ := b.blockchain.State()
|
stateDB, _ := b.blockchain.State()
|
||||||
|
|
||||||
b.pendingBlock = blocks[0]
|
b.pendingBlock = blocks[0]
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
|
||||||
|
b.pendingReceipts = receipts[0]
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -683,29 +758,32 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.Filter
|
||||||
var filter *filters.Filter
|
var filter *filters.Filter
|
||||||
if query.BlockHash != nil {
|
if query.BlockHash != nil {
|
||||||
// Block filter requested, construct a single-shot filter
|
// Block filter requested, construct a single-shot filter
|
||||||
filter = filters.NewBlockFilter(&filterBackend{b.database, b.blockchain}, *query.BlockHash, query.Addresses, query.Topics)
|
filter = b.filterSystem.NewBlockFilter(*query.BlockHash, query.Addresses, query.Topics)
|
||||||
} else {
|
} else {
|
||||||
// Initialize unset filter boundaries to run from genesis to chain head
|
// Initialize unset filter boundaries to run from genesis to chain head
|
||||||
from := int64(0)
|
from := int64(0)
|
||||||
if query.FromBlock != nil {
|
if query.FromBlock != nil {
|
||||||
from = query.FromBlock.Int64()
|
from = query.FromBlock.Int64()
|
||||||
}
|
}
|
||||||
|
|
||||||
to := int64(-1)
|
to := int64(-1)
|
||||||
if query.ToBlock != nil {
|
if query.ToBlock != nil {
|
||||||
to = query.ToBlock.Int64()
|
to = query.ToBlock.Int64()
|
||||||
}
|
}
|
||||||
// Construct the range filter
|
// Construct the range filter
|
||||||
filter = filters.NewRangeFilter(&filterBackend{b.database, b.blockchain}, from, to, query.Addresses, query.Topics)
|
filter = b.filterSystem.NewRangeFilter(from, to, query.Addresses, query.Topics)
|
||||||
}
|
}
|
||||||
// Run the filter and return all the logs
|
// Run the filter and return all the logs
|
||||||
logs, err := filter.Logs(ctx)
|
logs, err := filter.Logs(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
res := make([]types.Log, len(logs))
|
res := make([]types.Log, len(logs))
|
||||||
for i, nLog := range logs {
|
for i, nLog := range logs {
|
||||||
res[i] = *nLog
|
res[i] = *nLog
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -722,6 +800,7 @@ func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query ethere
|
||||||
// Since we're getting logs in batches, we need to flatten them into a plain stream
|
// Since we're getting logs in batches, we need to flatten them into a plain stream
|
||||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||||
defer sub.Unsubscribe()
|
defer sub.Unsubscribe()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case logs := <-sink:
|
case logs := <-sink:
|
||||||
|
|
@ -751,6 +830,7 @@ func (b *SimulatedBackend) SubscribeNewHead(ctx context.Context, ch chan<- *type
|
||||||
|
|
||||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||||
defer sub.Unsubscribe()
|
defer sub.Unsubscribe()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case head := <-sink:
|
case head := <-sink:
|
||||||
|
|
@ -779,8 +859,13 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
|
||||||
if len(b.pendingBlock.Transactions()) != 0 {
|
if len(b.pendingBlock.Transactions()) != 0 {
|
||||||
return errors.New("Could not adjust time on non-empty block")
|
return errors.New("Could not adjust time on non-empty block")
|
||||||
}
|
}
|
||||||
|
// Get the last block
|
||||||
|
block := b.blockchain.GetBlockByHash(b.pendingBlock.ParentHash())
|
||||||
|
if block == nil {
|
||||||
|
return fmt.Errorf("could not find parent")
|
||||||
|
}
|
||||||
|
|
||||||
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
blocks, _ := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||||
block.OffsetTime(int64(adjustment.Seconds()))
|
block.OffsetTime(int64(adjustment.Seconds()))
|
||||||
})
|
})
|
||||||
stateDB, _ := b.blockchain.State()
|
stateDB, _ := b.blockchain.State()
|
||||||
|
|
@ -796,65 +881,65 @@ func (b *SimulatedBackend) Blockchain() *core.BlockChain {
|
||||||
return b.blockchain
|
return b.blockchain
|
||||||
}
|
}
|
||||||
|
|
||||||
// callMsg implements core.Message to allow passing it as a transaction simulator.
|
|
||||||
type callMsg struct {
|
|
||||||
ethereum.CallMsg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m callMsg) From() common.Address { return m.CallMsg.From }
|
|
||||||
func (m callMsg) Nonce() uint64 { return 0 }
|
|
||||||
func (m callMsg) IsFake() bool { return true }
|
|
||||||
func (m callMsg) To() *common.Address { return m.CallMsg.To }
|
|
||||||
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
|
|
||||||
func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap }
|
|
||||||
func (m callMsg) GasTipCap() *big.Int { return m.CallMsg.GasTipCap }
|
|
||||||
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
|
|
||||||
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
|
|
||||||
func (m callMsg) Data() []byte { return m.CallMsg.Data }
|
|
||||||
func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList }
|
|
||||||
|
|
||||||
// filterBackend implements filters.Backend to support filtering for logs without
|
// filterBackend implements filters.Backend to support filtering for logs without
|
||||||
// taking bloom-bits acceleration structures into account.
|
// taking bloom-bits acceleration structures into account.
|
||||||
type filterBackend struct {
|
type filterBackend struct {
|
||||||
db ethdb.Database
|
db ethdb.Database
|
||||||
bc *core.BlockChain
|
bc *core.BlockChain
|
||||||
|
backend *SimulatedBackend
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db }
|
func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db }
|
||||||
|
|
||||||
func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") }
|
func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") }
|
||||||
|
|
||||||
func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumber) (*types.Header, error) {
|
func (fb *filterBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
|
||||||
if block == rpc.LatestBlockNumber {
|
// nolint : exhaustive
|
||||||
return fb.bc.CurrentHeader(), nil
|
switch number {
|
||||||
|
case rpc.PendingBlockNumber:
|
||||||
|
if block := fb.backend.pendingBlock; block != nil {
|
||||||
|
return block.Header(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
case rpc.LatestBlockNumber:
|
||||||
|
return fb.bc.CurrentHeader(), nil
|
||||||
|
case rpc.FinalizedBlockNumber:
|
||||||
|
return fb.bc.CurrentFinalBlock(), nil
|
||||||
|
case rpc.SafeBlockNumber:
|
||||||
|
return fb.bc.CurrentSafeBlock(), nil
|
||||||
|
default:
|
||||||
|
return fb.bc.GetHeaderByNumber(uint64(number.Int64())), nil
|
||||||
}
|
}
|
||||||
return fb.bc.GetHeaderByNumber(uint64(block.Int64())), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
||||||
return fb.bc.GetHeaderByHash(hash), nil
|
return fb.bc.GetHeaderByHash(hash), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fb *filterBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
|
||||||
|
if body := fb.bc.GetBody(hash); body != nil {
|
||||||
|
return body, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("block body not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fb *filterBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
|
||||||
|
return fb.backend.pendingBlock, fb.backend.pendingReceipts
|
||||||
|
}
|
||||||
|
|
||||||
func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
||||||
number := rawdb.ReadHeaderNumber(fb.db, hash)
|
number := rawdb.ReadHeaderNumber(fb.db, hash)
|
||||||
if number == nil {
|
if number == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil
|
return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
|
func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
|
||||||
number := rawdb.ReadHeaderNumber(fb.db, hash)
|
logs := rawdb.ReadLogs(fb.db, hash, number, fb.bc.Config())
|
||||||
if number == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
receipts := rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config())
|
|
||||||
if receipts == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
logs := make([][]*types.Log, len(receipts))
|
|
||||||
for i, receipt := range receipts {
|
|
||||||
logs[i] = receipt.Logs
|
|
||||||
}
|
|
||||||
return logs, nil
|
return logs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -884,6 +969,14 @@ func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.Matche
|
||||||
panic("not supported")
|
panic("not supported")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (fb *filterBackend) ChainConfig() *params.ChainConfig {
|
||||||
|
panic("not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fb *filterBackend) CurrentHeader() *types.Header {
|
||||||
|
panic("not supported")
|
||||||
|
}
|
||||||
|
|
||||||
func nullSubscription() event.Subscription {
|
func nullSubscription() event.Subscription {
|
||||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||||
<-quit
|
<-quit
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ func TestSimulatedBackend(t *testing.T) {
|
||||||
defer goleak.VerifyNone(t, leak.IgnoreList()...)
|
defer goleak.VerifyNone(t, leak.IgnoreList()...)
|
||||||
|
|
||||||
var gasLimit uint64 = 8000029
|
var gasLimit uint64 = 8000029
|
||||||
|
|
||||||
key, _ := crypto.GenerateKey() // nolint: gosec
|
key, _ := crypto.GenerateKey() // nolint: gosec
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||||
genAlloc := make(core.GenesisAlloc)
|
genAlloc := make(core.GenesisAlloc)
|
||||||
|
|
@ -59,6 +60,7 @@ func TestSimulatedBackend(t *testing.T) {
|
||||||
if isPending {
|
if isPending {
|
||||||
t.Fatal("transaction should not be pending")
|
t.Fatal("transaction should not be pending")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != ethereum.NotFound {
|
if err != ethereum.NotFound {
|
||||||
t.Fatalf("err should be `ethereum.NotFound` but received %v", err)
|
t.Fatalf("err should be `ethereum.NotFound` but received %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -68,6 +70,7 @@ func TestSimulatedBackend(t *testing.T) {
|
||||||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||||
|
|
||||||
code := `6060604052600a8060106000396000f360606040526008565b00`
|
code := `6060604052600a8060106000396000f360606040526008565b00`
|
||||||
|
|
||||||
var gas uint64 = 3000000
|
var gas uint64 = 3000000
|
||||||
tx := types.NewContractCreation(0, big.NewInt(0), gas, gasPrice, common.FromHex(code))
|
tx := types.NewContractCreation(0, big.NewInt(0), gas, gasPrice, common.FromHex(code))
|
||||||
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, key)
|
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, key)
|
||||||
|
|
@ -79,18 +82,22 @@ func TestSimulatedBackend(t *testing.T) {
|
||||||
|
|
||||||
txHash = tx.Hash()
|
txHash = tx.Hash()
|
||||||
_, isPending, err = sim.TransactionByHash(context.Background(), txHash)
|
_, isPending, err = sim.TransactionByHash(context.Background(), txHash)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error getting transaction with hash: %v", txHash.String())
|
t.Fatalf("error getting transaction with hash: %v", txHash.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isPending {
|
if !isPending {
|
||||||
t.Fatal("transaction should have pending status")
|
t.Fatal("transaction should have pending status")
|
||||||
}
|
}
|
||||||
|
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
|
|
||||||
_, isPending, err = sim.TransactionByHash(context.Background(), txHash)
|
_, isPending, err = sim.TransactionByHash(context.Background(), txHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error getting transaction with hash: %v", txHash.String())
|
t.Fatalf("error getting transaction with hash: %v", txHash.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if isPending {
|
if isPending {
|
||||||
t.Fatal("transaction should not have pending status")
|
t.Fatal("transaction should not have pending status")
|
||||||
}
|
}
|
||||||
|
|
@ -99,6 +106,7 @@ func TestSimulatedBackend(t *testing.T) {
|
||||||
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
|
||||||
// the following is based on this contract:
|
// the following is based on this contract:
|
||||||
|
//
|
||||||
// contract T {
|
// contract T {
|
||||||
// event received(address sender, uint amount, bytes memo);
|
// event received(address sender, uint amount, bytes memo);
|
||||||
// event receivedAddr(address sender);
|
// event receivedAddr(address sender);
|
||||||
|
|
@ -127,6 +135,7 @@ func simTestBackend(testAddr common.Address) *SimulatedBackend {
|
||||||
func TestNewSimulatedBackend(t *testing.T) {
|
func TestNewSimulatedBackend(t *testing.T) {
|
||||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
expectedBal := big.NewInt(10000000000000000)
|
expectedBal := big.NewInt(10000000000000000)
|
||||||
|
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
|
|
@ -139,6 +148,7 @@ func TestNewSimulatedBackend(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
stateDB, _ := sim.blockchain.State()
|
stateDB, _ := sim.blockchain.State()
|
||||||
|
|
||||||
bal := stateDB.GetBalance(testAddr)
|
bal := stateDB.GetBalance(testAddr)
|
||||||
if bal.Cmp(expectedBal) != 0 {
|
if bal.Cmp(expectedBal) != 0 {
|
||||||
t.Errorf("expected balance for test address not received. expected: %v actual: %v", expectedBal, bal)
|
t.Errorf("expected balance for test address not received. expected: %v actual: %v", expectedBal, bal)
|
||||||
|
|
@ -152,9 +162,11 @@ func TestAdjustTime(t *testing.T) {
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
prevTime := sim.pendingBlock.Time()
|
prevTime := sim.pendingBlock.Time()
|
||||||
|
|
||||||
if err := sim.AdjustTime(time.Second); err != nil {
|
if err := sim.AdjustTime(time.Second); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
newTime := sim.pendingBlock.Time()
|
newTime := sim.pendingBlock.Time()
|
||||||
|
|
||||||
if newTime-prevTime != uint64(time.Second.Seconds()) {
|
if newTime-prevTime != uint64(time.Second.Seconds()) {
|
||||||
|
|
@ -171,33 +183,41 @@ func TestNewAdjustTimeFail(t *testing.T) {
|
||||||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||||
|
|
||||||
tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||||
|
|
||||||
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not sign tx: %v", err)
|
t.Errorf("could not sign tx: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sim.SendTransaction(context.Background(), signedTx)
|
sim.SendTransaction(context.Background(), signedTx)
|
||||||
// AdjustTime should fail on non-empty block
|
// AdjustTime should fail on non-empty block
|
||||||
if err := sim.AdjustTime(time.Second); err == nil {
|
if err := sim.AdjustTime(time.Second); err == nil {
|
||||||
t.Error("Expected adjust time to error on non-empty block")
|
t.Error("Expected adjust time to error on non-empty block")
|
||||||
}
|
}
|
||||||
|
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
|
|
||||||
prevTime := sim.pendingBlock.Time()
|
prevTime := sim.pendingBlock.Time()
|
||||||
|
|
||||||
if err := sim.AdjustTime(time.Minute); err != nil {
|
if err := sim.AdjustTime(time.Minute); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
newTime := sim.pendingBlock.Time()
|
newTime := sim.pendingBlock.Time()
|
||||||
if newTime-prevTime != uint64(time.Minute.Seconds()) {
|
if newTime-prevTime != uint64(time.Minute.Seconds()) {
|
||||||
t.Errorf("adjusted time not equal to a minute. prev: %v, new: %v", prevTime, newTime)
|
t.Errorf("adjusted time not equal to a minute. prev: %v, new: %v", prevTime, newTime)
|
||||||
}
|
}
|
||||||
// Put a transaction after adjusting time
|
// Put a transaction after adjusting time
|
||||||
tx2 := types.NewTransaction(1, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
tx2 := types.NewTransaction(1, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||||
|
|
||||||
signedTx2, err := types.SignTx(tx2, types.HomesteadSigner{}, testKey)
|
signedTx2, err := types.SignTx(tx2, types.HomesteadSigner{}, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not sign tx: %v", err)
|
t.Errorf("could not sign tx: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sim.SendTransaction(context.Background(), signedTx2)
|
sim.SendTransaction(context.Background(), signedTx2)
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
|
|
||||||
newTime = sim.pendingBlock.Time()
|
newTime = sim.pendingBlock.Time()
|
||||||
if newTime-prevTime >= uint64(time.Minute.Seconds()) {
|
if newTime-prevTime >= uint64(time.Minute.Seconds()) {
|
||||||
t.Errorf("time adjusted, but shouldn't be: prev: %v, new: %v", prevTime, newTime)
|
t.Errorf("time adjusted, but shouldn't be: prev: %v, new: %v", prevTime, newTime)
|
||||||
|
|
@ -207,8 +227,10 @@ func TestNewAdjustTimeFail(t *testing.T) {
|
||||||
func TestBalanceAt(t *testing.T) {
|
func TestBalanceAt(t *testing.T) {
|
||||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
expectedBal := big.NewInt(10000000000000000)
|
expectedBal := big.NewInt(10000000000000000)
|
||||||
|
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
bgCtx := context.Background()
|
bgCtx := context.Background()
|
||||||
|
|
||||||
bal, err := sim.BalanceAt(bgCtx, testAddr, nil)
|
bal, err := sim.BalanceAt(bgCtx, testAddr, nil)
|
||||||
|
|
@ -226,12 +248,14 @@ func TestBlockByHash(t *testing.T) {
|
||||||
core.GenesisAlloc{}, 10000000,
|
core.GenesisAlloc{}, 10000000,
|
||||||
)
|
)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
bgCtx := context.Background()
|
bgCtx := context.Background()
|
||||||
|
|
||||||
block, err := sim.BlockByNumber(bgCtx, nil)
|
block, err := sim.BlockByNumber(bgCtx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get recent block: %v", err)
|
t.Errorf("could not get recent block: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
blockByHash, err := sim.BlockByHash(bgCtx, block.Hash())
|
blockByHash, err := sim.BlockByHash(bgCtx, block.Hash())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get recent block: %v", err)
|
t.Errorf("could not get recent block: %v", err)
|
||||||
|
|
@ -247,12 +271,14 @@ func TestBlockByNumber(t *testing.T) {
|
||||||
core.GenesisAlloc{}, 10000000,
|
core.GenesisAlloc{}, 10000000,
|
||||||
)
|
)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
bgCtx := context.Background()
|
bgCtx := context.Background()
|
||||||
|
|
||||||
block, err := sim.BlockByNumber(bgCtx, nil)
|
block, err := sim.BlockByNumber(bgCtx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get recent block: %v", err)
|
t.Errorf("could not get recent block: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if block.NumberU64() != 0 {
|
if block.NumberU64() != 0 {
|
||||||
t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64())
|
t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64())
|
||||||
}
|
}
|
||||||
|
|
@ -264,6 +290,7 @@ func TestBlockByNumber(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get recent block: %v", err)
|
t.Errorf("could not get recent block: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if block.NumberU64() != 1 {
|
if block.NumberU64() != 1 {
|
||||||
t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64())
|
t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64())
|
||||||
}
|
}
|
||||||
|
|
@ -272,6 +299,7 @@ func TestBlockByNumber(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get block by number: %v", err)
|
t.Errorf("could not get block by number: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if blockByNumber.Hash() != block.Hash() {
|
if blockByNumber.Hash() != block.Hash() {
|
||||||
t.Errorf("did not get the same block with height of 1 as before")
|
t.Errorf("did not get the same block with height of 1 as before")
|
||||||
}
|
}
|
||||||
|
|
@ -282,6 +310,7 @@ func TestNonceAt(t *testing.T) {
|
||||||
|
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
bgCtx := context.Background()
|
bgCtx := context.Background()
|
||||||
|
|
||||||
nonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(0))
|
nonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(0))
|
||||||
|
|
@ -298,6 +327,7 @@ func TestNonceAt(t *testing.T) {
|
||||||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||||
|
|
||||||
tx := types.NewTransaction(nonce, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
tx := types.NewTransaction(nonce, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||||
|
|
||||||
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not sign tx: %v", err)
|
t.Errorf("could not sign tx: %v", err)
|
||||||
|
|
@ -308,6 +338,7 @@ func TestNonceAt(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not add tx to pending block: %v", err)
|
t.Errorf("could not add tx to pending block: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
|
|
||||||
newNonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(1))
|
newNonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(1))
|
||||||
|
|
@ -325,6 +356,7 @@ func TestNonceAt(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("could not get nonce for test addr: %v", err)
|
t.Fatalf("could not get nonce for test addr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if newNonce != nonce+uint64(1) {
|
if newNonce != nonce+uint64(1) {
|
||||||
t.Fatalf("received incorrect nonce. expected 1, got %v", nonce)
|
t.Fatalf("received incorrect nonce. expected 1, got %v", nonce)
|
||||||
}
|
}
|
||||||
|
|
@ -335,6 +367,7 @@ func TestSendTransaction(t *testing.T) {
|
||||||
|
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
bgCtx := context.Background()
|
bgCtx := context.Background()
|
||||||
|
|
||||||
// create a signed transaction to send
|
// create a signed transaction to send
|
||||||
|
|
@ -342,6 +375,7 @@ func TestSendTransaction(t *testing.T) {
|
||||||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||||
|
|
||||||
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||||
|
|
||||||
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not sign tx: %v", err)
|
t.Errorf("could not sign tx: %v", err)
|
||||||
|
|
@ -352,6 +386,7 @@ func TestSendTransaction(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not add tx to pending block: %v", err)
|
t.Errorf("could not add tx to pending block: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
|
|
||||||
block, err := sim.BlockByNumber(bgCtx, big.NewInt(1))
|
block, err := sim.BlockByNumber(bgCtx, big.NewInt(1))
|
||||||
|
|
@ -373,6 +408,7 @@ func TestTransactionByHash(t *testing.T) {
|
||||||
}, 10000000,
|
}, 10000000,
|
||||||
)
|
)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
bgCtx := context.Background()
|
bgCtx := context.Background()
|
||||||
|
|
||||||
// create a signed transaction to send
|
// create a signed transaction to send
|
||||||
|
|
@ -380,6 +416,7 @@ func TestTransactionByHash(t *testing.T) {
|
||||||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||||
|
|
||||||
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||||
|
|
||||||
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not sign tx: %v", err)
|
t.Errorf("could not sign tx: %v", err)
|
||||||
|
|
@ -396,9 +433,11 @@ func TestTransactionByHash(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err)
|
t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !pending {
|
if !pending {
|
||||||
t.Errorf("expected transaction to be in pending state")
|
t.Errorf("expected transaction to be in pending state")
|
||||||
}
|
}
|
||||||
|
|
||||||
if receivedTx.Hash() != signedTx.Hash() {
|
if receivedTx.Hash() != signedTx.Hash() {
|
||||||
t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash())
|
t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash())
|
||||||
}
|
}
|
||||||
|
|
@ -410,9 +449,11 @@ func TestTransactionByHash(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err)
|
t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if pending {
|
if pending {
|
||||||
t.Errorf("expected transaction to not be in pending state")
|
t.Errorf("expected transaction to not be in pending state")
|
||||||
}
|
}
|
||||||
|
|
||||||
if receivedTx.Hash() != signedTx.Hash() {
|
if receivedTx.Hash() != signedTx.Hash() {
|
||||||
t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash())
|
t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash())
|
||||||
}
|
}
|
||||||
|
|
@ -427,8 +468,10 @@ func TestEstimateGas(t *testing.T) {
|
||||||
function OOG() public { for (uint i = 0; ; i++) {}}
|
function OOG() public { for (uint i = 0; ; i++) {}}
|
||||||
function Assert() public { assert(false);}
|
function Assert() public { assert(false);}
|
||||||
function Valid() public {}
|
function Valid() public {}
|
||||||
}*/
|
}
|
||||||
|
*/
|
||||||
const contractAbi = "[{\"inputs\":[],\"name\":\"Assert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OOG\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PureRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Revert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Valid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
|
const contractAbi = "[{\"inputs\":[],\"name\":\"Assert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"OOG\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"PureRevert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Revert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"Valid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"
|
||||||
|
|
||||||
const contractBin = "0x60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040523480156100115760006000fd5b506004361061005c5760003560e01c806350f6fe3414610062578063aa8b1d301461006c578063b9b046f914610076578063d8b9839114610080578063e09fface1461008a5761005c565b60006000fd5b61006a610094565b005b6100746100ad565b005b61007e6100b5565b005b6100886100c2565b005b610092610135565b005b6000600090505b5b808060010191505061009b565b505b565b60006000fd5b565b600015156100bf57fe5b5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081526020015060200191505060405180910390fd5b565b5b56fea2646970667358221220345bbcbb1a5ecf22b53a78eaebf95f8ee0eceff6d10d4b9643495084d2ec934a64736f6c63430006040033"
|
const contractBin = "0x60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040523480156100115760006000fd5b506004361061005c5760003560e01c806350f6fe3414610062578063aa8b1d301461006c578063b9b046f914610076578063d8b9839114610080578063e09fface1461008a5761005c565b60006000fd5b61006a610094565b005b6100746100ad565b005b61007e6100b5565b005b6100886100c2565b005b610092610135565b005b6000600090505b5b808060010191505061009b565b505b565b60006000fd5b565b600015156100bf57fe5b5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081526020015060200191505060405180910390fd5b565b5b56fea2646970667358221220345bbcbb1a5ecf22b53a78eaebf95f8ee0eceff6d10d4b9643495084d2ec934a64736f6c63430006040033"
|
||||||
|
|
||||||
key, _ := crypto.GenerateKey()
|
key, _ := crypto.GenerateKey()
|
||||||
|
|
@ -512,15 +555,18 @@ func TestEstimateGas(t *testing.T) {
|
||||||
Data: common.Hex2Bytes("e09fface"),
|
Data: common.Hex2Bytes("e09fface"),
|
||||||
}, 21275, nil, nil},
|
}, 21275, nil, nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
got, err := sim.EstimateGas(context.Background(), c.message)
|
got, err := sim.EstimateGas(context.Background(), c.message)
|
||||||
if c.expectError != nil {
|
if c.expectError != nil {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("Expect error, got nil")
|
t.Fatalf("Expect error, got nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.expectError.Error() != err.Error() {
|
if c.expectError.Error() != err.Error() {
|
||||||
t.Fatalf("Expect error, want %v, got %v", c.expectError, err)
|
t.Fatalf("Expect error, want %v, got %v", c.expectError, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.expectData != nil {
|
if c.expectData != nil {
|
||||||
if err, ok := err.(*revertError); !ok {
|
if err, ok := err.(*revertError); !ok {
|
||||||
t.Fatalf("Expect revert error, got %T", err)
|
t.Fatalf("Expect revert error, got %T", err)
|
||||||
|
|
@ -528,8 +574,10 @@ func TestEstimateGas(t *testing.T) {
|
||||||
t.Fatalf("Error data mismatch, want %v, got %v", c.expectData, err.ErrorData())
|
t.Fatalf("Error data mismatch, want %v, got %v", c.expectData, err.ErrorData())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if got != c.expect {
|
if got != c.expect {
|
||||||
t.Fatalf("Gas estimation mismatch, want %d, got %d", c.expect, got)
|
t.Fatalf("Gas estimation mismatch, want %d, got %d", c.expect, got)
|
||||||
}
|
}
|
||||||
|
|
@ -544,6 +592,7 @@ func TestEstimateGasWithPrice(t *testing.T) {
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
recipient := common.HexToAddress("deadbeef")
|
recipient := common.HexToAddress("deadbeef")
|
||||||
|
|
||||||
var cases = []struct {
|
var cases = []struct {
|
||||||
name string
|
name string
|
||||||
message ethereum.CallMsg
|
message ethereum.CallMsg
|
||||||
|
|
@ -606,20 +655,25 @@ func TestEstimateGasWithPrice(t *testing.T) {
|
||||||
Data: nil,
|
Data: nil,
|
||||||
}, params.TxGas, errors.New("gas required exceeds allowance (20999)")}, // 20999=(2.2ether-0.1ether-1wei)/(1e14)
|
}, params.TxGas, errors.New("gas required exceeds allowance (20999)")}, // 20999=(2.2ether-0.1ether-1wei)/(1e14)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, c := range cases {
|
for i, c := range cases {
|
||||||
got, err := sim.EstimateGas(context.Background(), c.message)
|
got, err := sim.EstimateGas(context.Background(), c.message)
|
||||||
if c.expectError != nil {
|
if c.expectError != nil {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("test %d: expect error, got nil", i)
|
t.Fatalf("test %d: expect error, got nil", i)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.expectError.Error() != err.Error() {
|
if c.expectError.Error() != err.Error() {
|
||||||
t.Fatalf("test %d: expect error, want %v, got %v", i, c.expectError, err)
|
t.Fatalf("test %d: expect error, want %v, got %v", i, c.expectError, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.expectError == nil && err != nil {
|
if c.expectError == nil && err != nil {
|
||||||
t.Fatalf("test %d: didn't expect error, got %v", i, err)
|
t.Fatalf("test %d: didn't expect error, got %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if got != c.expect {
|
if got != c.expect {
|
||||||
t.Fatalf("test %d: gas estimation mismatch, want %d, got %d", i, c.expect, got)
|
t.Fatalf("test %d: gas estimation mismatch, want %d, got %d", i, c.expect, got)
|
||||||
}
|
}
|
||||||
|
|
@ -631,12 +685,14 @@ func TestHeaderByHash(t *testing.T) {
|
||||||
|
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
bgCtx := context.Background()
|
bgCtx := context.Background()
|
||||||
|
|
||||||
header, err := sim.HeaderByNumber(bgCtx, nil)
|
header, err := sim.HeaderByNumber(bgCtx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get recent block: %v", err)
|
t.Errorf("could not get recent block: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
headerByHash, err := sim.HeaderByHash(bgCtx, header.Hash())
|
headerByHash, err := sim.HeaderByHash(bgCtx, header.Hash())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get recent block: %v", err)
|
t.Errorf("could not get recent block: %v", err)
|
||||||
|
|
@ -652,16 +708,17 @@ func TestHeaderByNumber(t *testing.T) {
|
||||||
|
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
bgCtx := context.Background()
|
bgCtx := context.Background()
|
||||||
|
|
||||||
latestBlockHeader, err := sim.HeaderByNumber(bgCtx, nil)
|
latestBlockHeader, err := sim.HeaderByNumber(bgCtx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get header for tip of chain: %v", err)
|
t.Errorf("could not get header for tip of chain: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if latestBlockHeader == nil {
|
if latestBlockHeader == nil {
|
||||||
t.Errorf("received a nil block header")
|
t.Errorf("received a nil block header")
|
||||||
}
|
} else if latestBlockHeader.Number.Uint64() != uint64(0) {
|
||||||
if latestBlockHeader.Number.Uint64() != uint64(0) {
|
|
||||||
t.Errorf("expected block header number 0, instead got %v", latestBlockHeader.Number.Uint64())
|
t.Errorf("expected block header number 0, instead got %v", latestBlockHeader.Number.Uint64())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -680,6 +737,7 @@ func TestHeaderByNumber(t *testing.T) {
|
||||||
if blockHeader.Hash() != latestBlockHeader.Hash() {
|
if blockHeader.Hash() != latestBlockHeader.Hash() {
|
||||||
t.Errorf("block header and latest block header are not the same")
|
t.Errorf("block header and latest block header are not the same")
|
||||||
}
|
}
|
||||||
|
|
||||||
if blockHeader.Number.Int64() != int64(1) {
|
if blockHeader.Number.Int64() != int64(1) {
|
||||||
t.Errorf("did not get blockheader for block 1. instead got block %v", blockHeader.Number.Int64())
|
t.Errorf("did not get blockheader for block 1. instead got block %v", blockHeader.Number.Int64())
|
||||||
}
|
}
|
||||||
|
|
@ -699,7 +757,9 @@ func TestTransactionCount(t *testing.T) {
|
||||||
|
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
bgCtx := context.Background()
|
bgCtx := context.Background()
|
||||||
|
|
||||||
currentBlock, err := sim.BlockByNumber(bgCtx, nil)
|
currentBlock, err := sim.BlockByNumber(bgCtx, nil)
|
||||||
if err != nil || currentBlock == nil {
|
if err != nil || currentBlock == nil {
|
||||||
t.Error("could not get current block")
|
t.Error("could not get current block")
|
||||||
|
|
@ -718,6 +778,7 @@ func TestTransactionCount(t *testing.T) {
|
||||||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||||
|
|
||||||
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||||
|
|
||||||
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not sign tx: %v", err)
|
t.Errorf("could not sign tx: %v", err)
|
||||||
|
|
@ -751,12 +812,14 @@ func TestTransactionInBlock(t *testing.T) {
|
||||||
|
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
bgCtx := context.Background()
|
bgCtx := context.Background()
|
||||||
|
|
||||||
transaction, err := sim.TransactionInBlock(bgCtx, sim.pendingBlock.Hash(), uint(0))
|
transaction, err := sim.TransactionInBlock(bgCtx, sim.pendingBlock.Hash(), uint(0))
|
||||||
if err == nil && err != errTransactionDoesNotExist {
|
if err == nil && err != errTransactionDoesNotExist {
|
||||||
t.Errorf("expected a transaction does not exist error to be received but received %v", err)
|
t.Errorf("expected a transaction does not exist error to be received but received %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if transaction != nil {
|
if transaction != nil {
|
||||||
t.Errorf("expected transaction to be nil but received %v", transaction)
|
t.Errorf("expected transaction to be nil but received %v", transaction)
|
||||||
}
|
}
|
||||||
|
|
@ -775,6 +838,7 @@ func TestTransactionInBlock(t *testing.T) {
|
||||||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||||
|
|
||||||
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||||
|
|
||||||
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not sign tx: %v", err)
|
t.Errorf("could not sign tx: %v", err)
|
||||||
|
|
@ -797,6 +861,7 @@ func TestTransactionInBlock(t *testing.T) {
|
||||||
if err == nil && err != errTransactionDoesNotExist {
|
if err == nil && err != errTransactionDoesNotExist {
|
||||||
t.Errorf("expected a transaction does not exist error to be received but received %v", err)
|
t.Errorf("expected a transaction does not exist error to be received but received %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if transaction != nil {
|
if transaction != nil {
|
||||||
t.Errorf("expected transaction to be nil but received %v", transaction)
|
t.Errorf("expected transaction to be nil but received %v", transaction)
|
||||||
}
|
}
|
||||||
|
|
@ -816,6 +881,7 @@ func TestPendingNonceAt(t *testing.T) {
|
||||||
|
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
bgCtx := context.Background()
|
bgCtx := context.Background()
|
||||||
|
|
||||||
// expect pending nonce to be 0 since account has not been used
|
// expect pending nonce to be 0 since account has not been used
|
||||||
|
|
@ -833,6 +899,7 @@ func TestPendingNonceAt(t *testing.T) {
|
||||||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||||
|
|
||||||
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||||
|
|
||||||
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not sign tx: %v", err)
|
t.Errorf("could not sign tx: %v", err)
|
||||||
|
|
@ -856,10 +923,12 @@ func TestPendingNonceAt(t *testing.T) {
|
||||||
|
|
||||||
// make a new transaction with a nonce of 1
|
// make a new transaction with a nonce of 1
|
||||||
tx = types.NewTransaction(uint64(1), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
tx = types.NewTransaction(uint64(1), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||||
|
|
||||||
signedTx, err = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
signedTx, err = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not sign tx: %v", err)
|
t.Errorf("could not sign tx: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = sim.SendTransaction(bgCtx, signedTx)
|
err = sim.SendTransaction(bgCtx, signedTx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not send tx: %v", err)
|
t.Errorf("could not send tx: %v", err)
|
||||||
|
|
@ -881,6 +950,7 @@ func TestTransactionReceipt(t *testing.T) {
|
||||||
|
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
bgCtx := context.Background()
|
bgCtx := context.Background()
|
||||||
|
|
||||||
// create a signed transaction to send
|
// create a signed transaction to send
|
||||||
|
|
@ -888,6 +958,7 @@ func TestTransactionReceipt(t *testing.T) {
|
||||||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||||
|
|
||||||
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||||
|
|
||||||
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not sign tx: %v", err)
|
t.Errorf("could not sign tx: %v", err)
|
||||||
|
|
@ -898,6 +969,7 @@ func TestTransactionReceipt(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not add tx to pending block: %v", err)
|
t.Errorf("could not add tx to pending block: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
|
|
||||||
receipt, err := sim.TransactionReceipt(bgCtx, signedTx.Hash())
|
receipt, err := sim.TransactionReceipt(bgCtx, signedTx.Hash())
|
||||||
|
|
@ -916,11 +988,14 @@ func TestSuggestGasPrice(t *testing.T) {
|
||||||
10000000,
|
10000000,
|
||||||
)
|
)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
bgCtx := context.Background()
|
bgCtx := context.Background()
|
||||||
|
|
||||||
gasPrice, err := sim.SuggestGasPrice(bgCtx)
|
gasPrice, err := sim.SuggestGasPrice(bgCtx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get gas price: %v", err)
|
t.Errorf("could not get gas price: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if gasPrice.Uint64() != sim.pendingBlock.Header().BaseFee.Uint64() {
|
if gasPrice.Uint64() != sim.pendingBlock.Header().BaseFee.Uint64() {
|
||||||
t.Errorf("gas price was not expected value of %v. actual: %v", sim.pendingBlock.Header().BaseFee.Uint64(), gasPrice.Uint64())
|
t.Errorf("gas price was not expected value of %v. actual: %v", sim.pendingBlock.Header().BaseFee.Uint64(), gasPrice.Uint64())
|
||||||
}
|
}
|
||||||
|
|
@ -928,13 +1003,17 @@ func TestSuggestGasPrice(t *testing.T) {
|
||||||
|
|
||||||
func TestPendingCodeAt(t *testing.T) {
|
func TestPendingCodeAt(t *testing.T) {
|
||||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
|
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
bgCtx := context.Background()
|
bgCtx := context.Background()
|
||||||
|
|
||||||
code, err := sim.CodeAt(bgCtx, testAddr, nil)
|
code, err := sim.CodeAt(bgCtx, testAddr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get code at test addr: %v", err)
|
t.Errorf("could not get code at test addr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(code) != 0 {
|
if len(code) != 0 {
|
||||||
t.Errorf("got code for account that does not have contract code")
|
t.Errorf("got code for account that does not have contract code")
|
||||||
}
|
}
|
||||||
|
|
@ -943,7 +1022,9 @@ func TestPendingCodeAt(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get code at test addr: %v", err)
|
t.Errorf("could not get code at test addr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
|
||||||
|
|
||||||
contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
|
contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
|
t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
|
||||||
|
|
@ -953,6 +1034,7 @@ func TestPendingCodeAt(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get code at test addr: %v", err)
|
t.Errorf("could not get code at test addr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(code) == 0 {
|
if len(code) == 0 {
|
||||||
t.Errorf("did not get code for account that has contract code")
|
t.Errorf("did not get code for account that has contract code")
|
||||||
}
|
}
|
||||||
|
|
@ -964,13 +1046,17 @@ func TestPendingCodeAt(t *testing.T) {
|
||||||
|
|
||||||
func TestCodeAt(t *testing.T) {
|
func TestCodeAt(t *testing.T) {
|
||||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
|
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
bgCtx := context.Background()
|
bgCtx := context.Background()
|
||||||
|
|
||||||
code, err := sim.CodeAt(bgCtx, testAddr, nil)
|
code, err := sim.CodeAt(bgCtx, testAddr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get code at test addr: %v", err)
|
t.Errorf("could not get code at test addr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(code) != 0 {
|
if len(code) != 0 {
|
||||||
t.Errorf("got code for account that does not have contract code")
|
t.Errorf("got code for account that does not have contract code")
|
||||||
}
|
}
|
||||||
|
|
@ -979,17 +1065,21 @@ func TestCodeAt(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get code at test addr: %v", err)
|
t.Errorf("could not get code at test addr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
|
||||||
|
|
||||||
contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
|
contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
|
t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
|
||||||
}
|
}
|
||||||
|
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
|
|
||||||
code, err = sim.CodeAt(bgCtx, contractAddr, nil)
|
code, err = sim.CodeAt(bgCtx, contractAddr, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get code at test addr: %v", err)
|
t.Errorf("could not get code at test addr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(code) == 0 {
|
if len(code) == 0 {
|
||||||
t.Errorf("did not get code for account that has contract code")
|
t.Errorf("did not get code for account that has contract code")
|
||||||
}
|
}
|
||||||
|
|
@ -1000,18 +1090,23 @@ func TestCodeAt(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt:
|
// When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt:
|
||||||
|
//
|
||||||
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
|
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
|
||||||
func TestPendingAndCallContract(t *testing.T) {
|
func TestPendingAndCallContract(t *testing.T) {
|
||||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
|
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
bgCtx := context.Background()
|
bgCtx := context.Background()
|
||||||
|
|
||||||
parsed, err := abi.JSON(strings.NewReader(abiJSON))
|
parsed, err := abi.JSON(strings.NewReader(abiJSON))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get code at test addr: %v", err)
|
t.Errorf("could not get code at test addr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
|
contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
|
||||||
|
|
||||||
addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(abiBin), sim)
|
addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(abiBin), sim)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not deploy contract: %v", err)
|
t.Errorf("could not deploy contract: %v", err)
|
||||||
|
|
@ -1031,6 +1126,7 @@ func TestPendingAndCallContract(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not call receive method on contract: %v", err)
|
t.Errorf("could not call receive method on contract: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(res) == 0 {
|
if len(res) == 0 {
|
||||||
t.Errorf("result of contract call was empty: %v", res)
|
t.Errorf("result of contract call was empty: %v", res)
|
||||||
}
|
}
|
||||||
|
|
@ -1051,6 +1147,7 @@ func TestPendingAndCallContract(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not call receive method on contract: %v", err)
|
t.Errorf("could not call receive method on contract: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(res) == 0 {
|
if len(res) == 0 {
|
||||||
t.Errorf("result of contract call was empty: %v", res)
|
t.Errorf("result of contract call was empty: %v", res)
|
||||||
}
|
}
|
||||||
|
|
@ -1087,8 +1184,10 @@ contract Reverter {
|
||||||
}*/
|
}*/
|
||||||
func TestCallContractRevert(t *testing.T) {
|
func TestCallContractRevert(t *testing.T) {
|
||||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
|
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
|
|
||||||
bgCtx := context.Background()
|
bgCtx := context.Background()
|
||||||
|
|
||||||
reverterABI := `[{"inputs": [],"name": "noRevert","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertASM","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertNoString","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertString","outputs": [],"stateMutability": "pure","type": "function"}]`
|
reverterABI := `[{"inputs": [],"name": "noRevert","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertASM","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertNoString","outputs": [],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "revertString","outputs": [],"stateMutability": "pure","type": "function"}]`
|
||||||
|
|
@ -1098,7 +1197,9 @@ func TestCallContractRevert(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not get code at test addr: %v", err)
|
t.Errorf("could not get code at test addr: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
|
contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
|
||||||
|
|
||||||
addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(reverterBin), sim)
|
addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(reverterBin), sim)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not deploy contract: %v", err)
|
t.Errorf("could not deploy contract: %v", err)
|
||||||
|
|
@ -1137,14 +1238,17 @@ func TestCallContractRevert(t *testing.T) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("call to %v was not reverted", key)
|
t.Errorf("call to %v was not reverted", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
if res != nil {
|
if res != nil {
|
||||||
t.Errorf("result from %v was not nil: %v", key, res)
|
t.Errorf("result from %v was not nil: %v", key, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
if val != nil {
|
if val != nil {
|
||||||
rerr, ok := err.(*revertError)
|
rerr, ok := err.(*revertError)
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Errorf("expect revert error")
|
t.Errorf("expect revert error")
|
||||||
}
|
}
|
||||||
|
|
||||||
if rerr.Error() != "execution reverted: "+val.(string) {
|
if rerr.Error() != "execution reverted: "+val.(string) {
|
||||||
t.Errorf("error was malformed: got %v want %v", rerr.Error(), val)
|
t.Errorf("error was malformed: got %v want %v", rerr.Error(), val)
|
||||||
}
|
}
|
||||||
|
|
@ -1155,17 +1259,21 @@ func TestCallContractRevert(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input, err := parsed.Pack("noRevert")
|
input, err := parsed.Pack("noRevert")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("could not pack noRevert function on contract: %v", err)
|
t.Errorf("could not pack noRevert function on contract: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := cl(input)
|
res, err := cl(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error("call to noRevert was reverted")
|
t.Error("call to noRevert was reverted")
|
||||||
}
|
}
|
||||||
|
|
||||||
if res == nil {
|
if res == nil {
|
||||||
t.Errorf("result from noRevert was nil")
|
t.Errorf("result from noRevert was nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1182,6 +1290,7 @@ func TestCallContractRevert(t *testing.T) {
|
||||||
// having a chain length of just n+1 means that a reorg occurred.
|
// having a chain length of just n+1 means that a reorg occurred.
|
||||||
func TestFork(t *testing.T) {
|
func TestFork(t *testing.T) {
|
||||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
|
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
// 1.
|
// 1.
|
||||||
|
|
@ -1192,7 +1301,7 @@ func TestFork(t *testing.T) {
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
}
|
}
|
||||||
// 3.
|
// 3.
|
||||||
if sim.blockchain.CurrentBlock().NumberU64() != uint64(n) {
|
if sim.blockchain.CurrentBlock().Number.Uint64() != uint64(n) {
|
||||||
t.Error("wrong chain length")
|
t.Error("wrong chain length")
|
||||||
}
|
}
|
||||||
// 4.
|
// 4.
|
||||||
|
|
@ -1202,7 +1311,7 @@ func TestFork(t *testing.T) {
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
}
|
}
|
||||||
// 6.
|
// 6.
|
||||||
if sim.blockchain.CurrentBlock().NumberU64() != uint64(n+1) {
|
if sim.blockchain.CurrentBlock().Number.Uint64() != uint64(n+1) {
|
||||||
t.Error("wrong chain length")
|
t.Error("wrong chain length")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1235,15 +1344,18 @@ const callableBin = "6080604052348015600f57600080fd5b5060998061001e6000396000f3f
|
||||||
// 10. Check that the event was reborn.
|
// 10. Check that the event was reborn.
|
||||||
func TestForkLogsReborn(t *testing.T) {
|
func TestForkLogsReborn(t *testing.T) {
|
||||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
|
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
// 1.
|
// 1.
|
||||||
parsed, _ := abi.JSON(strings.NewReader(callableAbi))
|
parsed, _ := abi.JSON(strings.NewReader(callableAbi))
|
||||||
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
|
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
|
||||||
|
|
||||||
_, _, contract, err := bind.DeployContract(auth, parsed, common.FromHex(callableBin), sim)
|
_, _, contract, err := bind.DeployContract(auth, parsed, common.FromHex(callableBin), sim)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("deploying contract: %v", err)
|
t.Errorf("deploying contract: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
// 2.
|
// 2.
|
||||||
logs, sub, err := contract.WatchLogs(nil, "Called")
|
logs, sub, err := contract.WatchLogs(nil, "Called")
|
||||||
|
|
@ -1258,12 +1370,14 @@ func TestForkLogsReborn(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("transacting: %v", err)
|
t.Errorf("transacting: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
// 5.
|
// 5.
|
||||||
log := <-logs
|
log := <-logs
|
||||||
if log.TxHash != tx.Hash() {
|
if log.TxHash != tx.Hash() {
|
||||||
t.Error("wrong event tx hash")
|
t.Error("wrong event tx hash")
|
||||||
}
|
}
|
||||||
|
|
||||||
if log.Removed {
|
if log.Removed {
|
||||||
t.Error("Event should be included")
|
t.Error("Event should be included")
|
||||||
}
|
}
|
||||||
|
|
@ -1279,6 +1393,7 @@ func TestForkLogsReborn(t *testing.T) {
|
||||||
if log.TxHash != tx.Hash() {
|
if log.TxHash != tx.Hash() {
|
||||||
t.Error("wrong event tx hash")
|
t.Error("wrong event tx hash")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !log.Removed {
|
if !log.Removed {
|
||||||
t.Error("Event should be removed")
|
t.Error("Event should be removed")
|
||||||
}
|
}
|
||||||
|
|
@ -1286,12 +1401,14 @@ func TestForkLogsReborn(t *testing.T) {
|
||||||
if err := sim.SendTransaction(context.Background(), tx); err != nil {
|
if err := sim.SendTransaction(context.Background(), tx); err != nil {
|
||||||
t.Errorf("sending transaction: %v", err)
|
t.Errorf("sending transaction: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
// 10.
|
// 10.
|
||||||
log = <-logs
|
log = <-logs
|
||||||
if log.TxHash != tx.Hash() {
|
if log.TxHash != tx.Hash() {
|
||||||
t.Error("wrong event tx hash")
|
t.Error("wrong event tx hash")
|
||||||
}
|
}
|
||||||
|
|
||||||
if log.Removed {
|
if log.Removed {
|
||||||
t.Error("Event should be included")
|
t.Error("Event should be included")
|
||||||
}
|
}
|
||||||
|
|
@ -1308,6 +1425,7 @@ func TestForkLogsReborn(t *testing.T) {
|
||||||
// 6. Check that the TX is now included in block 2.
|
// 6. Check that the TX is now included in block 2.
|
||||||
func TestForkResendTx(t *testing.T) {
|
func TestForkResendTx(t *testing.T) {
|
||||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
|
|
||||||
sim := simTestBackend(testAddr)
|
sim := simTestBackend(testAddr)
|
||||||
defer sim.Close()
|
defer sim.Close()
|
||||||
// 1.
|
// 1.
|
||||||
|
|
@ -1331,9 +1449,11 @@ func TestForkResendTx(t *testing.T) {
|
||||||
}
|
}
|
||||||
// 5.
|
// 5.
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
|
|
||||||
if err := sim.SendTransaction(context.Background(), tx); err != nil {
|
if err := sim.SendTransaction(context.Background(), tx); err != nil {
|
||||||
t.Errorf("sending transaction: %v", err)
|
t.Errorf("sending transaction: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
sim.Commit()
|
sim.Commit()
|
||||||
// 6.
|
// 6.
|
||||||
receipt, _ = sim.TransactionReceipt(context.Background(), tx.Hash())
|
receipt, _ = sim.TransactionReceipt(context.Background(), tx.Hash())
|
||||||
|
|
@ -1341,3 +1461,69 @@ func TestForkResendTx(t *testing.T) {
|
||||||
t.Errorf("TX included in wrong block: %d", h)
|
t.Errorf("TX included in wrong block: %d", h)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCommitReturnValue(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
|
sim := simTestBackend(testAddr)
|
||||||
|
|
||||||
|
defer sim.Close()
|
||||||
|
|
||||||
|
startBlockHeight := sim.blockchain.CurrentBlock().Number.Uint64()
|
||||||
|
|
||||||
|
// Test if Commit returns the correct block hash
|
||||||
|
h1 := sim.Commit()
|
||||||
|
if h1 != sim.blockchain.CurrentBlock().Hash() {
|
||||||
|
t.Error("Commit did not return the hash of the last block.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a block in the original chain (containing a transaction to force different block hashes)
|
||||||
|
head, _ := sim.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
|
||||||
|
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||||
|
_tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||||
|
tx, _ := types.SignTx(_tx, types.HomesteadSigner{}, testKey)
|
||||||
|
_ = sim.SendTransaction(context.Background(), tx)
|
||||||
|
h2 := sim.Commit()
|
||||||
|
|
||||||
|
// Create another block in the original chain
|
||||||
|
sim.Commit()
|
||||||
|
|
||||||
|
// Fork at the first bock
|
||||||
|
if err := sim.Fork(context.Background(), h1); err != nil {
|
||||||
|
t.Errorf("forking: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if Commit returns the correct block hash after the reorg
|
||||||
|
h2fork := sim.Commit()
|
||||||
|
if h2 == h2fork {
|
||||||
|
t.Error("The block in the fork and the original block are the same block!")
|
||||||
|
}
|
||||||
|
|
||||||
|
if sim.blockchain.GetHeader(h2fork, startBlockHeight+2) == nil {
|
||||||
|
t.Error("Could not retrieve the just created block (side-chain)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAdjustTimeAfterFork ensures that after a fork, AdjustTime uses the pending fork
|
||||||
|
// block's parent rather than the canonical head's parent.
|
||||||
|
func TestAdjustTimeAfterFork(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||||
|
sim := simTestBackend(testAddr)
|
||||||
|
|
||||||
|
defer sim.Close()
|
||||||
|
|
||||||
|
sim.Commit() // h1
|
||||||
|
h1 := sim.blockchain.CurrentHeader().Hash()
|
||||||
|
sim.Commit() // h2
|
||||||
|
_ = sim.Fork(context.Background(), h1)
|
||||||
|
_ = sim.AdjustTime(1 * time.Second)
|
||||||
|
sim.Commit()
|
||||||
|
|
||||||
|
head := sim.blockchain.CurrentHeader()
|
||||||
|
if head.Number == common.Big2 && head.ParentHash != h1 {
|
||||||
|
t.Errorf("failed to build block on fork")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,13 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const basefeeWiggleMultiplier = 2
|
||||||
|
|
||||||
|
var (
|
||||||
|
errNoEventSignature = errors.New("no event signature")
|
||||||
|
errEventSignatureMismatch = errors.New("event signature mismatch")
|
||||||
|
)
|
||||||
|
|
||||||
// SignerFn is a signer function callback when a contract requires a method to
|
// SignerFn is a signer function callback when a contract requires a method to
|
||||||
// sign the transaction before submission.
|
// sign the transaction before submission.
|
||||||
type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, error)
|
type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, error)
|
||||||
|
|
@ -90,14 +97,17 @@ type MetaData struct {
|
||||||
func (m *MetaData) GetAbi() (*abi.ABI, error) {
|
func (m *MetaData) GetAbi() (*abi.ABI, error) {
|
||||||
m.mu.Lock()
|
m.mu.Lock()
|
||||||
defer m.mu.Unlock()
|
defer m.mu.Unlock()
|
||||||
|
|
||||||
if m.ab != nil {
|
if m.ab != nil {
|
||||||
return m.ab, nil
|
return m.ab, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if parsed, err := abi.JSON(strings.NewReader(m.ABI)); err != nil {
|
if parsed, err := abi.JSON(strings.NewReader(m.ABI)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
m.ab = &parsed
|
m.ab = &parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.ab, nil
|
return m.ab, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,11 +144,14 @@ func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend Co
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, nil, nil, err
|
return common.Address{}, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tx, err := c.transact(opts, nil, append(bytecode, input...))
|
tx, err := c.transact(opts, nil, append(bytecode, input...))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, nil, nil, err
|
return common.Address{}, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.address = crypto.CreateAddress(opts.From, tx.Nonce())
|
c.address = crypto.CreateAddress(opts.From, tx.Nonce())
|
||||||
|
|
||||||
return c.address, tx, c, nil
|
return c.address, tx, c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,6 +164,7 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = new(CallOpts)
|
opts = new(CallOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
if results == nil {
|
if results == nil {
|
||||||
results = new([]interface{})
|
results = new([]interface{})
|
||||||
}
|
}
|
||||||
|
|
@ -159,19 +173,26 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input}
|
msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input}
|
||||||
ctx = ensureContext(opts.Context)
|
ctx = ensureContext(opts.Context)
|
||||||
code []byte
|
code []byte
|
||||||
output []byte
|
output []byte
|
||||||
)
|
)
|
||||||
|
|
||||||
if opts.Pending {
|
if opts.Pending {
|
||||||
pb, ok := c.caller.(PendingContractCaller)
|
pb, ok := c.caller.(PendingContractCaller)
|
||||||
if !ok {
|
if !ok {
|
||||||
return ErrNoPendingState
|
return ErrNoPendingState
|
||||||
}
|
}
|
||||||
|
|
||||||
output, err = pb.PendingCallContract(ctx, msg)
|
output, err = pb.PendingCallContract(ctx, msg)
|
||||||
if err == nil && len(output) == 0 {
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(output) == 0 {
|
||||||
// Make sure we have a contract to operate on, and bail out otherwise.
|
// Make sure we have a contract to operate on, and bail out otherwise.
|
||||||
if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
|
if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -184,6 +205,7 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(output) == 0 {
|
if len(output) == 0 {
|
||||||
// Make sure we have a contract to operate on, and bail out otherwise.
|
// Make sure we have a contract to operate on, and bail out otherwise.
|
||||||
if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil {
|
if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil {
|
||||||
|
|
@ -197,9 +219,12 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
|
||||||
if len(*results) == 0 {
|
if len(*results) == 0 {
|
||||||
res, err := c.abi.Unpack(method, output)
|
res, err := c.abi.Unpack(method, output)
|
||||||
*results = res
|
*results = res
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
res := *results
|
res := *results
|
||||||
|
|
||||||
return c.abi.UnpackIntoInterface(res[0], method, output)
|
return c.abi.UnpackIntoInterface(res[0], method, output)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -244,6 +269,7 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
gasTipCap = tip
|
gasTipCap = tip
|
||||||
}
|
}
|
||||||
// Estimate FeeCap
|
// Estimate FeeCap
|
||||||
|
|
@ -251,16 +277,19 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add
|
||||||
if gasFeeCap == nil {
|
if gasFeeCap == nil {
|
||||||
gasFeeCap = new(big.Int).Add(
|
gasFeeCap = new(big.Int).Add(
|
||||||
gasTipCap,
|
gasTipCap,
|
||||||
new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
|
new(big.Int).Mul(head.BaseFee, big.NewInt(basefeeWiggleMultiplier)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if gasFeeCap.Cmp(gasTipCap) < 0 {
|
if gasFeeCap.Cmp(gasTipCap) < 0 {
|
||||||
return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap)
|
return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap)
|
||||||
}
|
}
|
||||||
// Estimate GasLimit
|
// Estimate GasLimit
|
||||||
gasLimit := opts.GasLimit
|
gasLimit := opts.GasLimit
|
||||||
|
|
||||||
if opts.GasLimit == 0 {
|
if opts.GasLimit == 0 {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
gasLimit, err = c.estimateGasLimit(opts, contract, input, nil, gasTipCap, gasFeeCap, value)
|
gasLimit, err = c.estimateGasLimit(opts, contract, input, nil, gasTipCap, gasFeeCap, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -271,6 +300,7 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
baseTx := &types.DynamicFeeTx{
|
baseTx := &types.DynamicFeeTx{
|
||||||
To: contract,
|
To: contract,
|
||||||
Nonce: nonce,
|
Nonce: nonce,
|
||||||
|
|
@ -280,6 +310,7 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add
|
||||||
Value: value,
|
Value: value,
|
||||||
Data: input,
|
Data: input,
|
||||||
}
|
}
|
||||||
|
|
||||||
return types.NewTx(baseTx), nil
|
return types.NewTx(baseTx), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -299,12 +330,15 @@ func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Addr
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
gasPrice = price
|
gasPrice = price
|
||||||
}
|
}
|
||||||
// Estimate GasLimit
|
// Estimate GasLimit
|
||||||
gasLimit := opts.GasLimit
|
gasLimit := opts.GasLimit
|
||||||
|
|
||||||
if opts.GasLimit == 0 {
|
if opts.GasLimit == 0 {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, nil, nil, value)
|
gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, nil, nil, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -315,6 +349,7 @@ func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Addr
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
baseTx := &types.LegacyTx{
|
baseTx := &types.LegacyTx{
|
||||||
To: contract,
|
To: contract,
|
||||||
Nonce: nonce,
|
Nonce: nonce,
|
||||||
|
|
@ -323,6 +358,7 @@ func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Addr
|
||||||
Value: value,
|
Value: value,
|
||||||
Data: input,
|
Data: input,
|
||||||
}
|
}
|
||||||
|
|
||||||
return types.NewTx(baseTx), nil
|
return types.NewTx(baseTx), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -335,6 +371,7 @@ func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Ad
|
||||||
return 0, ErrNoCode
|
return 0, ErrNoCode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := ethereum.CallMsg{
|
msg := ethereum.CallMsg{
|
||||||
From: opts.From,
|
From: opts.From,
|
||||||
To: contract,
|
To: contract,
|
||||||
|
|
@ -344,6 +381,7 @@ func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Ad
|
||||||
Value: value,
|
Value: value,
|
||||||
Data: input,
|
Data: input,
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.transactor.EstimateGas(ensureContext(opts.Context), msg)
|
return c.transactor.EstimateGas(ensureContext(opts.Context), msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -366,8 +404,11 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
||||||
rawTx *types.Transaction
|
rawTx *types.Transaction
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if opts.GasPrice != nil {
|
if opts.GasPrice != nil {
|
||||||
rawTx, err = c.createLegacyTx(opts, contract, input)
|
rawTx, err = c.createLegacyTx(opts, contract, input)
|
||||||
|
} else if opts.GasFeeCap != nil && opts.GasTipCap != nil {
|
||||||
|
rawTx, err = c.createDynamicTx(opts, contract, input, nil)
|
||||||
} else {
|
} else {
|
||||||
// Only query for basefee if gasPrice not specified
|
// Only query for basefee if gasPrice not specified
|
||||||
if head, errHead := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil); errHead != nil {
|
if head, errHead := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil); errHead != nil {
|
||||||
|
|
@ -379,6 +420,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
||||||
rawTx, err = c.createLegacyTx(opts, contract, input)
|
rawTx, err = c.createLegacyTx(opts, contract, input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -386,16 +428,20 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
||||||
if opts.Signer == nil {
|
if opts.Signer == nil {
|
||||||
return nil, errors.New("no signer to authorize the transaction with")
|
return nil, errors.New("no signer to authorize the transaction with")
|
||||||
}
|
}
|
||||||
|
|
||||||
signedTx, err := opts.Signer(opts.From, rawTx)
|
signedTx, err := opts.Signer(opts.From, rawTx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.NoSend {
|
if opts.NoSend {
|
||||||
return signedTx, nil
|
return signedTx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
|
if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return signedTx, nil
|
return signedTx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -431,6 +477,7 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sub, err := event.NewSubscription(func(quit <-chan struct{}) error {
|
sub, err := event.NewSubscription(func(quit <-chan struct{}) error {
|
||||||
for _, log := range buff {
|
for _, log := range buff {
|
||||||
select {
|
select {
|
||||||
|
|
@ -439,12 +486,14 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}), nil
|
}), nil
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return logs, sub, nil
|
return logs, sub, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -472,48 +521,68 @@ func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]inter
|
||||||
if opts.Start != nil {
|
if opts.Start != nil {
|
||||||
config.FromBlock = new(big.Int).SetUint64(*opts.Start)
|
config.FromBlock = new(big.Int).SetUint64(*opts.Start)
|
||||||
}
|
}
|
||||||
|
|
||||||
sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
|
sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return logs, sub, nil
|
return logs, sub, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnpackLog unpacks a retrieved log into the provided output structure.
|
// UnpackLog unpacks a retrieved log into the provided output structure.
|
||||||
func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error {
|
func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error {
|
||||||
if log.Topics[0] != c.abi.Events[event].ID {
|
// Anonymous events are not supported.
|
||||||
return fmt.Errorf("event signature mismatch")
|
if len(log.Topics) == 0 {
|
||||||
|
return errNoEventSignature
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if log.Topics[0] != c.abi.Events[event].ID {
|
||||||
|
return errEventSignatureMismatch
|
||||||
|
}
|
||||||
|
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil {
|
if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var indexed abi.Arguments
|
var indexed abi.Arguments
|
||||||
|
|
||||||
for _, arg := range c.abi.Events[event].Inputs {
|
for _, arg := range c.abi.Events[event].Inputs {
|
||||||
if arg.Indexed {
|
if arg.Indexed {
|
||||||
indexed = append(indexed, arg)
|
indexed = append(indexed, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return abi.ParseTopics(out, indexed, log.Topics[1:])
|
return abi.ParseTopics(out, indexed, log.Topics[1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnpackLogIntoMap unpacks a retrieved log into the provided map.
|
// UnpackLogIntoMap unpacks a retrieved log into the provided map.
|
||||||
func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log types.Log) error {
|
func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log types.Log) error {
|
||||||
if log.Topics[0] != c.abi.Events[event].ID {
|
// Anonymous events are not supported.
|
||||||
return fmt.Errorf("event signature mismatch")
|
if len(log.Topics) == 0 {
|
||||||
|
return errNoEventSignature
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if log.Topics[0] != c.abi.Events[event].ID {
|
||||||
|
return errEventSignatureMismatch
|
||||||
|
}
|
||||||
|
|
||||||
if len(log.Data) > 0 {
|
if len(log.Data) > 0 {
|
||||||
if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil {
|
if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var indexed abi.Arguments
|
var indexed abi.Arguments
|
||||||
|
|
||||||
for _, arg := range c.abi.Events[event].Inputs {
|
for _, arg := range c.abi.Events[event].Inputs {
|
||||||
if arg.Indexed {
|
if arg.Indexed {
|
||||||
indexed = append(indexed, arg)
|
indexed = append(indexed, arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return abi.ParseTopicsIntoMap(out, indexed, log.Topics[1:])
|
return abi.ParseTopicsIntoMap(out, indexed, log.Topics[1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -523,5 +592,6 @@ func ensureContext(ctx context.Context) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
return context.Background()
|
return context.Background()
|
||||||
}
|
}
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package bind_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -77,32 +78,50 @@ func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transac
|
||||||
type mockCaller struct {
|
type mockCaller struct {
|
||||||
codeAtBlockNumber *big.Int
|
codeAtBlockNumber *big.Int
|
||||||
callContractBlockNumber *big.Int
|
callContractBlockNumber *big.Int
|
||||||
pendingCodeAtCalled bool
|
callContractBytes []byte
|
||||||
pendingCallContractCalled bool
|
callContractErr error
|
||||||
|
codeAtBytes []byte
|
||||||
|
codeAtErr error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
|
func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||||
mc.codeAtBlockNumber = blockNumber
|
mc.codeAtBlockNumber = blockNumber
|
||||||
return []byte{1, 2, 3}, nil
|
return mc.codeAtBytes, mc.codeAtErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *mockCaller) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
func (mc *mockCaller) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
||||||
mc.callContractBlockNumber = blockNumber
|
mc.callContractBlockNumber = blockNumber
|
||||||
return nil, nil
|
return mc.callContractBytes, mc.callContractErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *mockCaller) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
type mockPendingCaller struct {
|
||||||
|
*mockCaller
|
||||||
|
pendingCodeAtBytes []byte
|
||||||
|
pendingCodeAtErr error
|
||||||
|
pendingCodeAtCalled bool
|
||||||
|
pendingCallContractCalled bool
|
||||||
|
pendingCallContractBytes []byte
|
||||||
|
pendingCallContractErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *mockPendingCaller) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
||||||
mc.pendingCodeAtCalled = true
|
mc.pendingCodeAtCalled = true
|
||||||
return nil, nil
|
return mc.pendingCodeAtBytes, mc.pendingCodeAtErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *mockCaller) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
|
func (mc *mockPendingCaller) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
|
||||||
mc.pendingCallContractCalled = true
|
mc.pendingCallContractCalled = true
|
||||||
return nil, nil
|
return mc.pendingCallContractBytes, mc.pendingCallContractErr
|
||||||
}
|
}
|
||||||
func TestPassingBlockNumber(t *testing.T) {
|
|
||||||
|
|
||||||
mc := &mockCaller{}
|
func TestPassingBlockNumber(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
mc := &mockPendingCaller{
|
||||||
|
mockCaller: &mockCaller{
|
||||||
|
codeAtBytes: []byte{1, 2, 3},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
|
bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
|
||||||
Methods: map[string]abi.Method{
|
Methods: map[string]abi.Method{
|
||||||
|
|
@ -169,11 +188,33 @@ func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
|
||||||
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUnpackAnonymousLogIntoMap(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
mockLog := newMockLog(nil, common.HexToHash("0x0"))
|
||||||
|
|
||||||
|
abiString := `[{"anonymous":false,"inputs":[{"indexed":false,"name":"amount","type":"uint256"}],"name":"received","type":"event"}]`
|
||||||
|
parsedAbi, _ := abi.JSON(strings.NewReader(abiString))
|
||||||
|
bc := bind.NewBoundContract(common.HexToAddress("0x0"), parsedAbi, nil, nil, nil)
|
||||||
|
|
||||||
|
var received map[string]interface{}
|
||||||
|
err := bc.UnpackLogIntoMap(received, "received", mockLog)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Error("unpacking anonymous event is not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err.Error() != "no event signature" {
|
||||||
|
t.Errorf("expected error 'no event signature', got '%s'", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
|
func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
|
||||||
sliceBytes, err := rlp.EncodeToBytes([]string{"name1", "name2", "name3", "name4"})
|
sliceBytes, err := rlp.EncodeToBytes([]string{"name1", "name2", "name3", "name4"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := crypto.Keccak256Hash(sliceBytes)
|
hash := crypto.Keccak256Hash(sliceBytes)
|
||||||
topics := []common.Hash{
|
topics := []common.Hash{
|
||||||
crypto.Keccak256Hash([]byte("received(string[],address,uint256,bytes)")),
|
crypto.Keccak256Hash([]byte("received(string[],address,uint256,bytes)")),
|
||||||
|
|
@ -199,6 +240,7 @@ func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := crypto.Keccak256Hash(arrBytes)
|
hash := crypto.Keccak256Hash(arrBytes)
|
||||||
topics := []common.Hash{
|
topics := []common.Hash{
|
||||||
crypto.Keccak256Hash([]byte("received(address[2],address,uint256,bytes)")),
|
crypto.Keccak256Hash([]byte("received(address[2],address,uint256,bytes)")),
|
||||||
|
|
@ -225,7 +267,9 @@ func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
|
||||||
hash := crypto.Keccak256Hash([]byte("mockFunction(address,uint)"))
|
hash := crypto.Keccak256Hash([]byte("mockFunction(address,uint)"))
|
||||||
functionSelector := hash[:4]
|
functionSelector := hash[:4]
|
||||||
functionTyBytes := append(addrBytes, functionSelector...)
|
functionTyBytes := append(addrBytes, functionSelector...)
|
||||||
|
|
||||||
var functionTy [24]byte
|
var functionTy [24]byte
|
||||||
|
|
||||||
copy(functionTy[:], functionTyBytes[0:24])
|
copy(functionTy[:], functionTyBytes[0:24])
|
||||||
topics := []common.Hash{
|
topics := []common.Hash{
|
||||||
crypto.Keccak256Hash([]byte("received(function,address,uint256,bytes)")),
|
crypto.Keccak256Hash([]byte("received(function,address,uint256,bytes)")),
|
||||||
|
|
@ -321,6 +365,7 @@ func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]in
|
||||||
if len(received) != len(expected) {
|
if len(received) != len(expected) {
|
||||||
t.Fatalf("unpacked map length %v not equal expected length of %v", len(received), len(expected))
|
t.Fatalf("unpacked map length %v not equal expected length of %v", len(received), len(expected))
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, elem := range expected {
|
for name, elem := range expected {
|
||||||
if !reflect.DeepEqual(elem, received[name]) {
|
if !reflect.DeepEqual(elem, received[name]) {
|
||||||
t.Errorf("field %v does not match expected, want %v, got %v", name, elem, received[name])
|
t.Errorf("field %v does not match expected, want %v, got %v", name, elem, received[name])
|
||||||
|
|
@ -341,3 +386,150 @@ func newMockLog(topics []common.Hash, txHash common.Hash) types.Log {
|
||||||
Removed: false,
|
Removed: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCall(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var method, methodWithArg = "something", "somethingArrrrg"
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name, method string
|
||||||
|
opts *bind.CallOpts
|
||||||
|
mc bind.ContractCaller
|
||||||
|
results *[]interface{}
|
||||||
|
wantErr bool
|
||||||
|
wantErrExact error
|
||||||
|
}{{
|
||||||
|
name: "ok not pending",
|
||||||
|
mc: &mockCaller{
|
||||||
|
codeAtBytes: []byte{0},
|
||||||
|
},
|
||||||
|
method: method,
|
||||||
|
}, {
|
||||||
|
name: "ok pending",
|
||||||
|
mc: &mockPendingCaller{
|
||||||
|
pendingCodeAtBytes: []byte{0},
|
||||||
|
},
|
||||||
|
opts: &bind.CallOpts{
|
||||||
|
Pending: true,
|
||||||
|
},
|
||||||
|
method: method,
|
||||||
|
}, {
|
||||||
|
name: "pack error, no method",
|
||||||
|
mc: new(mockCaller),
|
||||||
|
method: "else",
|
||||||
|
wantErr: true,
|
||||||
|
}, {
|
||||||
|
name: "interface error, pending but not a PendingContractCaller",
|
||||||
|
mc: new(mockCaller),
|
||||||
|
opts: &bind.CallOpts{
|
||||||
|
Pending: true,
|
||||||
|
},
|
||||||
|
method: method,
|
||||||
|
wantErrExact: bind.ErrNoPendingState,
|
||||||
|
}, {
|
||||||
|
name: "pending call canceled",
|
||||||
|
mc: &mockPendingCaller{
|
||||||
|
pendingCallContractErr: context.DeadlineExceeded,
|
||||||
|
},
|
||||||
|
opts: &bind.CallOpts{
|
||||||
|
Pending: true,
|
||||||
|
},
|
||||||
|
method: method,
|
||||||
|
wantErrExact: context.DeadlineExceeded,
|
||||||
|
}, {
|
||||||
|
name: "pending code at error",
|
||||||
|
mc: &mockPendingCaller{
|
||||||
|
pendingCodeAtErr: errors.New(""),
|
||||||
|
},
|
||||||
|
opts: &bind.CallOpts{
|
||||||
|
Pending: true,
|
||||||
|
},
|
||||||
|
method: method,
|
||||||
|
wantErr: true,
|
||||||
|
}, {
|
||||||
|
name: "no pending code at",
|
||||||
|
mc: new(mockPendingCaller),
|
||||||
|
opts: &bind.CallOpts{
|
||||||
|
Pending: true,
|
||||||
|
},
|
||||||
|
method: method,
|
||||||
|
wantErrExact: bind.ErrNoCode,
|
||||||
|
}, {
|
||||||
|
name: "call contract error",
|
||||||
|
mc: &mockCaller{
|
||||||
|
callContractErr: context.DeadlineExceeded,
|
||||||
|
},
|
||||||
|
method: method,
|
||||||
|
wantErrExact: context.DeadlineExceeded,
|
||||||
|
}, {
|
||||||
|
name: "code at error",
|
||||||
|
mc: &mockCaller{
|
||||||
|
codeAtErr: errors.New(""),
|
||||||
|
},
|
||||||
|
method: method,
|
||||||
|
wantErr: true,
|
||||||
|
}, {
|
||||||
|
name: "no code at",
|
||||||
|
mc: new(mockCaller),
|
||||||
|
method: method,
|
||||||
|
wantErrExact: bind.ErrNoCode,
|
||||||
|
}, {
|
||||||
|
name: "unpack error missing arg",
|
||||||
|
mc: &mockCaller{
|
||||||
|
codeAtBytes: []byte{0},
|
||||||
|
},
|
||||||
|
method: methodWithArg,
|
||||||
|
wantErr: true,
|
||||||
|
}, {
|
||||||
|
name: "interface unpack error",
|
||||||
|
mc: &mockCaller{
|
||||||
|
codeAtBytes: []byte{0},
|
||||||
|
},
|
||||||
|
method: method,
|
||||||
|
results: &[]interface{}{0},
|
||||||
|
wantErr: true,
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
|
||||||
|
Methods: map[string]abi.Method{
|
||||||
|
method: {
|
||||||
|
Name: method,
|
||||||
|
Outputs: abi.Arguments{},
|
||||||
|
},
|
||||||
|
methodWithArg: {
|
||||||
|
Name: methodWithArg,
|
||||||
|
Outputs: abi.Arguments{abi.Argument{}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, test.mc, nil, nil)
|
||||||
|
err := bc.Call(test.opts, test.results, test.method)
|
||||||
|
|
||||||
|
if test.wantErr || test.wantErrExact != nil {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("%q expected error", test.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if test.wantErrExact != nil && !errors.Is(err, test.wantErrExact) {
|
||||||
|
t.Fatalf("%q expected error %q but got %q", test.name, test.wantErrExact, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%q unexpected error: %v", test.name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCrashers contains some strings which previously caused the abi codec to crash.
|
||||||
|
func TestCrashers(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
_, _ = abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"_1"}]}]}]`))
|
||||||
|
_, _ = abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"&"}]}]}]`))
|
||||||
|
_, _ = abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"----"}]}]}]`))
|
||||||
|
_, _ = abi.JSON(strings.NewReader(`[{"inputs":[{"type":"tuple[]","components":[{"type":"bool","name":"foo.Bar"}]}]}]`))
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ package bind
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/format"
|
"go/format"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
@ -39,10 +38,45 @@ type Lang int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LangGo Lang = iota
|
LangGo Lang = iota
|
||||||
LangJava
|
|
||||||
LangObjC
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func isKeyWord(arg string) bool {
|
||||||
|
switch arg {
|
||||||
|
case "break":
|
||||||
|
case "case":
|
||||||
|
case "chan":
|
||||||
|
case "const":
|
||||||
|
case "continue":
|
||||||
|
case "default":
|
||||||
|
case "defer":
|
||||||
|
case "else":
|
||||||
|
case "fallthrough":
|
||||||
|
case "for":
|
||||||
|
case "func":
|
||||||
|
case "go":
|
||||||
|
case "goto":
|
||||||
|
case "if":
|
||||||
|
case "import":
|
||||||
|
case "interface":
|
||||||
|
case "iota":
|
||||||
|
case "map":
|
||||||
|
case "make":
|
||||||
|
case "new":
|
||||||
|
case "package":
|
||||||
|
case "range":
|
||||||
|
case "return":
|
||||||
|
case "select":
|
||||||
|
case "struct":
|
||||||
|
case "switch":
|
||||||
|
case "type":
|
||||||
|
case "var":
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
|
// Bind generates a Go wrapper around a contract ABI. This wrapper isn't meant
|
||||||
// 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
|
||||||
|
|
@ -58,6 +92,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
// isLib is the map used to flag each encountered library as such
|
// isLib is the map used to flag each encountered library as such
|
||||||
isLib = make(map[string]struct{})
|
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]))
|
||||||
|
|
@ -69,6 +104,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
if unicode.IsSpace(r) {
|
if unicode.IsSpace(r) {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
return r
|
return r
|
||||||
}, abis[i])
|
}, abis[i])
|
||||||
|
|
||||||
|
|
@ -99,32 +135,41 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
// Normalize the method for capital cases and non-anonymous inputs/outputs
|
// Normalize the method for capital cases and non-anonymous inputs/outputs
|
||||||
normalized := original
|
normalized := original
|
||||||
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
|
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
|
||||||
|
|
||||||
// Ensure there is no duplicated identifier
|
// Ensure there is no duplicated identifier
|
||||||
var identifiers = callIdentifiers
|
var identifiers = callIdentifiers
|
||||||
if !original.IsConstant() {
|
if !original.IsConstant() {
|
||||||
identifiers = transactIdentifiers
|
identifiers = transactIdentifiers
|
||||||
}
|
}
|
||||||
|
|
||||||
if identifiers[normalizedName] {
|
if identifiers[normalizedName] {
|
||||||
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
|
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
|
||||||
}
|
}
|
||||||
|
|
||||||
identifiers[normalizedName] = true
|
identifiers[normalizedName] = true
|
||||||
|
|
||||||
normalized.Name = normalizedName
|
normalized.Name = normalizedName
|
||||||
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 {
|
||||||
if input.Name == "" {
|
if input.Name == "" || isKeyWord(input.Name) {
|
||||||
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
|
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasStruct(input.Type) {
|
if hasStruct(input.Type) {
|
||||||
bindStructType[lang](input.Type, structs)
|
bindStructType[lang](input.Type, structs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
normalized.Outputs = make([]abi.Argument, len(original.Outputs))
|
normalized.Outputs = make([]abi.Argument, len(original.Outputs))
|
||||||
copy(normalized.Outputs, original.Outputs)
|
copy(normalized.Outputs, original.Outputs)
|
||||||
|
|
||||||
for j, output := range normalized.Outputs {
|
for j, output := range normalized.Outputs {
|
||||||
if output.Name != "" {
|
if output.Name != "" {
|
||||||
normalized.Outputs[j].Name = capitalise(output.Name)
|
normalized.Outputs[j].Name = capitalise(output.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasStruct(output.Type) {
|
if hasStruct(output.Type) {
|
||||||
bindStructType[lang](output.Type, structs)
|
bindStructType[lang](output.Type, structs)
|
||||||
}
|
}
|
||||||
|
|
@ -136,6 +181,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
|
transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, original := range evmABI.Events {
|
for _, original := range evmABI.Events {
|
||||||
// Skip anonymous events as they don't support explicit filtering
|
// Skip anonymous events as they don't support explicit filtering
|
||||||
if original.Anonymous {
|
if original.Anonymous {
|
||||||
|
|
@ -149,15 +195,29 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
if eventIdentifiers[normalizedName] {
|
if eventIdentifiers[normalizedName] {
|
||||||
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
|
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
|
||||||
}
|
}
|
||||||
|
|
||||||
eventIdentifiers[normalizedName] = true
|
eventIdentifiers[normalizedName] = true
|
||||||
normalized.Name = normalizedName
|
normalized.Name = normalizedName
|
||||||
|
|
||||||
|
used := make(map[string]bool)
|
||||||
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 {
|
||||||
if input.Name == "" {
|
if input.Name == "" || isKeyWord(input.Name) {
|
||||||
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
|
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
|
||||||
}
|
}
|
||||||
|
// Event is a bit special, we need to define event struct in binding,
|
||||||
|
// ensure there is no camel-case-style name conflict.
|
||||||
|
for index := 0; ; index++ {
|
||||||
|
if !used[capitalise(normalized.Inputs[j].Name)] {
|
||||||
|
used[capitalise(normalized.Inputs[j].Name)] = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
normalized.Inputs[j].Name = fmt.Sprintf("%s%d", normalized.Inputs[j].Name, index)
|
||||||
|
}
|
||||||
|
|
||||||
if hasStruct(input.Type) {
|
if hasStruct(input.Type) {
|
||||||
bindStructType[lang](input.Type, structs)
|
bindStructType[lang](input.Type, structs)
|
||||||
}
|
}
|
||||||
|
|
@ -169,17 +229,14 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
if evmABI.HasFallback() {
|
if evmABI.HasFallback() {
|
||||||
fallback = &tmplMethod{Original: evmABI.Fallback}
|
fallback = &tmplMethod{Original: evmABI.Fallback}
|
||||||
}
|
}
|
||||||
|
|
||||||
if evmABI.HasReceive() {
|
if evmABI.HasReceive() {
|
||||||
receive = &tmplMethod{Original: evmABI.Receive}
|
receive = &tmplMethod{Original: evmABI.Receive}
|
||||||
}
|
}
|
||||||
// There is no easy way to pass arbitrary java objects to the Go side.
|
|
||||||
if len(structs) > 0 && lang == LangJava {
|
|
||||||
return "", errors.New("java binding for tuple arguments is not supported yet")
|
|
||||||
}
|
|
||||||
|
|
||||||
contracts[types[i]] = &tmplContract{
|
contracts[types[i]] = &tmplContract{
|
||||||
Type: capitalise(types[i]),
|
Type: capitalise(types[i]),
|
||||||
InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1),
|
InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""),
|
||||||
InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
|
InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
|
||||||
Constructor: evmABI.Constructor,
|
Constructor: evmABI.Constructor,
|
||||||
Calls: calls,
|
Calls: calls,
|
||||||
|
|
@ -200,6 +257,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err)
|
log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if matched {
|
if matched {
|
||||||
contracts[types[i]].Libraries[pattern] = name
|
contracts[types[i]].Libraries[pattern] = name
|
||||||
// keep track that this type is a library
|
// keep track that this type is a library
|
||||||
|
|
@ -230,6 +288,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
"capitalise": capitalise,
|
"capitalise": capitalise,
|
||||||
"decapitalise": decapitalise,
|
"decapitalise": decapitalise,
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
|
tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
|
||||||
if err := tmpl.Execute(buffer, data); err != nil {
|
if err := tmpl.Execute(buffer, data); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
@ -240,6 +299,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("%v\n%s", err, buffer)
|
return "", fmt.Errorf("%v\n%s", err, buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
return string(code), nil
|
return string(code), nil
|
||||||
}
|
}
|
||||||
// For all others just return as is for now
|
// For all others just return as is for now
|
||||||
|
|
@ -250,7 +310,6 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
||||||
// programming language types.
|
// programming language types.
|
||||||
var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
|
var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
|
||||||
LangGo: bindTypeGo,
|
LangGo: bindTypeGo,
|
||||||
LangJava: bindTypeJava,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go ones.
|
// bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go ones.
|
||||||
|
|
@ -264,6 +323,7 @@ func bindBasicTypeGo(kind abi.Type) string {
|
||||||
case "8", "16", "32", "64":
|
case "8", "16", "32", "64":
|
||||||
return fmt.Sprintf("%sint%s", parts[1], parts[2])
|
return fmt.Sprintf("%sint%s", parts[1], parts[2])
|
||||||
}
|
}
|
||||||
|
|
||||||
return "*big.Int"
|
return "*big.Int"
|
||||||
case abi.FixedBytesTy:
|
case abi.FixedBytesTy:
|
||||||
return fmt.Sprintf("[%d]byte", kind.Size)
|
return fmt.Sprintf("[%d]byte", kind.Size)
|
||||||
|
|
@ -293,86 +353,10 @@ func bindTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// bindBasicTypeJava converts basic solidity types(except array, slice and tuple) to Java ones.
|
|
||||||
func bindBasicTypeJava(kind abi.Type) string {
|
|
||||||
switch kind.T {
|
|
||||||
case abi.AddressTy:
|
|
||||||
return "Address"
|
|
||||||
case abi.IntTy, abi.UintTy:
|
|
||||||
// Note that uint and int (without digits) are also matched,
|
|
||||||
// these are size 256, and will translate to BigInt (the default).
|
|
||||||
parts := regexp.MustCompile(`(u)?int([0-9]*)`).FindStringSubmatch(kind.String())
|
|
||||||
if len(parts) != 3 {
|
|
||||||
return kind.String()
|
|
||||||
}
|
|
||||||
// All unsigned integers should be translated to BigInt since gomobile doesn't
|
|
||||||
// support them.
|
|
||||||
if parts[1] == "u" {
|
|
||||||
return "BigInt"
|
|
||||||
}
|
|
||||||
|
|
||||||
namedSize := map[string]string{
|
|
||||||
"8": "byte",
|
|
||||||
"16": "short",
|
|
||||||
"32": "int",
|
|
||||||
"64": "long",
|
|
||||||
}[parts[2]]
|
|
||||||
|
|
||||||
// default to BigInt
|
|
||||||
if namedSize == "" {
|
|
||||||
namedSize = "BigInt"
|
|
||||||
}
|
|
||||||
return namedSize
|
|
||||||
case abi.FixedBytesTy, abi.BytesTy:
|
|
||||||
return "byte[]"
|
|
||||||
case abi.BoolTy:
|
|
||||||
return "boolean"
|
|
||||||
case abi.StringTy:
|
|
||||||
return "String"
|
|
||||||
case abi.FunctionTy:
|
|
||||||
return "byte[24]"
|
|
||||||
default:
|
|
||||||
return kind.String()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pluralizeJavaType explicitly converts multidimensional types to predefined
|
|
||||||
// types in go side.
|
|
||||||
func pluralizeJavaType(typ string) string {
|
|
||||||
switch typ {
|
|
||||||
case "boolean":
|
|
||||||
return "Bools"
|
|
||||||
case "String":
|
|
||||||
return "Strings"
|
|
||||||
case "Address":
|
|
||||||
return "Addresses"
|
|
||||||
case "byte[]":
|
|
||||||
return "Binaries"
|
|
||||||
case "BigInt":
|
|
||||||
return "BigInts"
|
|
||||||
}
|
|
||||||
return typ + "[]"
|
|
||||||
}
|
|
||||||
|
|
||||||
// bindTypeJava converts a Solidity type to a Java one. Since there is no clear mapping
|
|
||||||
// from all Solidity types to Java ones (e.g. uint17), those that cannot be exactly
|
|
||||||
// mapped will use an upscaled type (e.g. BigDecimal).
|
|
||||||
func bindTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
|
|
||||||
switch kind.T {
|
|
||||||
case abi.TupleTy:
|
|
||||||
return structs[kind.TupleRawName+kind.String()].Name
|
|
||||||
case abi.ArrayTy, abi.SliceTy:
|
|
||||||
return pluralizeJavaType(bindTypeJava(*kind.Elem, structs))
|
|
||||||
default:
|
|
||||||
return bindBasicTypeJava(kind)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// bindTopicType is a set of type binders that convert Solidity types to some
|
// bindTopicType is a set of type binders that convert Solidity types to some
|
||||||
// supported programming language topic types.
|
// supported programming language topic types.
|
||||||
var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
|
var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
|
||||||
LangGo: bindTopicTypeGo,
|
LangGo: bindTopicTypeGo,
|
||||||
LangJava: bindTopicTypeJava,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same
|
// bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same
|
||||||
|
|
@ -389,23 +373,7 @@ func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
||||||
if bound == "string" || bound == "[]byte" {
|
if bound == "string" || bound == "[]byte" {
|
||||||
bound = "common.Hash"
|
bound = "common.Hash"
|
||||||
}
|
}
|
||||||
return bound
|
|
||||||
}
|
|
||||||
|
|
||||||
// bindTopicTypeJava converts a Solidity topic type to a Java one. It is almost the same
|
|
||||||
// functionality as for simple types, but dynamic types get converted to hashes.
|
|
||||||
func bindTopicTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
|
|
||||||
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[]" {
|
|
||||||
bound = "Hash"
|
|
||||||
}
|
|
||||||
return bound
|
return bound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -413,7 +381,6 @@ func bindTopicTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
|
||||||
// programming language struct definition.
|
// programming language struct definition.
|
||||||
var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
|
var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
|
||||||
LangGo: bindStructTypeGo,
|
LangGo: bindStructTypeGo,
|
||||||
LangJava: bindStructTypeJava,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping
|
// bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping
|
||||||
|
|
@ -432,19 +399,32 @@ func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
||||||
if s, exist := structs[id]; exist {
|
if s, exist := structs[id]; exist {
|
||||||
return s.Name
|
return s.Name
|
||||||
}
|
}
|
||||||
var fields []*tmplField
|
|
||||||
|
var (
|
||||||
|
names = make(map[string]bool)
|
||||||
|
fields []*tmplField
|
||||||
|
)
|
||||||
|
|
||||||
for i, elem := range kind.TupleElems {
|
for i, elem := range kind.TupleElems {
|
||||||
field := bindStructTypeGo(*elem, structs)
|
name := capitalise(kind.TupleRawNames[i])
|
||||||
fields = append(fields, &tmplField{Type: field, Name: capitalise(kind.TupleRawNames[i]), SolKind: *elem})
|
name = abi.ResolveNameConflict(name, func(s string) bool { return names[s] })
|
||||||
|
names[name] = true
|
||||||
|
|
||||||
|
fields = append(fields, &tmplField{Type: bindStructTypeGo(*elem, structs), Name: name, SolKind: *elem})
|
||||||
}
|
}
|
||||||
|
|
||||||
name := kind.TupleRawName
|
name := kind.TupleRawName
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = fmt.Sprintf("Struct%d", len(structs))
|
name = fmt.Sprintf("Struct%d", len(structs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name = capitalise(name)
|
||||||
|
|
||||||
structs[id] = &tmplStruct{
|
structs[id] = &tmplStruct{
|
||||||
Name: name,
|
Name: name,
|
||||||
Fields: fields,
|
Fields: fields,
|
||||||
}
|
}
|
||||||
|
|
||||||
return name
|
return name
|
||||||
case abi.ArrayTy:
|
case abi.ArrayTy:
|
||||||
return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs)
|
return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs)
|
||||||
|
|
@ -455,74 +435,10 @@ func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// bindStructTypeJava converts a Solidity tuple type to a Java one and records the mapping
|
|
||||||
// in the given map.
|
|
||||||
// Notably, this function will resolve and record nested struct recursively.
|
|
||||||
func bindStructTypeJava(kind abi.Type, structs map[string]*tmplStruct) string {
|
|
||||||
switch kind.T {
|
|
||||||
case abi.TupleTy:
|
|
||||||
// We compose a raw struct name and a 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
|
|
||||||
}
|
|
||||||
var fields []*tmplField
|
|
||||||
for i, elem := range kind.TupleElems {
|
|
||||||
field := bindStructTypeJava(*elem, structs)
|
|
||||||
fields = append(fields, &tmplField{Type: field, Name: decapitalise(kind.TupleRawNames[i]), SolKind: *elem})
|
|
||||||
}
|
|
||||||
name := kind.TupleRawName
|
|
||||||
if name == "" {
|
|
||||||
name = fmt.Sprintf("Class%d", len(structs))
|
|
||||||
}
|
|
||||||
structs[id] = &tmplStruct{
|
|
||||||
Name: name,
|
|
||||||
Fields: fields,
|
|
||||||
}
|
|
||||||
return name
|
|
||||||
case abi.ArrayTy, abi.SliceTy:
|
|
||||||
return pluralizeJavaType(bindStructTypeJava(*kind.Elem, structs))
|
|
||||||
default:
|
|
||||||
return bindBasicTypeJava(kind)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// namedType is a set of functions that transform language specific types to
|
// namedType is a set of functions that transform language specific types to
|
||||||
// named versions that may be used inside method names.
|
// named versions that may be used inside method names.
|
||||||
var namedType = map[Lang]func(string, abi.Type) string{
|
var namedType = map[Lang]func(string, abi.Type) string{
|
||||||
LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
|
LangGo: func(string, abi.Type) string { panic("this shouldn't be needed") },
|
||||||
LangJava: namedTypeJava,
|
|
||||||
}
|
|
||||||
|
|
||||||
// namedTypeJava converts some primitive data types to named variants that can
|
|
||||||
// be used as parts of method names.
|
|
||||||
func namedTypeJava(javaKind string, solKind abi.Type) string {
|
|
||||||
switch javaKind {
|
|
||||||
case "byte[]":
|
|
||||||
return "Binary"
|
|
||||||
case "boolean":
|
|
||||||
return "Bool"
|
|
||||||
default:
|
|
||||||
parts := regexp.MustCompile(`(u)?int([0-9]*)(\[[0-9]*\])?`).FindStringSubmatch(solKind.String())
|
|
||||||
if len(parts) != 4 {
|
|
||||||
return javaKind
|
|
||||||
}
|
|
||||||
switch parts[2] {
|
|
||||||
case "8", "16", "32", "64":
|
|
||||||
if parts[3] == "" {
|
|
||||||
return capitalise(fmt.Sprintf("%sint%s", parts[1], parts[2]))
|
|
||||||
}
|
|
||||||
return capitalise(fmt.Sprintf("%sint%ss", parts[1], parts[2]))
|
|
||||||
|
|
||||||
default:
|
|
||||||
return javaKind
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// alias returns an alias of the given string based on the aliasing rules
|
// alias returns an alias of the given string based on the aliasing rules
|
||||||
|
|
@ -531,6 +447,7 @@ func alias(aliases map[string]string, n string) string {
|
||||||
if alias, exist := aliases[n]; exist {
|
if alias, exist := aliases[n]; exist {
|
||||||
return alias
|
return alias
|
||||||
}
|
}
|
||||||
|
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -538,7 +455,6 @@ func alias(aliases map[string]string, n string) string {
|
||||||
// conform to target language naming conventions.
|
// conform to target language naming conventions.
|
||||||
var methodNormalizer = map[Lang]func(string) string{
|
var methodNormalizer = map[Lang]func(string) string{
|
||||||
LangGo: abi.ToCamelCase,
|
LangGo: abi.ToCamelCase,
|
||||||
LangJava: decapitalise,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// capitalise makes a camel-case string which starts with an upper case character.
|
// capitalise makes a camel-case string which starts with an upper case character.
|
||||||
|
|
@ -551,6 +467,7 @@ func decapitalise(input string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
goForm := abi.ToCamelCase(input)
|
goForm := abi.ToCamelCase(input)
|
||||||
|
|
||||||
return strings.ToLower(goForm[:1]) + goForm[1:]
|
return strings.ToLower(goForm[:1]) + goForm[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -560,7 +477,9 @@ func structured(args abi.Arguments) bool {
|
||||||
if len(args) < 2 {
|
if len(args) < 2 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
exists := make(map[string]bool)
|
exists := make(map[string]bool)
|
||||||
|
|
||||||
for _, out := range args {
|
for _, out := range args {
|
||||||
// If the name is anonymous, we can't organize into a struct
|
// If the name is anonymous, we can't organize into a struct
|
||||||
if out.Name == "" {
|
if out.Name == "" {
|
||||||
|
|
@ -572,8 +491,10 @@ func structured(args abi.Arguments) bool {
|
||||||
if field == "" || exists[field] {
|
if field == "" || exists[field] {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
exists[field] = true
|
exists[field] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -76,7 +76,6 @@ type tmplStruct struct {
|
||||||
// programming languages the package can generate to.
|
// programming languages the package can generate to.
|
||||||
var tmplSource = map[Lang]string{
|
var tmplSource = map[Lang]string{
|
||||||
LangGo: tmplSourceGo,
|
LangGo: tmplSourceGo,
|
||||||
LangJava: tmplSourceJava,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// tmplSourceGo is the Go source template that the generated Go contract binding
|
// tmplSourceGo is the Go source template that the generated Go contract binding
|
||||||
|
|
@ -110,6 +109,7 @@ var (
|
||||||
_ = common.Big1
|
_ = common.Big1
|
||||||
_ = types.BloomLookup
|
_ = types.BloomLookup
|
||||||
_ = event.NewSubscription
|
_ = event.NewSubscription
|
||||||
|
_ = abi.ConvertType
|
||||||
)
|
)
|
||||||
|
|
||||||
{{$structs := .Structs}}
|
{{$structs := .Structs}}
|
||||||
|
|
@ -161,7 +161,7 @@ var (
|
||||||
}
|
}
|
||||||
{{range $pattern, $name := .Libraries}}
|
{{range $pattern, $name := .Libraries}}
|
||||||
{{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend)
|
{{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend)
|
||||||
{{$contract.Type}}Bin = strings.Replace({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:], -1)
|
{{$contract.Type}}Bin = strings.ReplaceAll({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:])
|
||||||
{{end}}
|
{{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 {
|
||||||
|
|
@ -268,11 +268,11 @@ var (
|
||||||
|
|
||||||
// bind{{.Type}} binds a generic wrapper to an already deployed contract.
|
// bind{{.Type}} binds a generic wrapper to an already deployed contract.
|
||||||
func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
|
func bind{{.Type}}(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
|
||||||
parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI))
|
parsed, err := {{.Type}}MetaData.GetAbi()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil
|
return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call invokes the (constant) contract method with params as input values and
|
// Call invokes the (constant) contract method with params as input values and
|
||||||
|
|
@ -569,140 +569,3 @@ var (
|
||||||
{{end}}
|
{{end}}
|
||||||
{{end}}
|
{{end}}
|
||||||
`
|
`
|
||||||
|
|
||||||
// tmplSourceJava is the Java source template that the generated Java contract binding
|
|
||||||
// is based on.
|
|
||||||
const tmplSourceJava = `
|
|
||||||
// This file is an automatically generated Java binding. Do not modify as any
|
|
||||||
// change will likely be lost upon the next re-generation!
|
|
||||||
|
|
||||||
package {{.Package}};
|
|
||||||
|
|
||||||
import org.ethereum.geth.*;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
{{$structs := .Structs}}
|
|
||||||
{{range $contract := .Contracts}}
|
|
||||||
{{if not .Library}}public {{end}}class {{.Type}} {
|
|
||||||
// ABI is the input ABI used to generate the binding from.
|
|
||||||
public final static String ABI = "{{.InputABI}}";
|
|
||||||
{{if $contract.FuncSigs}}
|
|
||||||
// {{.Type}}FuncSigs maps the 4-byte function signature to its string representation.
|
|
||||||
public final static Map<String, String> {{.Type}}FuncSigs;
|
|
||||||
static {
|
|
||||||
Hashtable<String, String> temp = new Hashtable<String, String>();
|
|
||||||
{{range $strsig, $binsig := .FuncSigs}}temp.put("{{$binsig}}", "{{$strsig}}");
|
|
||||||
{{end}}
|
|
||||||
{{.Type}}FuncSigs = Collections.unmodifiableMap(temp);
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
{{if .InputBin}}
|
|
||||||
// BYTECODE is the compiled bytecode used for deploying new contracts.
|
|
||||||
public final static String BYTECODE = "0x{{.InputBin}}";
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
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}});
|
|
||||||
{{end}}
|
|
||||||
return new {{.Type}}(Geth.deployContract(auth, ABI, Geth.decodeFromHex(bytecode), client, args));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal constructor used by contract deployment.
|
|
||||||
private {{.Type}}(BoundContract deployment) {
|
|
||||||
this.Address = deployment.getAddress();
|
|
||||||
this.Deployer = deployment.getDeployer();
|
|
||||||
this.Contract = deployment;
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
// Ethereum address where this contract is located at.
|
|
||||||
public final Address Address;
|
|
||||||
|
|
||||||
// Ethereum transaction in which this contract was deployed (if known!).
|
|
||||||
public final Transaction Deployer;
|
|
||||||
|
|
||||||
// Contract instance bound to a blockchain address.
|
|
||||||
private final BoundContract Contract;
|
|
||||||
|
|
||||||
// Creates a new instance of {{.Type}}, bound to a specific deployed contract.
|
|
||||||
public {{.Type}}(Address address, EthereumClient client) throws Exception {
|
|
||||||
this(Geth.bindContract(address, ABI, client));
|
|
||||||
}
|
|
||||||
|
|
||||||
{{range .Calls}}
|
|
||||||
{{if gt (len .Normalized.Outputs) 1}}
|
|
||||||
// {{capitalise .Normalized.Name}}Results is the output of a call to {{.Normalized.Name}}.
|
|
||||||
public class {{capitalise .Normalized.Name}}Results {
|
|
||||||
{{range $index, $item := .Normalized.Outputs}}public {{bindtype .Type $structs}} {{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}};
|
|
||||||
{{end}}
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Original.String}}
|
|
||||||
public {{if gt (len .Normalized.Outputs) 1}}{{capitalise .Normalized.Name}}Results{{else if eq (len .Normalized.Outputs) 0}}void{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}}{{end}}{{end}} {{.Normalized.Name}}(CallOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
|
|
||||||
Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
|
|
||||||
{{range $index, $item := .Normalized.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
Interfaces results = Geth.newInterfaces({{(len .Normalized.Outputs)}});
|
|
||||||
{{range $index, $item := .Normalized.Outputs}}Interface result{{$index}} = Geth.newInterface(); result{{$index}}.setDefault{{namedtype (bindtype .Type $structs) .Type}}(); results.set({{$index}}, result{{$index}});
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
if (opts == null) {
|
|
||||||
opts = Geth.newCallOpts();
|
|
||||||
}
|
|
||||||
this.Contract.call(opts, results, "{{.Original.Name}}", args);
|
|
||||||
{{if gt (len .Normalized.Outputs) 1}}
|
|
||||||
{{capitalise .Normalized.Name}}Results result = new {{capitalise .Normalized.Name}}Results();
|
|
||||||
{{range $index, $item := .Normalized.Outputs}}result.{{if ne .Name ""}}{{.Name}}{{else}}Return{{$index}}{{end}} = results.get({{$index}}).get{{namedtype (bindtype .Type $structs) .Type}}();
|
|
||||||
{{end}}
|
|
||||||
return result;
|
|
||||||
{{else}}{{range .Normalized.Outputs}}return results.get(0).get{{namedtype (bindtype .Type $structs) .Type}}();{{end}}
|
|
||||||
{{end}}
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{range .Transacts}}
|
|
||||||
// {{.Normalized.Name}} is a paid mutator transaction binding the contract method 0x{{printf "%x" .Original.ID}}.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Original.String}}
|
|
||||||
public Transaction {{.Normalized.Name}}(TransactOpts opts{{range .Normalized.Inputs}}, {{bindtype .Type $structs}} {{.Name}}{{end}}) throws Exception {
|
|
||||||
Interfaces args = Geth.newInterfaces({{(len .Normalized.Inputs)}});
|
|
||||||
{{range $index, $item := .Normalized.Inputs}}Interface arg{{$index}} = Geth.newInterface();arg{{$index}}.set{{namedtype (bindtype .Type $structs) .Type}}({{.Name}});args.set({{$index}},arg{{$index}});
|
|
||||||
{{end}}
|
|
||||||
return this.Contract.transact(opts, "{{.Original.Name}}" , args);
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{if .Fallback}}
|
|
||||||
// Fallback is a paid mutator transaction binding the contract fallback function.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Fallback.Original.String}}
|
|
||||||
public Transaction Fallback(TransactOpts opts, byte[] calldata) throws Exception {
|
|
||||||
return this.Contract.rawTransact(opts, calldata);
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
|
|
||||||
{{if .Receive}}
|
|
||||||
// Receive is a paid mutator transaction binding the contract receive function.
|
|
||||||
//
|
|
||||||
// Solidity: {{.Receive.Original.String}}
|
|
||||||
public Transaction Receive(TransactOpts opts) throws Exception {
|
|
||||||
return this.Contract.rawTransact(opts, null);
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
}
|
|
||||||
{{end}}
|
|
||||||
`
|
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*ty
|
||||||
defer queryTicker.Stop()
|
defer queryTicker.Stop()
|
||||||
|
|
||||||
logger := log.New("hash", tx.Hash())
|
logger := log.New("hash", tx.Hash())
|
||||||
|
|
||||||
for {
|
for {
|
||||||
receipt, err := b.TransactionReceipt(ctx, tx.Hash())
|
receipt, err := b.TransactionReceipt(ctx, tx.Hash())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
@ -61,10 +62,12 @@ func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (
|
||||||
if tx.To() != nil {
|
if tx.To() != nil {
|
||||||
return common.Address{}, errors.New("tx is not contract creation")
|
return common.Address{}, errors.New("tx is not contract creation")
|
||||||
}
|
}
|
||||||
|
|
||||||
receipt, err := WaitMined(ctx, b, tx)
|
receipt, err := WaitMined(ctx, b, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, err
|
return common.Address{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if receipt.ContractAddress == (common.Address{}) {
|
if receipt.ContractAddress == (common.Address{}) {
|
||||||
return common.Address{}, errors.New("zero address")
|
return common.Address{}, errors.New("zero address")
|
||||||
}
|
}
|
||||||
|
|
@ -75,5 +78,6 @@ func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (
|
||||||
if err == nil && len(code) == 0 {
|
if err == nil && len(code) == 0 {
|
||||||
err = ErrNoCodeAfterDeploy
|
err = ErrNoCodeAfterDeploy
|
||||||
}
|
}
|
||||||
|
|
||||||
return receipt.ContractAddress, err
|
return receipt.ContractAddress, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,8 +76,10 @@ func TestWaitDeployed(t *testing.T) {
|
||||||
mined = make(chan struct{})
|
mined = make(chan struct{})
|
||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
)
|
)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
address, err = bind.WaitDeployed(ctx, backend, tx)
|
address, err = bind.WaitDeployed(ctx, backend, tx)
|
||||||
|
|
||||||
close(mined)
|
close(mined)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -90,6 +92,7 @@ func TestWaitDeployed(t *testing.T) {
|
||||||
if err != test.wantErr {
|
if err != test.wantErr {
|
||||||
t.Errorf("test %q: error mismatch: want %q, got %q", name, test.wantErr, err)
|
t.Errorf("test %q: error mismatch: want %q, got %q", name, test.wantErr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if address != test.wantAddress {
|
if address != test.wantAddress {
|
||||||
t.Errorf("test %q: unexpected contract address %s", name, address.Hex())
|
t.Errorf("test %q: unexpected contract address %s", name, address.Hex())
|
||||||
}
|
}
|
||||||
|
|
@ -115,10 +118,12 @@ func TestWaitDeployedCornerCases(t *testing.T) {
|
||||||
code := "6060604052600a8060106000396000f360606040526008565b00"
|
code := "6060604052600a8060106000396000f360606040526008565b00"
|
||||||
tx := types.NewTransaction(0, common.HexToAddress("0x01"), big.NewInt(0), 3000000, gasPrice, common.FromHex(code))
|
tx := types.NewTransaction(0, common.HexToAddress("0x01"), big.NewInt(0), 3000000, gasPrice, common.FromHex(code))
|
||||||
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
defer cancel()
|
defer cancel()
|
||||||
backend.SendTransaction(ctx, tx)
|
backend.SendTransaction(ctx, tx)
|
||||||
backend.Commit()
|
backend.Commit()
|
||||||
|
|
||||||
notContentCreation := errors.New("tx is not contract creation")
|
notContentCreation := errors.New("tx is not contract creation")
|
||||||
if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != notContentCreation.Error() {
|
if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != notContentCreation.Error() {
|
||||||
t.Errorf("error mismatch: want %q, got %q, ", notContentCreation, err)
|
t.Errorf("error mismatch: want %q, got %q, ", notContentCreation, err)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2021 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
|
||||||
|
|
@ -30,11 +30,13 @@ type Error struct {
|
||||||
Name string
|
Name string
|
||||||
Inputs Arguments
|
Inputs Arguments
|
||||||
str string
|
str string
|
||||||
|
|
||||||
// Sig contains the string signature according to the ABI spec.
|
// Sig contains the string signature according to the ABI spec.
|
||||||
// e.g. event foo(uint32 a, int b) = "foo(uint32,int256)"
|
// e.g. error 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"
|
||||||
Sig string
|
Sig string
|
||||||
// ID returns the canonical representation of the event's signature used by the
|
|
||||||
|
// ID returns the canonical representation of the error's signature used by the
|
||||||
// abi definition to identify event names and types.
|
// abi definition to identify event names and types.
|
||||||
ID common.Hash
|
ID common.Hash
|
||||||
}
|
}
|
||||||
|
|
@ -44,6 +46,7 @@ func NewError(name string, inputs Arguments) Error {
|
||||||
// and precompute string and sig representation.
|
// and precompute string and sig representation.
|
||||||
names := make([]string, len(inputs))
|
names := make([]string, len(inputs))
|
||||||
types := make([]string, len(inputs))
|
types := make([]string, len(inputs))
|
||||||
|
|
||||||
for i, input := range inputs {
|
for i, input := range inputs {
|
||||||
if input.Name == "" {
|
if input.Name == "" {
|
||||||
inputs[i] = Argument{
|
inputs[i] = Argument{
|
||||||
|
|
@ -84,8 +87,10 @@ func (e *Error) Unpack(data []byte) (interface{}, error) {
|
||||||
if len(data) < 4 {
|
if len(data) < 4 {
|
||||||
return "", errors.New("invalid data for unpacking")
|
return "", errors.New("invalid data for unpacking")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(data[:4], e.ID[:4]) {
|
if !bytes.Equal(data[:4], e.ID[:4]) {
|
||||||
return "", errors.New("invalid data for unpacking")
|
return "", errors.New("invalid data for unpacking")
|
||||||
}
|
}
|
||||||
|
|
||||||
return e.Inputs.Unpack(data[4:])
|
return e.Inputs.Unpack(data[4:])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,14 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errBadBool = errors.New("abi: improperly encoded boolean value")
|
errBadBool = errors.New("abi: improperly encoded boolean value")
|
||||||
|
errBadUint8 = errors.New("abi: improperly encoded uint8 value")
|
||||||
|
errBadUint16 = errors.New("abi: improperly encoded uint16 value")
|
||||||
|
errBadUint32 = errors.New("abi: improperly encoded uint32 value")
|
||||||
|
errBadUint64 = errors.New("abi: improperly encoded uint64 value")
|
||||||
|
errBadInt8 = errors.New("abi: improperly encoded int8 value")
|
||||||
|
errBadInt16 = errors.New("abi: improperly encoded int16 value")
|
||||||
|
errBadInt32 = errors.New("abi: improperly encoded int32 value")
|
||||||
|
errBadInt64 = errors.New("abi: improperly encoded int64 value")
|
||||||
)
|
)
|
||||||
|
|
||||||
// formatSliceString formats the reflection kind with the given slice size
|
// formatSliceString formats the reflection kind with the given slice size
|
||||||
|
|
@ -32,6 +40,7 @@ func formatSliceString(kind reflect.Kind, sliceSize int) string {
|
||||||
if sliceSize == -1 {
|
if sliceSize == -1 {
|
||||||
return fmt.Sprintf("[]%v", kind)
|
return fmt.Sprintf("[]%v", kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("[%d]%v", sliceSize, kind)
|
return fmt.Sprintf("[%d]%v", sliceSize, kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,6 +64,7 @@ func sliceTypeCheck(t Type, val reflect.Value) error {
|
||||||
if val.Type().Elem().Kind() != t.Elem.GetType().Kind() {
|
if val.Type().Elem().Kind() != t.Elem.GetType().Kind() {
|
||||||
return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), val.Type())
|
return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), val.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -73,7 +83,6 @@ func typeCheck(t Type, value reflect.Value) error {
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// typeErr returns a formatted type casting error.
|
// typeErr returns a formatted type casting error.
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import (
|
||||||
// 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 is the event name used for internal representation. It's derived from
|
// 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.
|
// the raw name and a suffix will be added in the case of event overloading.
|
||||||
//
|
//
|
||||||
// e.g.
|
// e.g.
|
||||||
// These are two events that have the same name:
|
// These are two events that have the same name:
|
||||||
|
|
@ -38,15 +38,18 @@ type Event struct {
|
||||||
// The event name of the first one will be resolved as foo while the second one
|
// The event name of the first one will be resolved as foo while the second one
|
||||||
// will be resolved as foo0.
|
// will be resolved as foo0.
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
// RawName is the raw event name parsed from ABI.
|
// RawName is the raw event name parsed from ABI.
|
||||||
RawName string
|
RawName string
|
||||||
Anonymous bool
|
Anonymous bool
|
||||||
Inputs Arguments
|
Inputs Arguments
|
||||||
str string
|
str string
|
||||||
|
|
||||||
// Sig contains the string signature according to the ABI spec.
|
// Sig contains the string signature according to the ABI spec.
|
||||||
// e.g. event foo(uint32 a, int b) = "foo(uint32,int256)"
|
// e.g. event 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"
|
||||||
Sig string
|
Sig string
|
||||||
|
|
||||||
// ID returns the canonical representation of the event's signature used by the
|
// ID returns the canonical representation of the event's signature used by the
|
||||||
// abi definition to identify event names and types.
|
// abi definition to identify event names and types.
|
||||||
ID common.Hash
|
ID common.Hash
|
||||||
|
|
@ -61,6 +64,7 @@ func NewEvent(name, rawName string, anonymous bool, inputs Arguments) Event {
|
||||||
// and precompute string and sig representation.
|
// and precompute string and sig representation.
|
||||||
names := make([]string, len(inputs))
|
names := make([]string, len(inputs))
|
||||||
types := make([]string, len(inputs))
|
types := make([]string, len(inputs))
|
||||||
|
|
||||||
for i, input := range inputs {
|
for i, input := range inputs {
|
||||||
if input.Name == "" {
|
if input.Name == "" {
|
||||||
inputs[i] = Argument{
|
inputs[i] = Argument{
|
||||||
|
|
|
||||||
|
|
@ -149,11 +149,14 @@ func TestEventMultiValueWithArrayUnpack(t *testing.T) {
|
||||||
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": false, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
|
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": false, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
|
|
||||||
var i uint8 = 1
|
var i uint8 = 1
|
||||||
for ; i <= 3; i++ {
|
for ; i <= 3; i++ {
|
||||||
b.Write(packNum(reflect.ValueOf(i)))
|
b.Write(packNum(reflect.ValueOf(i)))
|
||||||
}
|
}
|
||||||
|
|
||||||
unpacked, err := abi.Unpack("test", b.Bytes())
|
unpacked, err := abi.Unpack("test", b.Bytes())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, [2]uint8{1, 2}, unpacked[0])
|
require.Equal(t, [2]uint8{1, 2}, unpacked[0])
|
||||||
|
|
@ -161,7 +164,6 @@ func TestEventMultiValueWithArrayUnpack(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEventTupleUnpack(t *testing.T) {
|
func TestEventTupleUnpack(t *testing.T) {
|
||||||
|
|
||||||
type EventTransfer struct {
|
type EventTransfer struct {
|
||||||
Value *big.Int
|
Value *big.Int
|
||||||
}
|
}
|
||||||
|
|
@ -210,6 +212,7 @@ func TestEventTupleUnpack(t *testing.T) {
|
||||||
bigintExpected2 := big.NewInt(2218516807680)
|
bigintExpected2 := big.NewInt(2218516807680)
|
||||||
bigintExpected3 := big.NewInt(1000001)
|
bigintExpected3 := big.NewInt(1000001)
|
||||||
addr := common.HexToAddress("0x00Ce0d46d924CC8437c806721496599FC3FFA268")
|
addr := common.HexToAddress("0x00Ce0d46d924CC8437c806721496599FC3FFA268")
|
||||||
|
|
||||||
var testCases = []struct {
|
var testCases = []struct {
|
||||||
data string
|
data string
|
||||||
dest interface{}
|
dest interface{}
|
||||||
|
|
@ -344,24 +347,33 @@ func TestEventTupleUnpack(t *testing.T) {
|
||||||
func unpackTestEventData(dest interface{}, hexData string, jsonEvent []byte, assert *assert.Assertions) error {
|
func unpackTestEventData(dest interface{}, hexData string, jsonEvent []byte, assert *assert.Assertions) error {
|
||||||
data, err := hex.DecodeString(hexData)
|
data, err := hex.DecodeString(hexData)
|
||||||
assert.NoError(err, "Hex data should be a correct hex-string")
|
assert.NoError(err, "Hex data should be a correct hex-string")
|
||||||
|
|
||||||
var e Event
|
var e Event
|
||||||
|
|
||||||
assert.NoError(json.Unmarshal(jsonEvent, &e), "Should be able to unmarshal event ABI")
|
assert.NoError(json.Unmarshal(jsonEvent, &e), "Should be able to unmarshal event ABI")
|
||||||
a := ABI{Events: map[string]Event{"e": e}}
|
a := ABI{Events: map[string]Event{"e": e}}
|
||||||
|
|
||||||
return a.UnpackIntoInterface(dest, "e", data)
|
return a.UnpackIntoInterface(dest, "e", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder.
|
// TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder.
|
||||||
func TestEventUnpackIndexed(t *testing.T) {
|
func TestEventUnpackIndexed(t *testing.T) {
|
||||||
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
|
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
|
||||||
|
|
||||||
type testStruct struct {
|
type testStruct struct {
|
||||||
Value1 uint8 // indexed
|
Value1 uint8 // indexed
|
||||||
Value2 uint8
|
Value2 uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
|
|
||||||
b.Write(packNum(reflect.ValueOf(uint8(8))))
|
b.Write(packNum(reflect.ValueOf(uint8(8))))
|
||||||
|
|
||||||
var rst testStruct
|
var rst testStruct
|
||||||
|
|
||||||
require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes()))
|
require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes()))
|
||||||
require.Equal(t, uint8(0), rst.Value1)
|
require.Equal(t, uint8(0), rst.Value1)
|
||||||
require.Equal(t, uint8(8), rst.Value2)
|
require.Equal(t, uint8(8), rst.Value2)
|
||||||
|
|
@ -370,13 +382,17 @@ func TestEventUnpackIndexed(t *testing.T) {
|
||||||
// TestEventIndexedWithArrayUnpack verifies that decoder will not overflow when static array is indexed input.
|
// TestEventIndexedWithArrayUnpack verifies that decoder will not overflow when static array is indexed input.
|
||||||
func TestEventIndexedWithArrayUnpack(t *testing.T) {
|
func TestEventIndexedWithArrayUnpack(t *testing.T) {
|
||||||
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"string"}]}]`
|
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"string"}]}]`
|
||||||
|
|
||||||
type testStruct struct {
|
type testStruct struct {
|
||||||
Value1 [2]uint8 // indexed
|
Value1 [2]uint8 // indexed
|
||||||
Value2 string
|
Value2 string
|
||||||
}
|
}
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
|
|
||||||
stringOut := "abc"
|
stringOut := "abc"
|
||||||
// number of fields that will be encoded * 32
|
// number of fields that will be encoded * 32
|
||||||
b.Write(packNum(reflect.ValueOf(32)))
|
b.Write(packNum(reflect.ValueOf(32)))
|
||||||
|
|
@ -384,6 +400,7 @@ func TestEventIndexedWithArrayUnpack(t *testing.T) {
|
||||||
b.Write(common.RightPadBytes([]byte(stringOut), 32))
|
b.Write(common.RightPadBytes([]byte(stringOut), 32))
|
||||||
|
|
||||||
var rst testStruct
|
var rst testStruct
|
||||||
|
|
||||||
require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes()))
|
require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes()))
|
||||||
require.Equal(t, [2]uint8{0, 0}, rst.Value1)
|
require.Equal(t, [2]uint8{0, 0}, rst.Value1)
|
||||||
require.Equal(t, stringOut, rst.Value2)
|
require.Equal(t, stringOut, rst.Value2)
|
||||||
|
|
|
||||||
|
|
@ -97,10 +97,12 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
|
||||||
inputNames = make([]string, len(inputs))
|
inputNames = make([]string, len(inputs))
|
||||||
outputNames = make([]string, len(outputs))
|
outputNames = make([]string, len(outputs))
|
||||||
)
|
)
|
||||||
|
|
||||||
for i, input := range inputs {
|
for i, input := range inputs {
|
||||||
inputNames[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
|
inputNames[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
|
||||||
types[i] = input.Type.String()
|
types[i] = input.Type.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, output := range outputs {
|
for i, output := range outputs {
|
||||||
outputNames[i] = output.Type.String()
|
outputNames[i] = output.Type.String()
|
||||||
if len(output.Name) > 0 {
|
if len(output.Name) > 0 {
|
||||||
|
|
@ -113,6 +115,7 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
|
||||||
sig string
|
sig string
|
||||||
id []byte
|
id []byte
|
||||||
)
|
)
|
||||||
|
|
||||||
if funType == Function {
|
if funType == Function {
|
||||||
sig = fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ","))
|
sig = fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ","))
|
||||||
id = crypto.Keccak256([]byte(sig))[:4]
|
id = crypto.Keccak256([]byte(sig))[:4]
|
||||||
|
|
@ -123,9 +126,11 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
|
||||||
if state == "nonpayable" {
|
if state == "nonpayable" {
|
||||||
state = ""
|
state = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if state != "" {
|
if state != "" {
|
||||||
state = state + " "
|
state = state + " "
|
||||||
}
|
}
|
||||||
|
|
||||||
identity := fmt.Sprintf("function %v", rawName)
|
identity := fmt.Sprintf("function %v", rawName)
|
||||||
if funType == Fallback {
|
if funType == Fallback {
|
||||||
identity = "fallback"
|
identity = "fallback"
|
||||||
|
|
@ -134,6 +139,7 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
|
||||||
} else if funType == Constructor {
|
} else if funType == Constructor {
|
||||||
identity = "constructor"
|
identity = "constructor"
|
||||||
}
|
}
|
||||||
|
|
||||||
str := fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputNames, ", "), state, strings.Join(outputNames, ", "))
|
str := fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputNames, ", "), state, strings.Join(outputNames, ", "))
|
||||||
|
|
||||||
return Method{
|
return Method{
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ func TestMethodString(t *testing.T) {
|
||||||
} else {
|
} else {
|
||||||
got = abi.Methods[test.method].String()
|
got = abi.Methods[test.method].String()
|
||||||
}
|
}
|
||||||
|
|
||||||
if got != test.expectation {
|
if got != test.expectation {
|
||||||
t.Errorf("expected string to be %s, got %s", test.expectation, got)
|
t.Errorf("expected string to be %s, got %s", test.expectation, got)
|
||||||
}
|
}
|
||||||
|
|
@ -131,6 +132,7 @@ func TestMethodSig(t *testing.T) {
|
||||||
expect: "complexTuple((uint256,uint256)[5][])",
|
expect: "complexTuple((uint256,uint256)[5][])",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(methoddata))
|
abi, err := JSON(strings.NewReader(methoddata))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -51,19 +51,23 @@ func packElement(t Type, reflectValue reflect.Value) ([]byte, error) {
|
||||||
if reflectValue.Bool() {
|
if reflectValue.Bool() {
|
||||||
return math.PaddedBigBytes(common.Big1, 32), nil
|
return math.PaddedBigBytes(common.Big1, 32), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return math.PaddedBigBytes(common.Big0, 32), nil
|
return math.PaddedBigBytes(common.Big0, 32), nil
|
||||||
case BytesTy:
|
case BytesTy:
|
||||||
if reflectValue.Kind() == reflect.Array {
|
if reflectValue.Kind() == reflect.Array {
|
||||||
reflectValue = mustArrayToByteSlice(reflectValue)
|
reflectValue = mustArrayToByteSlice(reflectValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
if reflectValue.Type() != reflect.TypeOf([]byte{}) {
|
if reflectValue.Type() != reflect.TypeOf([]byte{}) {
|
||||||
return []byte{}, errors.New("Bytes type is neither slice nor array")
|
return []byte{}, errors.New("Bytes type is neither slice nor array")
|
||||||
}
|
}
|
||||||
|
|
||||||
return packBytesSlice(reflectValue.Bytes(), reflectValue.Len()), nil
|
return packBytesSlice(reflectValue.Bytes(), reflectValue.Len()), nil
|
||||||
case FixedBytesTy, FunctionTy:
|
case FixedBytesTy, FunctionTy:
|
||||||
if reflectValue.Kind() == reflect.Array {
|
if reflectValue.Kind() == reflect.Array {
|
||||||
reflectValue = mustArrayToByteSlice(reflectValue)
|
reflectValue = mustArrayToByteSlice(reflectValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return common.RightPadBytes(reflectValue.Bytes(), 32), nil
|
return common.RightPadBytes(reflectValue.Bytes(), 32), nil
|
||||||
default:
|
default:
|
||||||
return []byte{}, fmt.Errorf("Could not pack element, unknown type: %v", t.T)
|
return []byte{}, fmt.Errorf("Could not pack element, unknown type: %v", t.T)
|
||||||
|
|
|
||||||
|
|
@ -38,17 +38,21 @@ func TestPack(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("invalid hex %s: %v", test.packed, err)
|
t.Fatalf("invalid hex %s: %v", test.packed, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
inDef := fmt.Sprintf(`[{ "name" : "method", "type": "function", "inputs": %s}]`, test.def)
|
inDef := fmt.Sprintf(`[{ "name" : "method", "type": "function", "inputs": %s}]`, test.def)
|
||||||
inAbi, err := JSON(strings.NewReader(inDef))
|
inAbi, err := JSON(strings.NewReader(inDef))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("invalid ABI definition %s, %v", inDef, err)
|
t.Fatalf("invalid ABI definition %s, %v", inDef, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var packed []byte
|
var packed []byte
|
||||||
packed, err = inAbi.Pack("method", test.unpacked)
|
packed, err = inAbi.Pack("method", test.unpacked)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("test %d (%v) failed: %v", i, test.def, err)
|
t.Fatalf("test %d (%v) failed: %v", i, test.def, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(packed[4:], encb) {
|
if !reflect.DeepEqual(packed[4:], encb) {
|
||||||
t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, encb, packed[4:])
|
t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, encb, packed[4:])
|
||||||
}
|
}
|
||||||
|
|
@ -76,6 +80,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)...)
|
||||||
|
|
@ -86,11 +91,13 @@ func TestMethodPack(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)...)
|
||||||
|
|
@ -105,6 +112,7 @@ func TestMethodPack(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
@ -132,10 +140,12 @@ func TestMethodPack(t *testing.T) {
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes(addrC[:], 32)...)
|
sig = append(sig, common.LeftPadBytes(addrC[:], 32)...)
|
||||||
sig = append(sig, common.LeftPadBytes(addrD[:], 32)...)
|
sig = append(sig, common.LeftPadBytes(addrD[:], 32)...)
|
||||||
|
|
||||||
packed, err = abi.Pack("nestedArray", a, []common.Address{addrC, addrD})
|
packed, err = abi.Pack("nestedArray", a, []common.Address{addrC, addrD})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
@ -148,10 +158,12 @@ func TestMethodPack(t *testing.T) {
|
||||||
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{1}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
||||||
|
|
||||||
packed, err = abi.Pack("nestedArray2", [2][]uint8{{1}, {1}})
|
packed, err = abi.Pack("nestedArray2", [2][]uint8{{1}, {1}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
@ -167,10 +179,12 @@ func TestMethodPack(t *testing.T) {
|
||||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
sig = append(sig, common.LeftPadBytes([]byte{2}, 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)...)
|
||||||
|
|
||||||
packed, err = abi.Pack("nestedSlice", [][]uint8{{1, 2}, {1, 2}})
|
packed, err = abi.Pack("nestedSlice", [][]uint8{{1, 2}, {1, 2}})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,15 +25,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConvertType converts an interface of a runtime type into a interface of the
|
// ConvertType converts an interface of a runtime type into a interface of the
|
||||||
// given type
|
// given type, e.g. turn this code:
|
||||||
// e.g. turn
|
//
|
||||||
// var fields []reflect.StructField
|
// var fields []reflect.StructField
|
||||||
|
//
|
||||||
// fields = append(fields, reflect.StructField{
|
// fields = append(fields, reflect.StructField{
|
||||||
// Name: "X",
|
// Name: "X",
|
||||||
// Type: reflect.TypeOf(new(big.Int)),
|
// Type: reflect.TypeOf(new(big.Int)),
|
||||||
// Tag: reflect.StructTag("json:\"" + "x" + "\""),
|
// Tag: reflect.StructTag("json:\"" + "x" + "\""),
|
||||||
// }
|
// }
|
||||||
// into
|
//
|
||||||
|
// into:
|
||||||
|
//
|
||||||
// type TupleT struct { X *big.Int }
|
// type TupleT struct { X *big.Int }
|
||||||
func ConvertType(in interface{}, proto interface{}) interface{} {
|
func ConvertType(in interface{}, proto interface{}) interface{} {
|
||||||
protoType := reflect.TypeOf(proto)
|
protoType := reflect.TypeOf(proto)
|
||||||
|
|
@ -44,6 +47,7 @@ func ConvertType(in interface{}, proto interface{}) interface{} {
|
||||||
if err := set(reflect.ValueOf(proto), reflect.ValueOf(in)); err != nil {
|
if err := set(reflect.ValueOf(proto), reflect.ValueOf(in)); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return proto
|
return proto
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,6 +57,7 @@ func indirect(v reflect.Value) reflect.Value {
|
||||||
if v.Kind() == reflect.Ptr && v.Elem().Type() != reflect.TypeOf(big.Int{}) {
|
if v.Kind() == reflect.Ptr && v.Elem().Type() != reflect.TypeOf(big.Int{}) {
|
||||||
return indirect(v.Elem())
|
return indirect(v.Elem())
|
||||||
}
|
}
|
||||||
|
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,6 +76,7 @@ func reflectIntType(unsigned bool, size int) reflect.Type {
|
||||||
return reflect.TypeOf(uint64(0))
|
return reflect.TypeOf(uint64(0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch size {
|
switch size {
|
||||||
case 8:
|
case 8:
|
||||||
return reflect.TypeOf(int8(0))
|
return reflect.TypeOf(int8(0))
|
||||||
|
|
@ -81,6 +87,7 @@ func reflectIntType(unsigned bool, size int) reflect.Type {
|
||||||
case 64:
|
case 64:
|
||||||
return reflect.TypeOf(int64(0))
|
return reflect.TypeOf(int64(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
return reflect.TypeOf(&big.Int{})
|
return reflect.TypeOf(&big.Int{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,6 +96,7 @@ func reflectIntType(unsigned bool, size int) reflect.Type {
|
||||||
func mustArrayToByteSlice(value reflect.Value) reflect.Value {
|
func mustArrayToByteSlice(value reflect.Value) reflect.Value {
|
||||||
slice := reflect.MakeSlice(reflect.TypeOf([]byte{}), value.Len(), value.Len())
|
slice := reflect.MakeSlice(reflect.TypeOf([]byte{}), value.Len(), value.Len())
|
||||||
reflect.Copy(slice, value)
|
reflect.Copy(slice, value)
|
||||||
|
|
||||||
return slice
|
return slice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,8 +106,9 @@ func mustArrayToByteSlice(value reflect.Value) reflect.Value {
|
||||||
// strict ruleset as bare `reflect` does.
|
// strict ruleset as bare `reflect` does.
|
||||||
func set(dst, src reflect.Value) error {
|
func set(dst, src reflect.Value) error {
|
||||||
dstType, srcType := dst.Type(), src.Type()
|
dstType, srcType := dst.Type(), src.Type()
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case dstType.Kind() == reflect.Interface && dst.Elem().IsValid():
|
case dstType.Kind() == reflect.Interface && dst.Elem().IsValid() && (dst.Elem().Type().Kind() == reflect.Ptr || dst.Elem().CanSet()):
|
||||||
return set(dst.Elem(), src)
|
return set(dst.Elem(), src)
|
||||||
case dstType.Kind() == reflect.Ptr && dstType.Elem() != reflect.TypeOf(big.Int{}):
|
case dstType.Kind() == reflect.Ptr && dstType.Elem() != reflect.TypeOf(big.Int{}):
|
||||||
return set(dst.Elem(), src)
|
return set(dst.Elem(), src)
|
||||||
|
|
@ -114,6 +123,7 @@ func set(dst, src reflect.Value) error {
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
|
return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -127,10 +137,12 @@ func setSlice(dst, src reflect.Value) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if dst.CanSet() {
|
if dst.CanSet() {
|
||||||
dst.Set(slice)
|
dst.Set(slice)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.New("Cannot set slice, destination not settable")
|
return errors.New("Cannot set slice, destination not settable")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,20 +150,25 @@ func setArray(dst, src reflect.Value) error {
|
||||||
if src.Kind() == reflect.Ptr {
|
if src.Kind() == reflect.Ptr {
|
||||||
return set(dst, indirect(src))
|
return set(dst, indirect(src))
|
||||||
}
|
}
|
||||||
|
|
||||||
array := reflect.New(dst.Type()).Elem()
|
array := reflect.New(dst.Type()).Elem()
|
||||||
|
|
||||||
min := src.Len()
|
min := src.Len()
|
||||||
if src.Len() > dst.Len() {
|
if src.Len() > dst.Len() {
|
||||||
min = dst.Len()
|
min = dst.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < min; i++ {
|
for i := 0; i < min; i++ {
|
||||||
if err := set(array.Index(i), src.Index(i)); err != nil {
|
if err := set(array.Index(i), src.Index(i)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if dst.CanSet() {
|
if dst.CanSet() {
|
||||||
dst.Set(array)
|
dst.Set(array)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return errors.New("Cannot set array, destination not settable")
|
return errors.New("Cannot set array, destination not settable")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -159,22 +176,27 @@ func setStruct(dst, src reflect.Value) error {
|
||||||
for i := 0; i < src.NumField(); i++ {
|
for i := 0; i < src.NumField(); i++ {
|
||||||
srcField := src.Field(i)
|
srcField := src.Field(i)
|
||||||
dstField := dst.Field(i)
|
dstField := dst.Field(i)
|
||||||
|
|
||||||
if !dstField.IsValid() || !srcField.IsValid() {
|
if !dstField.IsValid() || !srcField.IsValid() {
|
||||||
return fmt.Errorf("Could not find src field: %v value: %v in destination", srcField.Type().Name(), srcField)
|
return fmt.Errorf("Could not find src field: %v value: %v in destination", srcField.Type().Name(), srcField)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := set(dstField, srcField); err != nil {
|
if err := set(dstField, srcField); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// mapArgNamesToStructFields maps a slice of argument names to struct fields.
|
// mapArgNamesToStructFields maps a slice of argument names to struct fields.
|
||||||
// first round: for each Exportable field that contains a `abi:""` tag
|
//
|
||||||
// and this field name exists in the given argument name list, pair them together.
|
// first round: for each Exportable field that contains a `abi:""` tag and this field name
|
||||||
// second round: for each argument name that has not been already linked,
|
// exists in the given argument name list, pair them together.
|
||||||
// find what variable is expected to be mapped into, if it exists and has not been
|
//
|
||||||
// used, pair them.
|
// second round: for each argument name that has not been already linked, find what
|
||||||
|
// variable is expected to be mapped into, if it exists and has not been used, pair them.
|
||||||
|
//
|
||||||
// Note this function assumes the given value is a struct value.
|
// Note this function assumes the given value is a struct value.
|
||||||
func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[string]string, error) {
|
func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[string]string, error) {
|
||||||
typ := value.Type()
|
typ := value.Type()
|
||||||
|
|
@ -201,6 +223,7 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
|
||||||
}
|
}
|
||||||
// check which argument field matches with the abi tag.
|
// check which argument field matches with the abi tag.
|
||||||
found := false
|
found := false
|
||||||
|
|
||||||
for _, arg := range argNames {
|
for _, arg := range argNames {
|
||||||
if arg == tagName {
|
if arg == tagName {
|
||||||
if abi2struct[arg] != "" {
|
if abi2struct[arg] != "" {
|
||||||
|
|
@ -220,7 +243,6 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
|
||||||
|
|
||||||
// second round ~~~
|
// second round ~~~
|
||||||
for _, argName := range argNames {
|
for _, argName := range argNames {
|
||||||
|
|
||||||
structFieldName := ToCamelCase(argName)
|
structFieldName := ToCamelCase(argName)
|
||||||
|
|
||||||
if structFieldName == "" {
|
if structFieldName == "" {
|
||||||
|
|
@ -237,6 +259,7 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
|
||||||
value.FieldByName(structFieldName).IsValid() {
|
value.FieldByName(structFieldName).IsValid() {
|
||||||
return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", argName)
|
return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", argName)
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,5 +279,6 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
|
||||||
struct2abi[structFieldName] = argName
|
struct2abi[structFieldName] = argName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return abi2struct, nil
|
return abi2struct, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ type reflectTest struct {
|
||||||
|
|
||||||
var reflectTests = []reflectTest{
|
var reflectTests = []reflectTest{
|
||||||
{
|
{
|
||||||
name: "OneToOneCorrespondance",
|
name: "OneToOneCorrespondence",
|
||||||
args: []string{"fieldA"},
|
args: []string{"fieldA"},
|
||||||
struc: struct {
|
struc: struct {
|
||||||
FieldA int `abi:"fieldA"`
|
FieldA int `abi:"fieldA"`
|
||||||
|
|
@ -181,6 +181,7 @@ func TestReflectNameToStruct(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for fname := range test.want {
|
for fname := range test.want {
|
||||||
if m[fname] != test.want[fname] {
|
if m[fname] != test.want[fname] {
|
||||||
t.Fatalf("Incorrect value for field %s: expected %v, got %v", fname, test.want[fname], m[fname])
|
t.Fatalf("Incorrect value for field %s: expected %v, got %v", fname, test.want[fname], m[fname])
|
||||||
|
|
@ -217,6 +218,7 @@ func TestConvertType(t *testing.T) {
|
||||||
if out.X.Cmp(big.NewInt(1)) != 0 {
|
if out.X.Cmp(big.NewInt(1)) != 0 {
|
||||||
t.Errorf("ConvertType failed, got %v want %v", out.X, big.NewInt(1))
|
t.Errorf("ConvertType failed, got %v want %v", out.X, big.NewInt(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
if out.Y.Cmp(big.NewInt(2)) != 0 {
|
if out.Y.Cmp(big.NewInt(2)) != 0 {
|
||||||
t.Errorf("ConvertType failed, got %v want %v", out.Y, big.NewInt(2))
|
t.Errorf("ConvertType failed, got %v want %v", out.Y, big.NewInt(2))
|
||||||
}
|
}
|
||||||
|
|
@ -226,16 +228,20 @@ func TestConvertType(t *testing.T) {
|
||||||
val2.Index(0).Field(1).Set(reflect.ValueOf(big.NewInt(2)))
|
val2.Index(0).Field(1).Set(reflect.ValueOf(big.NewInt(2)))
|
||||||
val2.Index(1).Field(0).Set(reflect.ValueOf(big.NewInt(3)))
|
val2.Index(1).Field(0).Set(reflect.ValueOf(big.NewInt(3)))
|
||||||
val2.Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4)))
|
val2.Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4)))
|
||||||
|
|
||||||
out2 := *ConvertType(val2.Interface(), new([]T)).(*[]T)
|
out2 := *ConvertType(val2.Interface(), new([]T)).(*[]T)
|
||||||
if out2[0].X.Cmp(big.NewInt(1)) != 0 {
|
if out2[0].X.Cmp(big.NewInt(1)) != 0 {
|
||||||
t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1))
|
t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
if out2[0].Y.Cmp(big.NewInt(2)) != 0 {
|
if out2[0].Y.Cmp(big.NewInt(2)) != 0 {
|
||||||
t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2))
|
t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
if out2[1].X.Cmp(big.NewInt(3)) != 0 {
|
if out2[1].X.Cmp(big.NewInt(3)) != 0 {
|
||||||
t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1))
|
t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
if out2[1].Y.Cmp(big.NewInt(4)) != 0 {
|
if out2[1].Y.Cmp(big.NewInt(4)) != 0 {
|
||||||
t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2))
|
t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2))
|
||||||
}
|
}
|
||||||
|
|
@ -245,16 +251,20 @@ func TestConvertType(t *testing.T) {
|
||||||
val3.Elem().Index(0).Field(1).Set(reflect.ValueOf(big.NewInt(2)))
|
val3.Elem().Index(0).Field(1).Set(reflect.ValueOf(big.NewInt(2)))
|
||||||
val3.Elem().Index(1).Field(0).Set(reflect.ValueOf(big.NewInt(3)))
|
val3.Elem().Index(1).Field(0).Set(reflect.ValueOf(big.NewInt(3)))
|
||||||
val3.Elem().Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4)))
|
val3.Elem().Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4)))
|
||||||
|
|
||||||
out3 := *ConvertType(val3.Interface(), new([2]T)).(*[2]T)
|
out3 := *ConvertType(val3.Interface(), new([2]T)).(*[2]T)
|
||||||
if out3[0].X.Cmp(big.NewInt(1)) != 0 {
|
if out3[0].X.Cmp(big.NewInt(1)) != 0 {
|
||||||
t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1))
|
t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
if out3[0].Y.Cmp(big.NewInt(2)) != 0 {
|
if out3[0].Y.Cmp(big.NewInt(2)) != 0 {
|
||||||
t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2))
|
t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
if out3[1].X.Cmp(big.NewInt(3)) != 0 {
|
if out3[1].X.Cmp(big.NewInt(3)) != 0 {
|
||||||
t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1))
|
t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
if out3[1].Y.Cmp(big.NewInt(4)) != 0 {
|
if out3[1].Y.Cmp(big.NewInt(4)) != 0 {
|
||||||
t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2))
|
t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,19 @@
|
||||||
|
// Copyright 2022 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -26,18 +42,23 @@ func parseToken(unescapedSelector string, isIdent bool) (string, string, error)
|
||||||
if len(unescapedSelector) == 0 {
|
if len(unescapedSelector) == 0 {
|
||||||
return "", "", fmt.Errorf("empty token")
|
return "", "", fmt.Errorf("empty token")
|
||||||
}
|
}
|
||||||
|
|
||||||
firstChar := unescapedSelector[0]
|
firstChar := unescapedSelector[0]
|
||||||
position := 1
|
position := 1
|
||||||
|
|
||||||
if !(isAlpha(firstChar) || (isIdent && isIdentifierSymbol(firstChar))) {
|
if !(isAlpha(firstChar) || (isIdent && isIdentifierSymbol(firstChar))) {
|
||||||
return "", "", fmt.Errorf("invalid token start: %c", firstChar)
|
return "", "", fmt.Errorf("invalid token start: %c", firstChar)
|
||||||
}
|
}
|
||||||
|
|
||||||
for position < len(unescapedSelector) {
|
for position < len(unescapedSelector) {
|
||||||
char := unescapedSelector[position]
|
char := unescapedSelector[position]
|
||||||
if !(isAlpha(char) || isDigit(char) || (isIdent && isIdentifierSymbol(char))) {
|
if !(isAlpha(char) || isDigit(char) || (isIdent && isIdentifierSymbol(char))) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
position++
|
position++
|
||||||
}
|
}
|
||||||
|
|
||||||
return unescapedSelector[:position], unescapedSelector[position:], nil
|
return unescapedSelector[:position], unescapedSelector[position:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,16 +75,20 @@ func parseElementaryType(unescapedSelector string) (string, string, error) {
|
||||||
for len(rest) > 0 && rest[0] == '[' {
|
for len(rest) > 0 && rest[0] == '[' {
|
||||||
parsedType = parsedType + string(rest[0])
|
parsedType = parsedType + string(rest[0])
|
||||||
rest = rest[1:]
|
rest = rest[1:]
|
||||||
|
|
||||||
for len(rest) > 0 && isDigit(rest[0]) {
|
for len(rest) > 0 && isDigit(rest[0]) {
|
||||||
parsedType = parsedType + string(rest[0])
|
parsedType = parsedType + string(rest[0])
|
||||||
rest = rest[1:]
|
rest = rest[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(rest) == 0 || rest[0] != ']' {
|
if len(rest) == 0 || rest[0] != ']' {
|
||||||
return "", "", fmt.Errorf("failed to parse array: expected ']', got %c", unescapedSelector[0])
|
return "", "", fmt.Errorf("failed to parse array: expected ']', got %c", unescapedSelector[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedType = parsedType + string(rest[0])
|
parsedType = parsedType + string(rest[0])
|
||||||
rest = rest[1:]
|
rest = rest[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsedType, rest, nil
|
return parsedType, rest, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -71,21 +96,31 @@ func parseCompositeType(unescapedSelector string) ([]interface{}, string, error)
|
||||||
if len(unescapedSelector) == 0 || unescapedSelector[0] != '(' {
|
if len(unescapedSelector) == 0 || unescapedSelector[0] != '(' {
|
||||||
return nil, "", fmt.Errorf("expected '(', got %c", unescapedSelector[0])
|
return nil, "", fmt.Errorf("expected '(', got %c", unescapedSelector[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedType, rest, err := parseType(unescapedSelector[1:])
|
parsedType, rest, err := parseType(unescapedSelector[1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", fmt.Errorf("failed to parse type: %v", err)
|
return nil, "", fmt.Errorf("failed to parse type: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
result := []interface{}{parsedType}
|
result := []interface{}{parsedType}
|
||||||
|
|
||||||
for len(rest) > 0 && rest[0] != ')' {
|
for len(rest) > 0 && rest[0] != ')' {
|
||||||
parsedType, rest, err = parseType(rest[1:])
|
parsedType, rest, err = parseType(rest[1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", fmt.Errorf("failed to parse type: %v", err)
|
return nil, "", fmt.Errorf("failed to parse type: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
result = append(result, parsedType)
|
result = append(result, parsedType)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(rest) == 0 || rest[0] != ')' {
|
if len(rest) == 0 || rest[0] != ')' {
|
||||||
return nil, "", fmt.Errorf("expected ')', got '%s'", rest)
|
return nil, "", fmt.Errorf("expected ')', got '%s'", rest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(rest) >= 3 && rest[1] == '[' && rest[2] == ']' {
|
||||||
|
return append(result, "[]"), rest[3:], nil
|
||||||
|
}
|
||||||
|
|
||||||
return result, rest[1:], nil
|
return result, rest[1:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,6 +128,7 @@ func parseType(unescapedSelector string) (interface{}, string, error) {
|
||||||
if len(unescapedSelector) == 0 {
|
if len(unescapedSelector) == 0 {
|
||||||
return nil, "", fmt.Errorf("empty type")
|
return nil, "", fmt.Errorf("empty type")
|
||||||
}
|
}
|
||||||
|
|
||||||
if unescapedSelector[0] == '(' {
|
if unescapedSelector[0] == '(' {
|
||||||
return parseCompositeType(unescapedSelector)
|
return parseCompositeType(unescapedSelector)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -102,6 +138,7 @@ func parseType(unescapedSelector string) (interface{}, string, error) {
|
||||||
|
|
||||||
func assembleArgs(args []interface{}) ([]ArgumentMarshaling, error) {
|
func assembleArgs(args []interface{}) ([]ArgumentMarshaling, error) {
|
||||||
arguments := make([]ArgumentMarshaling, 0)
|
arguments := make([]ArgumentMarshaling, 0)
|
||||||
|
|
||||||
for i, arg := range args {
|
for i, arg := range args {
|
||||||
// generate dummy name to avoid unmarshal issues
|
// generate dummy name to avoid unmarshal issues
|
||||||
name := fmt.Sprintf("name%d", i)
|
name := fmt.Sprintf("name%d", i)
|
||||||
|
|
@ -112,11 +149,20 @@ func assembleArgs(args []interface{}) ([]ArgumentMarshaling, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to assemble components: %v", err)
|
return nil, fmt.Errorf("failed to assemble components: %v", err)
|
||||||
}
|
}
|
||||||
arguments = append(arguments, ArgumentMarshaling{name, "tuple", "tuple", subArgs, false})
|
// nolint:goconst
|
||||||
|
tupleType := "tuple"
|
||||||
|
|
||||||
|
if len(subArgs) != 0 && subArgs[len(subArgs)-1].Type == "[]" {
|
||||||
|
subArgs = subArgs[:len(subArgs)-1]
|
||||||
|
tupleType = "tuple[]"
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments = append(arguments, ArgumentMarshaling{name, tupleType, tupleType, subArgs, false})
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("failed to assemble args: unexpected type %T", arg)
|
return nil, fmt.Errorf("failed to assemble args: unexpected type %T", arg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return arguments, nil
|
return arguments, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,7 +175,9 @@ func ParseSelector(unescapedSelector string) (SelectorMarshaling, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err)
|
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []interface{}{}
|
args := []interface{}{}
|
||||||
|
|
||||||
if len(rest) >= 2 && rest[0] == '(' && rest[1] == ')' {
|
if len(rest) >= 2 && rest[0] == '(' && rest[1] == ')' {
|
||||||
rest = rest[2:]
|
rest = rest[2:]
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -138,6 +186,7 @@ func ParseSelector(unescapedSelector string) (SelectorMarshaling, error) {
|
||||||
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err)
|
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(rest) > 0 {
|
if len(rest) > 0 {
|
||||||
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': unexpected string '%s'", unescapedSelector, rest)
|
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': unexpected string '%s'", unescapedSelector, rest)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,19 @@
|
||||||
|
// Copyright 2022 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
@ -10,18 +26,23 @@ import (
|
||||||
func TestParseSelector(t *testing.T) {
|
func TestParseSelector(t *testing.T) {
|
||||||
mkType := func(types ...interface{}) []ArgumentMarshaling {
|
mkType := func(types ...interface{}) []ArgumentMarshaling {
|
||||||
var result []ArgumentMarshaling
|
var result []ArgumentMarshaling
|
||||||
|
|
||||||
for i, typeOrComponents := range types {
|
for i, typeOrComponents := range types {
|
||||||
name := fmt.Sprintf("name%d", i)
|
name := fmt.Sprintf("name%d", i)
|
||||||
if typeName, ok := typeOrComponents.(string); ok {
|
if typeName, ok := typeOrComponents.(string); ok {
|
||||||
result = append(result, ArgumentMarshaling{name, typeName, typeName, nil, false})
|
result = append(result, ArgumentMarshaling{name, typeName, typeName, nil, false})
|
||||||
} else if components, ok := typeOrComponents.([]ArgumentMarshaling); ok {
|
} else if components, ok := typeOrComponents.([]ArgumentMarshaling); ok {
|
||||||
result = append(result, ArgumentMarshaling{name, "tuple", "tuple", components, false})
|
result = append(result, ArgumentMarshaling{name, "tuple", "tuple", components, false})
|
||||||
|
} else if components, ok := typeOrComponents.([][]ArgumentMarshaling); ok {
|
||||||
|
result = append(result, ArgumentMarshaling{name, "tuple[]", "tuple[]", components[0], false})
|
||||||
} else {
|
} else {
|
||||||
log.Fatalf("unexpected type %T", typeOrComponents)
|
log.Fatalf("unexpected type %T", typeOrComponents)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
name string
|
name string
|
||||||
|
|
@ -34,12 +55,20 @@ func TestParseSelector(t *testing.T) {
|
||||||
{"singleNest(bytes32,uint8,(uint256,uint256),address)", "singleNest", mkType("bytes32", "uint8", mkType("uint256", "uint256"), "address")},
|
{"singleNest(bytes32,uint8,(uint256,uint256),address)", "singleNest", mkType("bytes32", "uint8", mkType("uint256", "uint256"), "address")},
|
||||||
{"multiNest(address,(uint256[],uint256),((address,bytes32),uint256))", "multiNest",
|
{"multiNest(address,(uint256[],uint256),((address,bytes32),uint256))", "multiNest",
|
||||||
mkType("address", mkType("uint256[]", "uint256"), mkType(mkType("address", "bytes32"), "uint256"))},
|
mkType("address", mkType("uint256[]", "uint256"), mkType(mkType("address", "bytes32"), "uint256"))},
|
||||||
|
{"arrayNest((uint256,uint256)[],bytes32)", "arrayNest", mkType([][]ArgumentMarshaling{mkType("uint256", "uint256")}, "bytes32")},
|
||||||
|
{"multiArrayNest((uint256,uint256)[],(uint256,uint256)[])", "multiArrayNest",
|
||||||
|
mkType([][]ArgumentMarshaling{mkType("uint256", "uint256")}, [][]ArgumentMarshaling{mkType("uint256", "uint256")})},
|
||||||
|
{"singleArrayNestAndArray((uint256,uint256)[],bytes32[])", "singleArrayNestAndArray",
|
||||||
|
mkType([][]ArgumentMarshaling{mkType("uint256", "uint256")}, "bytes32[]")},
|
||||||
|
{"singleArrayNestWithArrayAndArray((uint256[],address[2],uint8[4][][5])[],bytes32[])", "singleArrayNestWithArrayAndArray",
|
||||||
|
mkType([][]ArgumentMarshaling{mkType("uint256[]", "address[2]", "uint8[4][][5]")}, "bytes32[]")},
|
||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
selector, err := ParseSelector(tt.input)
|
selector, err := ParseSelector(tt.input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("test %d: failed to parse selector '%v': %v", i, tt.input, err)
|
t.Errorf("test %d: failed to parse selector '%v': %v", i, tt.input, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if selector.Name != tt.name {
|
if selector.Name != tt.name {
|
||||||
t.Errorf("test %d: unexpected function name: '%s' != '%s'", i, selector.Name, tt.name)
|
t.Errorf("test %d: unexpected function name: '%s' != '%s'", i, selector.Name, tt.name)
|
||||||
}
|
}
|
||||||
|
|
@ -47,6 +76,7 @@ func TestParseSelector(t *testing.T) {
|
||||||
if selector.Type != "function" {
|
if selector.Type != "function" {
|
||||||
t.Errorf("test %d: unexpected type: '%s' != '%s'", i, selector.Type, "function")
|
t.Errorf("test %d: unexpected type: '%s' != '%s'", i, selector.Type, "function")
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(selector.Inputs, tt.args) {
|
if !reflect.DeepEqual(selector.Inputs, tt.args) {
|
||||||
t.Errorf("test %d: unexpected args: '%v' != '%v'", i, selector.Inputs, tt.args)
|
t.Errorf("test %d: unexpected args: '%v' != '%v'", i, selector.Inputs, tt.args)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import (
|
||||||
// MakeTopics converts a filter query argument list into a filter topic set.
|
// MakeTopics converts a filter query argument list into a filter topic set.
|
||||||
func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
|
func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
|
||||||
topics := make([][]common.Hash, len(query))
|
topics := make([][]common.Hash, len(query))
|
||||||
|
|
||||||
for i, filter := range query {
|
for i, filter := range query {
|
||||||
for _, rule := range filter {
|
for _, rule := range filter {
|
||||||
var topic common.Hash
|
var topic common.Hash
|
||||||
|
|
@ -81,9 +82,9 @@ func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
|
||||||
//
|
//
|
||||||
// We only convert stringS and bytes to hash, still need to deal with
|
// We only convert stringS and bytes to hash, still need to deal with
|
||||||
// array(both fixed-size and dynamic-size) and struct.
|
// 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:
|
||||||
|
|
@ -92,9 +93,11 @@ func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
|
||||||
return nil, fmt.Errorf("unsupported indexed type: %T", rule)
|
return nil, fmt.Errorf("unsupported indexed type: %T", rule)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
topics[i] = append(topics[i], topic)
|
topics[i] = append(topics[i], topic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return topics, nil
|
return topics, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,9 +108,11 @@ func genIntType(rule int64, size uint) []byte {
|
||||||
// extended to common.HashLength bytes.
|
// extended to common.HashLength bytes.
|
||||||
topic = [common.HashLength]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}
|
topic = [common.HashLength]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := uint(0); i < size; i++ {
|
for i := uint(0); i < size; i++ {
|
||||||
topic[common.HashLength-i-1] = byte(rule >> (i * 8))
|
topic[common.HashLength-i-1] = byte(rule >> (i * 8))
|
||||||
}
|
}
|
||||||
|
|
||||||
return topic[:]
|
return topic[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,7 +148,9 @@ func parseTopicWithSetter(fields Arguments, topics []common.Hash, setter func(Ar
|
||||||
if !arg.Indexed {
|
if !arg.Indexed {
|
||||||
return errors.New("non-indexed field in topic reconstruction")
|
return errors.New("non-indexed field in topic reconstruction")
|
||||||
}
|
}
|
||||||
|
|
||||||
var reconstr interface{}
|
var reconstr interface{}
|
||||||
|
|
||||||
switch arg.Type.T {
|
switch arg.Type.T {
|
||||||
case TupleTy:
|
case TupleTy:
|
||||||
return errors.New("tuple type in topic reconstruction")
|
return errors.New("tuple type in topic reconstruction")
|
||||||
|
|
@ -155,11 +162,14 @@ func parseTopicWithSetter(fields Arguments, topics []common.Hash, setter func(Ar
|
||||||
if garbage := binary.BigEndian.Uint64(topics[i][0:8]); garbage != 0 {
|
if garbage := binary.BigEndian.Uint64(topics[i][0:8]); garbage != 0 {
|
||||||
return fmt.Errorf("bind: got improperly encoded function type, got %v", topics[i].Bytes())
|
return fmt.Errorf("bind: got improperly encoded function type, got %v", topics[i].Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
var tmp [24]byte
|
var tmp [24]byte
|
||||||
|
|
||||||
copy(tmp[:], topics[i][8:32])
|
copy(tmp[:], topics[i][8:32])
|
||||||
reconstr = tmp
|
reconstr = tmp
|
||||||
default:
|
default:
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
reconstr, err = toGoType(0, arg.Type, topics[i].Bytes())
|
reconstr, err = toGoType(0, arg.Type, topics[i].Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2019 The go-ethereum Authors
|
// Copyright 2020 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
|
||||||
|
|
@ -29,6 +29,7 @@ func TestMakeTopics(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
query [][]interface{}
|
query [][]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
args args
|
||||||
|
|
@ -123,6 +124,7 @@ func TestMakeTopics(t *testing.T) {
|
||||||
t.Errorf("makeTopics() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("makeTopics() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
if !reflect.DeepEqual(got, tt.want) {
|
||||||
t.Errorf("makeTopics() = %v, want %v", got, tt.want)
|
t.Errorf("makeTopics() = %v, want %v", got, tt.want)
|
||||||
}
|
}
|
||||||
|
|
@ -355,6 +357,7 @@ func TestParseTopics(t *testing.T) {
|
||||||
if err := ParseTopics(createObj, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
|
if err := ParseTopics(createObj, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
|
||||||
t.Errorf("parseTopics() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("parseTopics() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
resultObj := tt.args.resultObj()
|
resultObj := tt.args.resultObj()
|
||||||
if !reflect.DeepEqual(createObj, resultObj) {
|
if !reflect.DeepEqual(createObj, resultObj) {
|
||||||
t.Errorf("parseTopics() = %v, want %v", createObj, resultObj)
|
t.Errorf("parseTopics() = %v, want %v", createObj, resultObj)
|
||||||
|
|
@ -372,6 +375,7 @@ func TestParseTopicsIntoMap(t *testing.T) {
|
||||||
if err := ParseTopicsIntoMap(outMap, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
|
if err := ParseTopicsIntoMap(outMap, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
|
||||||
t.Errorf("parseTopicsIntoMap() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("parseTopicsIntoMap() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
resultMap := tt.args.resultMap()
|
resultMap := tt.args.resultMap()
|
||||||
if !reflect.DeepEqual(outMap, resultMap) {
|
if !reflect.DeepEqual(outMap, resultMap) {
|
||||||
t.Errorf("parseTopicsIntoMap() = %v, want %v", outMap, resultMap)
|
t.Errorf("parseTopicsIntoMap() = %v, want %v", outMap, resultMap)
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
|
@ -70,6 +72,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
||||||
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")
|
||||||
}
|
}
|
||||||
|
|
||||||
typ.stringKind = t
|
typ.stringKind = t
|
||||||
|
|
||||||
// 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
|
||||||
|
|
@ -82,6 +85,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
||||||
}
|
}
|
||||||
// recursively embed the type
|
// recursively embed the type
|
||||||
i := strings.LastIndex(t, "[")
|
i := strings.LastIndex(t, "[")
|
||||||
|
|
||||||
embeddedType, err := NewType(t[:i], subInternal, components)
|
embeddedType, err := NewType(t[:i], subInternal, components)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Type{}, err
|
return Type{}, err
|
||||||
|
|
@ -101,14 +105,17 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
||||||
// is an array
|
// is an array
|
||||||
typ.T = ArrayTy
|
typ.T = ArrayTy
|
||||||
typ.Elem = &embeddedType
|
typ.Elem = &embeddedType
|
||||||
|
|
||||||
typ.Size, err = strconv.Atoi(intz[0])
|
typ.Size, err = strconv.Atoi(intz[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
|
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
typ.stringKind = embeddedType.stringKind + sliced
|
typ.stringKind = embeddedType.stringKind + sliced
|
||||||
} else {
|
} else {
|
||||||
return Type{}, fmt.Errorf("invalid formatting of array type")
|
return Type{}, fmt.Errorf("invalid formatting of array type")
|
||||||
}
|
}
|
||||||
|
|
||||||
return typ, err
|
return typ, err
|
||||||
}
|
}
|
||||||
// parse the type and size of the abi-type.
|
// parse the type and size of the abi-type.
|
||||||
|
|
@ -116,12 +123,15 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
||||||
if len(matches) == 0 {
|
if len(matches) == 0 {
|
||||||
return Type{}, fmt.Errorf("invalid type '%v'", t)
|
return Type{}, fmt.Errorf("invalid type '%v'", t)
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedType := matches[0]
|
parsedType := matches[0]
|
||||||
|
|
||||||
// varSize is the size of the variable
|
// varSize is the size of the variable
|
||||||
var varSize int
|
var varSize int
|
||||||
|
|
||||||
if len(parsedType[3]) > 0 {
|
if len(parsedType[3]) > 0 {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
varSize, err = strconv.Atoi(parsedType[2])
|
varSize, err = strconv.Atoi(parsedType[2])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
|
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
|
||||||
|
|
@ -152,6 +162,10 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
||||||
if varSize == 0 {
|
if varSize == 0 {
|
||||||
typ.T = BytesTy
|
typ.T = BytesTy
|
||||||
} else {
|
} else {
|
||||||
|
if varSize > 32 {
|
||||||
|
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
|
||||||
|
}
|
||||||
|
|
||||||
typ.T = FixedBytesTy
|
typ.T = FixedBytesTy
|
||||||
typ.Size = varSize
|
typ.Size = varSize
|
||||||
}
|
}
|
||||||
|
|
@ -161,31 +175,50 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
||||||
elems []*Type
|
elems []*Type
|
||||||
names []string
|
names []string
|
||||||
expression string // canonical parameter expression
|
expression string // canonical parameter expression
|
||||||
|
used = make(map[string]bool)
|
||||||
)
|
)
|
||||||
|
|
||||||
expression += "("
|
expression += "("
|
||||||
overloadedNames := make(map[string]string)
|
|
||||||
for idx, c := range components {
|
for idx, c := range components {
|
||||||
cType, err := NewType(c.Type, c.InternalType, c.Components)
|
cType, err := NewType(c.Type, c.InternalType, c.Components)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Type{}, err
|
return Type{}, err
|
||||||
}
|
}
|
||||||
fieldName, err := overloadedArgName(c.Name, overloadedNames)
|
|
||||||
|
name := ToCamelCase(c.Name)
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
return Type{}, errors.New("abi: purely anonymous or underscored field is not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldName := ResolveNameConflict(name, func(s string) bool { return used[s] })
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Type{}, err
|
return Type{}, err
|
||||||
}
|
}
|
||||||
overloadedNames[fieldName] = fieldName
|
|
||||||
|
used[fieldName] = true
|
||||||
|
|
||||||
|
if !isValidFieldName(fieldName) {
|
||||||
|
return Type{}, fmt.Errorf("field %d has invalid name", idx)
|
||||||
|
}
|
||||||
|
|
||||||
fields = append(fields, reflect.StructField{
|
fields = append(fields, reflect.StructField{
|
||||||
Name: fieldName, // reflect.StructOf will panic for any exported field.
|
Name: fieldName, // reflect.StructOf will panic for any exported field.
|
||||||
Type: cType.GetType(),
|
Type: cType.GetType(),
|
||||||
Tag: reflect.StructTag("json:\"" + c.Name + "\""),
|
Tag: reflect.StructTag("json:\"" + c.Name + "\""),
|
||||||
})
|
})
|
||||||
|
|
||||||
elems = append(elems, &cType)
|
elems = append(elems, &cType)
|
||||||
names = append(names, c.Name)
|
names = append(names, c.Name)
|
||||||
|
|
||||||
expression += cType.stringKind
|
expression += cType.stringKind
|
||||||
if idx != len(components)-1 {
|
if idx != len(components)-1 {
|
||||||
expression += ","
|
expression += ","
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
expression += ")"
|
expression += ")"
|
||||||
|
|
||||||
typ.TupleType = reflect.StructOf(fields)
|
typ.TupleType = reflect.StructOf(fields)
|
||||||
|
|
@ -201,7 +234,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
||||||
if internalType != "" && strings.HasPrefix(internalType, structPrefix) {
|
if internalType != "" && strings.HasPrefix(internalType, structPrefix) {
|
||||||
// Foo.Bar type definition is not allowed in golang,
|
// Foo.Bar type definition is not allowed in golang,
|
||||||
// convert the format to FooBar
|
// convert the format to FooBar
|
||||||
typ.TupleRawName = strings.Replace(internalType[len(structPrefix):], ".", "", -1)
|
typ.TupleRawName = strings.ReplaceAll(internalType[len(structPrefix):], ".", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
case "function":
|
case "function":
|
||||||
|
|
@ -250,20 +283,6 @@ func (t Type) GetType() reflect.Type {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func overloadedArgName(rawName string, names map[string]string) (string, error) {
|
|
||||||
fieldName := ToCamelCase(rawName)
|
|
||||||
if fieldName == "" {
|
|
||||||
return "", errors.New("abi: purely anonymous or underscored field is not supported")
|
|
||||||
}
|
|
||||||
// Handle overloaded fieldNames
|
|
||||||
_, ok := names[fieldName]
|
|
||||||
for idx := 0; ok; idx++ {
|
|
||||||
fieldName = fmt.Sprintf("%s%d", ToCamelCase(rawName), idx)
|
|
||||||
_, ok = names[fieldName]
|
|
||||||
}
|
|
||||||
return fieldName, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String implements Stringer.
|
// String implements Stringer.
|
||||||
func (t Type) String() (out string) {
|
func (t Type) String() (out string) {
|
||||||
return t.stringKind
|
return t.stringKind
|
||||||
|
|
@ -288,23 +307,29 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
|
||||||
// calculate offset if any
|
// calculate offset if any
|
||||||
offset := 0
|
offset := 0
|
||||||
offsetReq := isDynamicType(*t.Elem)
|
offsetReq := isDynamicType(*t.Elem)
|
||||||
|
|
||||||
if offsetReq {
|
if offsetReq {
|
||||||
offset = getTypeSize(*t.Elem) * v.Len()
|
offset = getTypeSize(*t.Elem) * v.Len()
|
||||||
}
|
}
|
||||||
|
|
||||||
var tail []byte
|
var tail []byte
|
||||||
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
for i := 0; i < v.Len(); i++ {
|
||||||
val, err := t.Elem.pack(v.Index(i))
|
val, err := t.Elem.pack(v.Index(i))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !offsetReq {
|
if !offsetReq {
|
||||||
ret = append(ret, val...)
|
ret = append(ret, val...)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = append(ret, packNum(reflect.ValueOf(offset))...)
|
ret = append(ret, packNum(reflect.ValueOf(offset))...)
|
||||||
offset += len(val)
|
offset += len(val)
|
||||||
tail = append(tail, val...)
|
tail = append(tail, val...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return append(ret, tail...), nil
|
return append(ret, tail...), nil
|
||||||
case TupleTy:
|
case TupleTy:
|
||||||
// (T1,...,Tk) for k >= 0 and any types T1, …, Tk
|
// (T1,...,Tk) for k >= 0 and any types T1, …, Tk
|
||||||
|
|
@ -325,16 +350,20 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
|
||||||
for _, elem := range t.TupleElems {
|
for _, elem := range t.TupleElems {
|
||||||
offset += getTypeSize(*elem)
|
offset += getTypeSize(*elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret, tail []byte
|
var ret, tail []byte
|
||||||
|
|
||||||
for i, elem := range t.TupleElems {
|
for i, elem := range t.TupleElems {
|
||||||
field := v.FieldByName(fieldmap[t.TupleRawNames[i]])
|
field := v.FieldByName(fieldmap[t.TupleRawNames[i]])
|
||||||
if !field.IsValid() {
|
if !field.IsValid() {
|
||||||
return nil, fmt.Errorf("field %s for tuple not found in the given struct", t.TupleRawNames[i])
|
return nil, fmt.Errorf("field %s for tuple not found in the given struct", t.TupleRawNames[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
val, err := elem.pack(field)
|
val, err := elem.pack(field)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if isDynamicType(*elem) {
|
if isDynamicType(*elem) {
|
||||||
ret = append(ret, packNum(reflect.ValueOf(offset))...)
|
ret = append(ret, packNum(reflect.ValueOf(offset))...)
|
||||||
tail = append(tail, val...)
|
tail = append(tail, val...)
|
||||||
|
|
@ -343,6 +372,7 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
|
||||||
ret = append(ret, val...)
|
ret = append(ret, val...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return append(ret, tail...), nil
|
return append(ret, tail...), nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
@ -370,8 +400,10 @@ func isDynamicType(t Type) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return t.T == StringTy || t.T == BytesTy || t.T == SliceTy || (t.T == ArrayTy && isDynamicType(*t.Elem))
|
return t.T == StringTy || t.T == BytesTy || t.T == SliceTy || (t.T == ArrayTy && isDynamicType(*t.Elem))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -389,13 +421,43 @@ func getTypeSize(t Type) int {
|
||||||
if t.Elem.T == ArrayTy || t.Elem.T == TupleTy {
|
if t.Elem.T == ArrayTy || t.Elem.T == TupleTy {
|
||||||
return t.Size * getTypeSize(*t.Elem)
|
return t.Size * getTypeSize(*t.Elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
return t.Size * 32
|
return t.Size * 32
|
||||||
} else if t.T == TupleTy && !isDynamicType(t) {
|
} else if t.T == TupleTy && !isDynamicType(t) {
|
||||||
total := 0
|
total := 0
|
||||||
for _, elem := range t.TupleElems {
|
for _, elem := range t.TupleElems {
|
||||||
total += getTypeSize(*elem)
|
total += getTypeSize(*elem)
|
||||||
}
|
}
|
||||||
|
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
return 32
|
return 32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isLetter reports whether a given 'rune' is classified as a Letter.
|
||||||
|
// This method is copied from reflect/type.go
|
||||||
|
func isLetter(ch rune) bool {
|
||||||
|
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= utf8.RuneSelf && unicode.IsLetter(ch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValidFieldName checks if a string is a valid (struct) field name or not.
|
||||||
|
//
|
||||||
|
// According to the language spec, a field name should be an identifier.
|
||||||
|
//
|
||||||
|
// identifier = letter { letter | unicode_digit } .
|
||||||
|
// letter = unicode_letter | "_" .
|
||||||
|
// This method is copied from reflect/type.go
|
||||||
|
func isValidFieldName(fieldName string) bool {
|
||||||
|
for i, c := range fieldName {
|
||||||
|
if i == 0 && !isLetter(c) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !(isLetter(c) || unicode.IsDigit(c)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(fieldName) > 0
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,7 @@ func TestTypeRegexp(t *testing.T) {
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(typ, tt.kind) {
|
if !reflect.DeepEqual(typ, tt.kind) {
|
||||||
t.Errorf("type %q: parsed type mismatch:\nGOT %s\nWANT %s ", tt.blob, spew.Sdump(typeWithoutStringer(typ)), spew.Sdump(typeWithoutStringer(tt.kind)))
|
t.Errorf("type %q: parsed type mismatch:\nGOT %s\nWANT %s ", tt.blob, spew.Sdump(typeWithoutStringer(typ)), spew.Sdump(typeWithoutStringer(tt.kind)))
|
||||||
}
|
}
|
||||||
|
|
@ -288,6 +289,7 @@ func TestTypeCheck(t *testing.T) {
|
||||||
if err.Error() != test.err {
|
if err.Error() != test.err {
|
||||||
t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err)
|
t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -296,6 +298,7 @@ func TestTypeCheck(t *testing.T) {
|
||||||
t.Errorf("%d failed. Expected no err but got: %v", i, err)
|
t.Errorf("%d failed. Expected no err but got: %v", i, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil && len(test.err) != 0 {
|
if err == nil && len(test.err) != 0 {
|
||||||
t.Errorf("%d failed. Expected err: %v but got none", i, test.err)
|
t.Errorf("%d failed. Expected err: %v but got none", i, test.err)
|
||||||
continue
|
continue
|
||||||
|
|
@ -323,9 +326,11 @@ func TestInternalType(t *testing.T) {
|
||||||
|
|
||||||
blob := "tuple"
|
blob := "tuple"
|
||||||
typ, err := NewType(blob, internalType, components)
|
typ, err := NewType(blob, internalType, components)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("type %q: failed to parse type string: %v", blob, err)
|
t.Errorf("type %q: failed to parse type string: %v", blob, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(typ, kind) {
|
if !reflect.DeepEqual(typ, kind) {
|
||||||
t.Errorf("type %q: parsed type mismatch:\nGOT %s\nWANT %s ", blob, spew.Sdump(typeWithoutStringer(typ)), spew.Sdump(typeWithoutStringer(kind)))
|
t.Errorf("type %q: parsed type mismatch:\nGOT %s\nWANT %s ", blob, spew.Sdump(typeWithoutStringer(typ)), spew.Sdump(typeWithoutStringer(kind)))
|
||||||
}
|
}
|
||||||
|
|
@ -366,3 +371,12 @@ func TestGetTypeSize(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewFixedBytesOver32(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
_, err := NewType("bytes4096", "", nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("fixed bytes with size over 32 is not spec'd")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ package abi
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
|
@ -33,43 +34,83 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadInteger reads the integer based on its kind and returns the appropriate value.
|
// ReadInteger reads the integer based on its kind and returns the appropriate value.
|
||||||
func ReadInteger(typ Type, b []byte) interface{} {
|
// nolint:gocognit
|
||||||
|
func ReadInteger(typ Type, b []byte) (interface{}, error) {
|
||||||
|
ret := new(big.Int).SetBytes(b)
|
||||||
|
|
||||||
if typ.T == UintTy {
|
if typ.T == UintTy {
|
||||||
|
u64, isu64 := ret.Uint64(), ret.IsUint64()
|
||||||
|
|
||||||
switch typ.Size {
|
switch typ.Size {
|
||||||
case 8:
|
case 8:
|
||||||
return b[len(b)-1]
|
if !isu64 || u64 > math.MaxUint8 {
|
||||||
|
return nil, errBadUint8
|
||||||
|
}
|
||||||
|
|
||||||
|
return byte(u64), nil
|
||||||
case 16:
|
case 16:
|
||||||
return binary.BigEndian.Uint16(b[len(b)-2:])
|
if !isu64 || u64 > math.MaxUint16 {
|
||||||
|
return nil, errBadUint16
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint16(u64), nil
|
||||||
case 32:
|
case 32:
|
||||||
return binary.BigEndian.Uint32(b[len(b)-4:])
|
if !isu64 || u64 > math.MaxUint32 {
|
||||||
|
return nil, errBadUint32
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint32(u64), nil
|
||||||
case 64:
|
case 64:
|
||||||
return binary.BigEndian.Uint64(b[len(b)-8:])
|
if !isu64 {
|
||||||
|
return nil, errBadUint64
|
||||||
|
}
|
||||||
|
|
||||||
|
return u64, nil
|
||||||
default:
|
default:
|
||||||
// the only case left for unsigned integer is uint256.
|
// the only case left for unsigned integer is uint256.
|
||||||
return new(big.Int).SetBytes(b)
|
return ret, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch typ.Size {
|
|
||||||
case 8:
|
|
||||||
return int8(b[len(b)-1])
|
|
||||||
case 16:
|
|
||||||
return int16(binary.BigEndian.Uint16(b[len(b)-2:]))
|
|
||||||
case 32:
|
|
||||||
return int32(binary.BigEndian.Uint32(b[len(b)-4:]))
|
|
||||||
case 64:
|
|
||||||
return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
|
|
||||||
default:
|
|
||||||
// the only case left for integer is int256
|
|
||||||
// big.SetBytes can't tell if a number is negative or positive in itself.
|
// big.SetBytes can't tell if a number is negative or positive in itself.
|
||||||
// On EVM, if the returned number > max int256, it is negative.
|
// On EVM, if the returned number > max int256, it is negative.
|
||||||
// A number is > max int256 if the bit at position 255 is set.
|
// A number is > max int256 if the bit at position 255 is set.
|
||||||
ret := new(big.Int).SetBytes(b)
|
|
||||||
if ret.Bit(255) == 1 {
|
if ret.Bit(255) == 1 {
|
||||||
ret.Add(MaxUint256, new(big.Int).Neg(ret))
|
ret.Add(MaxUint256, new(big.Int).Neg(ret))
|
||||||
ret.Add(ret, common.Big1)
|
ret.Add(ret, common.Big1)
|
||||||
ret.Neg(ret)
|
ret.Neg(ret)
|
||||||
}
|
}
|
||||||
return ret
|
|
||||||
|
i64, isi64 := ret.Int64(), ret.IsInt64()
|
||||||
|
|
||||||
|
switch typ.Size {
|
||||||
|
case 8:
|
||||||
|
if !isi64 || i64 < math.MinInt8 || i64 > math.MaxInt8 {
|
||||||
|
return nil, errBadInt8
|
||||||
|
}
|
||||||
|
|
||||||
|
return int8(i64), nil
|
||||||
|
case 16:
|
||||||
|
if !isi64 || i64 < math.MinInt16 || i64 > math.MaxInt16 {
|
||||||
|
return nil, errBadInt16
|
||||||
|
}
|
||||||
|
|
||||||
|
return int16(i64), nil
|
||||||
|
case 32:
|
||||||
|
if !isi64 || i64 < math.MinInt32 || i64 > math.MaxInt32 {
|
||||||
|
return nil, errBadInt32
|
||||||
|
}
|
||||||
|
|
||||||
|
return int32(i64), nil
|
||||||
|
case 64:
|
||||||
|
if !isi64 {
|
||||||
|
return nil, errBadInt64
|
||||||
|
}
|
||||||
|
|
||||||
|
return i64, nil
|
||||||
|
default:
|
||||||
|
// the only case left for integer is int256
|
||||||
|
return ret, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,6 +121,7 @@ func readBool(word []byte) (bool, error) {
|
||||||
return false, errBadBool
|
return false, errBadBool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch word[31] {
|
switch word[31] {
|
||||||
case 0:
|
case 0:
|
||||||
return false, nil
|
return false, nil
|
||||||
|
|
@ -97,11 +139,13 @@ func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
|
||||||
if t.T != FunctionTy {
|
if t.T != FunctionTy {
|
||||||
return [24]byte{}, fmt.Errorf("abi: invalid type in call to make function type byte array")
|
return [24]byte{}, fmt.Errorf("abi: invalid type in call to make function type byte array")
|
||||||
}
|
}
|
||||||
|
|
||||||
if garbage := binary.BigEndian.Uint64(word[24:32]); garbage != 0 {
|
if garbage := binary.BigEndian.Uint64(word[24:32]); garbage != 0 {
|
||||||
err = fmt.Errorf("abi: got improperly encoded function type, got %v", word)
|
err = fmt.Errorf("abi: got improperly encoded function type, got %v", word)
|
||||||
} else {
|
} else {
|
||||||
copy(funcTy[:], word[0:24])
|
copy(funcTy[:], word[0:24])
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,8 +158,8 @@ func ReadFixedBytes(t Type, word []byte) (interface{}, error) {
|
||||||
array := reflect.New(t.GetType()).Elem()
|
array := reflect.New(t.GetType()).Elem()
|
||||||
|
|
||||||
reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
|
reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
|
||||||
return array.Interface(), nil
|
|
||||||
|
|
||||||
|
return array.Interface(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// forEachUnpack iteratively unpack elements.
|
// forEachUnpack iteratively unpack elements.
|
||||||
|
|
@ -123,6 +167,7 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
|
||||||
if size < 0 {
|
if size < 0 {
|
||||||
return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size)
|
return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size)
|
||||||
}
|
}
|
||||||
|
|
||||||
if start+32*size > len(output) {
|
if start+32*size > len(output) {
|
||||||
return nil, fmt.Errorf("abi: cannot marshal into go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
|
return nil, fmt.Errorf("abi: cannot marshal into go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
|
||||||
}
|
}
|
||||||
|
|
@ -160,9 +205,14 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
|
||||||
|
|
||||||
func forTupleUnpack(t Type, output []byte) (interface{}, error) {
|
func forTupleUnpack(t Type, output []byte) (interface{}, error) {
|
||||||
retval := reflect.New(t.GetType()).Elem()
|
retval := reflect.New(t.GetType()).Elem()
|
||||||
|
|
||||||
virtualArgs := 0
|
virtualArgs := 0
|
||||||
for index, elem := range t.TupleElems {
|
for index, elem := range t.TupleElems {
|
||||||
marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output)
|
marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if elem.T == ArrayTy && !isDynamicType(*elem) {
|
if elem.T == ArrayTy && !isDynamicType(*elem) {
|
||||||
// If we have a static array, like [3]uint256, these are coded as
|
// If we have a static array, like [3]uint256, these are coded as
|
||||||
// just like uint256,uint256,uint256.
|
// just like uint256,uint256,uint256.
|
||||||
|
|
@ -180,11 +230,10 @@ func forTupleUnpack(t Type, output []byte) (interface{}, error) {
|
||||||
// coded as just like uint256,bool,uint256
|
// coded as just like uint256,bool,uint256
|
||||||
virtualArgs += getTypeSize(*elem)/32 - 1
|
virtualArgs += getTypeSize(*elem)/32 - 1
|
||||||
}
|
}
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
retval.Field(index).Set(reflect.ValueOf(marshalledValue))
|
retval.Field(index).Set(reflect.ValueOf(marshalledValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval.Interface(), nil
|
return retval.Interface(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -218,8 +267,10 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return forTupleUnpack(t, output[begin:])
|
return forTupleUnpack(t, output[begin:])
|
||||||
}
|
}
|
||||||
|
|
||||||
return forTupleUnpack(t, output[index:])
|
return forTupleUnpack(t, output[index:])
|
||||||
case SliceTy:
|
case SliceTy:
|
||||||
return forEachUnpack(t, output[begin:], 0, length)
|
return forEachUnpack(t, output[begin:], 0, length)
|
||||||
|
|
@ -229,13 +280,15 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
||||||
if offset > uint64(len(output)) {
|
if offset > uint64(len(output)) {
|
||||||
return nil, fmt.Errorf("abi: toGoType offset greater than output length: offset: %d, len(output): %d", offset, len(output))
|
return nil, fmt.Errorf("abi: toGoType offset greater than output length: offset: %d, len(output): %d", offset, len(output))
|
||||||
}
|
}
|
||||||
|
|
||||||
return forEachUnpack(t, output[offset:], 0, t.Size)
|
return forEachUnpack(t, output[offset:], 0, t.Size)
|
||||||
}
|
}
|
||||||
|
|
||||||
return forEachUnpack(t, output[index:], 0, t.Size)
|
return forEachUnpack(t, output[index:], 0, t.Size)
|
||||||
case StringTy: // variable arrays are written at the end of the return bytes
|
case StringTy: // variable arrays are written at the end of the return bytes
|
||||||
return string(output[begin : begin+length]), nil
|
return string(output[begin : begin+length]), nil
|
||||||
case IntTy, UintTy:
|
case IntTy, UintTy:
|
||||||
return ReadInteger(t, returnOutput), nil
|
return ReadInteger(t, returnOutput)
|
||||||
case BoolTy:
|
case BoolTy:
|
||||||
return readBool(returnOutput)
|
return readBool(returnOutput)
|
||||||
case AddressTy:
|
case AddressTy:
|
||||||
|
|
@ -255,8 +308,9 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
||||||
|
|
||||||
// lengthPrefixPointsTo interprets a 32 byte slice as an offset and then determines which indices to look to decode the type.
|
// lengthPrefixPointsTo interprets a 32 byte slice as an offset and then determines which indices to look to decode the type.
|
||||||
func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
|
func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
|
||||||
bigOffsetEnd := big.NewInt(0).SetBytes(output[index : index+32])
|
bigOffsetEnd := new(big.Int).SetBytes(output[index : index+32])
|
||||||
bigOffsetEnd.Add(bigOffsetEnd, common.Big32)
|
bigOffsetEnd.Add(bigOffsetEnd, common.Big32)
|
||||||
|
|
||||||
outputLength := big.NewInt(int64(len(output)))
|
outputLength := big.NewInt(int64(len(output)))
|
||||||
|
|
||||||
if bigOffsetEnd.Cmp(outputLength) > 0 {
|
if bigOffsetEnd.Cmp(outputLength) > 0 {
|
||||||
|
|
@ -268,11 +322,9 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
|
||||||
}
|
}
|
||||||
|
|
||||||
offsetEnd := int(bigOffsetEnd.Uint64())
|
offsetEnd := int(bigOffsetEnd.Uint64())
|
||||||
lengthBig := big.NewInt(0).SetBytes(output[offsetEnd-32 : offsetEnd])
|
lengthBig := new(big.Int).SetBytes(output[offsetEnd-32 : offsetEnd])
|
||||||
|
|
||||||
totalSize := big.NewInt(0)
|
totalSize := new(big.Int).Add(bigOffsetEnd, lengthBig)
|
||||||
totalSize.Add(totalSize, bigOffsetEnd)
|
|
||||||
totalSize.Add(totalSize, lengthBig)
|
|
||||||
if totalSize.BitLen() > 63 {
|
if totalSize.BitLen() > 63 {
|
||||||
return 0, 0, fmt.Errorf("abi: length larger than int64: %v", totalSize)
|
return 0, 0, fmt.Errorf("abi: length larger than int64: %v", totalSize)
|
||||||
}
|
}
|
||||||
|
|
@ -280,21 +332,25 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
|
||||||
if totalSize.Cmp(outputLength) > 0 {
|
if totalSize.Cmp(outputLength) > 0 {
|
||||||
return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %v require %v", outputLength, totalSize)
|
return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %v require %v", outputLength, totalSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
start = int(bigOffsetEnd.Uint64())
|
start = int(bigOffsetEnd.Uint64())
|
||||||
length = int(lengthBig.Uint64())
|
length = int(lengthBig.Uint64())
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// tuplePointsTo resolves the location reference for dynamic tuple.
|
// tuplePointsTo resolves the location reference for dynamic tuple.
|
||||||
func tuplePointsTo(index int, output []byte) (start int, err error) {
|
func tuplePointsTo(index int, output []byte) (start int, err error) {
|
||||||
offset := big.NewInt(0).SetBytes(output[index : index+32])
|
offset := new(big.Int).SetBytes(output[index : index+32])
|
||||||
outputLen := big.NewInt(int64(len(output)))
|
outputLen := big.NewInt(int64(len(output)))
|
||||||
|
|
||||||
if offset.Cmp(outputLen) > 0 {
|
if offset.Cmp(outputLen) > 0 {
|
||||||
return 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", offset, outputLen)
|
return 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", offset, outputLen)
|
||||||
}
|
}
|
||||||
|
|
||||||
if offset.BitLen() > 63 {
|
if offset.BitLen() > 63 {
|
||||||
return 0, fmt.Errorf("abi offset larger than int64: %v", offset)
|
return 0, fmt.Errorf("abi offset larger than int64: %v", offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
return int(offset.Uint64()), nil
|
return int(offset.Uint64()), nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
@ -37,18 +38,22 @@ func TestUnpack(t *testing.T) {
|
||||||
//Unpack
|
//Unpack
|
||||||
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
|
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
|
||||||
abi, err := JSON(strings.NewReader(def))
|
abi, err := JSON(strings.NewReader(def))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
encb, err := hex.DecodeString(test.packed)
|
encb, err := hex.DecodeString(test.packed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("invalid hex %s: %v", test.packed, err)
|
t.Fatalf("invalid hex %s: %v", test.packed, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := abi.Unpack("method", encb)
|
out, err := abi.Unpack("method", encb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("test %d (%v) failed: %v", i, test.def, err)
|
t.Errorf("test %d (%v) failed: %v", i, test.def, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(test.unpacked, ConvertType(out[0], test.unpacked)) {
|
if !reflect.DeepEqual(test.unpacked, ConvertType(out[0], test.unpacked)) {
|
||||||
t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.unpacked, out[0])
|
t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.unpacked, out[0])
|
||||||
}
|
}
|
||||||
|
|
@ -73,6 +78,7 @@ func (test unpackTest) checkError(err error) error {
|
||||||
} else if len(test.err) > 0 {
|
} else if len(test.err) > 0 {
|
||||||
return fmt.Errorf("expected err: %v but got none", test.err)
|
return fmt.Errorf("expected err: %v but got none", test.err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,6 +207,23 @@ var unpackTests = []unpackTest{
|
||||||
IntOne *big.Int
|
IntOne *big.Int
|
||||||
}{big.NewInt(1)},
|
}{big.NewInt(1)},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type":"bool"}]`,
|
||||||
|
enc: "",
|
||||||
|
want: false,
|
||||||
|
err: "abi: attempting to unmarshall an empty string while arguments are expected",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type":"bytes32","indexed":true},{"type":"uint256","indexed":false}]`,
|
||||||
|
enc: "",
|
||||||
|
want: false,
|
||||||
|
err: "abi: attempting to unmarshall an empty string while arguments are expected",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
def: `[{"type":"bool","indexed":true},{"type":"uint64","indexed":true}]`,
|
||||||
|
enc: "",
|
||||||
|
want: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestLocalUnpackTests runs test specially designed only for unpacking.
|
// TestLocalUnpackTests runs test specially designed only for unpacking.
|
||||||
|
|
@ -211,19 +234,24 @@ func TestLocalUnpackTests(t *testing.T) {
|
||||||
//Unpack
|
//Unpack
|
||||||
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
|
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
|
||||||
abi, err := JSON(strings.NewReader(def))
|
abi, err := JSON(strings.NewReader(def))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
encb, err := hex.DecodeString(test.enc)
|
encb, err := hex.DecodeString(test.enc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("invalid hex %s: %v", test.enc, err)
|
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.UnpackIntoInterface(outptr.Interface(), "method", encb)
|
err = abi.UnpackIntoInterface(outptr.Interface(), "method", encb)
|
||||||
if err := test.checkError(err); err != nil {
|
if err := test.checkError(err); err != nil {
|
||||||
t.Errorf("test %d (%v) failed: %v", i, test.def, err)
|
t.Errorf("test %d (%v) failed: %v", i, test.def, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
out := outptr.Elem().Interface()
|
out := outptr.Elem().Interface()
|
||||||
if !reflect.DeepEqual(test.want, out) {
|
if !reflect.DeepEqual(test.want, out) {
|
||||||
t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.want, out)
|
t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.want, out)
|
||||||
|
|
@ -251,13 +279,16 @@ func TestUnpackIntoInterfaceSetDynamicArrayOutput(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(out32) != 2 {
|
if len(out32) != 2 {
|
||||||
t.Fatalf("expected array with 2 values, got %d", len(out32))
|
t.Fatalf("expected array with 2 values, got %d", len(out32))
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := common.Hex2Bytes("3078313233343536373839300000000000000000000000000000000000000000")
|
expected := common.Hex2Bytes("3078313233343536373839300000000000000000000000000000000000000000")
|
||||||
if !bytes.Equal(out32[0][:], expected) {
|
if !bytes.Equal(out32[0][:], expected) {
|
||||||
t.Errorf("expected %x, got %x\n", expected, out32[0])
|
t.Errorf("expected %x, got %x\n", expected, out32[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
expected = common.Hex2Bytes("3078303938373635343332310000000000000000000000000000000000000000")
|
expected = common.Hex2Bytes("3078303938373635343332310000000000000000000000000000000000000000")
|
||||||
if !bytes.Equal(out32[1][:], expected) {
|
if !bytes.Equal(out32[1][:], expected) {
|
||||||
t.Errorf("expected %x, got %x\n", expected, out32[1])
|
t.Errorf("expected %x, got %x\n", expected, out32[1])
|
||||||
|
|
@ -268,13 +299,16 @@ func TestUnpackIntoInterfaceSetDynamicArrayOutput(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(out15) != 2 {
|
if len(out15) != 2 {
|
||||||
t.Fatalf("expected array with 2 values, got %d", len(out15))
|
t.Fatalf("expected array with 2 values, got %d", len(out15))
|
||||||
}
|
}
|
||||||
|
|
||||||
expected = common.Hex2Bytes("307830313233343500000000000000")
|
expected = common.Hex2Bytes("307830313233343500000000000000")
|
||||||
if !bytes.Equal(out15[0][:], expected) {
|
if !bytes.Equal(out15[0][:], expected) {
|
||||||
t.Errorf("expected %x, got %x\n", expected, out15[0])
|
t.Errorf("expected %x, got %x\n", expected, out15[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
expected = common.Hex2Bytes("307839383736353400000000000000")
|
expected = common.Hex2Bytes("307839383736353400000000000000")
|
||||||
if !bytes.Equal(out15[1][:], expected) {
|
if !bytes.Equal(out15[1][:], expected) {
|
||||||
t.Errorf("expected %x, got %x\n", expected, out15[1])
|
t.Errorf("expected %x, got %x\n", expected, out15[1])
|
||||||
|
|
@ -289,6 +323,7 @@ type methodMultiOutput struct {
|
||||||
func methodMultiReturn(require *require.Assertions) (ABI, []byte, methodMultiOutput) {
|
func methodMultiReturn(require *require.Assertions) (ABI, []byte, methodMultiOutput) {
|
||||||
const definition = `[
|
const definition = `[
|
||||||
{ "name" : "multi", "type": "function", "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
|
{ "name" : "multi", "type": "function", "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
|
||||||
|
|
||||||
var expected = methodMultiOutput{big.NewInt(1), "hello"}
|
var expected = methodMultiOutput{big.NewInt(1), "hello"}
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
|
|
@ -299,6 +334,7 @@ func methodMultiReturn(require *require.Assertions) (ABI, []byte, methodMultiOut
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005"))
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005"))
|
||||||
buff.Write(common.RightPadBytes([]byte(expected.String), 32))
|
buff.Write(common.RightPadBytes([]byte(expected.String), 32))
|
||||||
|
|
||||||
return abi, buff.Bytes(), expected
|
return abi, buff.Bytes(), expected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -315,6 +351,7 @@ func TestMethodMultiReturn(t *testing.T) {
|
||||||
|
|
||||||
abi, data, expected := methodMultiReturn(require.New(t))
|
abi, data, expected := methodMultiReturn(require.New(t))
|
||||||
bigint := new(big.Int)
|
bigint := new(big.Int)
|
||||||
|
|
||||||
var testCases = []struct {
|
var testCases = []struct {
|
||||||
dest interface{}
|
dest interface{}
|
||||||
expected interface{}
|
expected interface{}
|
||||||
|
|
@ -335,6 +372,11 @@ func TestMethodMultiReturn(t *testing.T) {
|
||||||
&[]interface{}{&expected.Int, &expected.String},
|
&[]interface{}{&expected.Int, &expected.String},
|
||||||
"",
|
"",
|
||||||
"Can unpack into a slice",
|
"Can unpack into a slice",
|
||||||
|
}, {
|
||||||
|
&[]interface{}{&bigint, ""},
|
||||||
|
&[]interface{}{&expected.Int, expected.String},
|
||||||
|
"",
|
||||||
|
"Can unpack into a slice without indirection",
|
||||||
}, {
|
}, {
|
||||||
&[2]interface{}{&bigint, new(string)},
|
&[2]interface{}{&bigint, new(string)},
|
||||||
&[2]interface{}{&expected.Int, &expected.String},
|
&[2]interface{}{&expected.Int, &expected.String},
|
||||||
|
|
@ -361,11 +403,13 @@ func TestMethodMultiReturn(t *testing.T) {
|
||||||
"abi: insufficient number of arguments for unpack, want 2, got 1",
|
"abi: insufficient number of arguments for unpack, want 2, got 1",
|
||||||
"Can not unpack into a slice with wrong types",
|
"Can not unpack into a slice with wrong types",
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
tc := tc
|
tc := tc
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
require := require.New(t)
|
require := require.New(t)
|
||||||
err := abi.UnpackIntoInterface(tc.dest, "multi", data)
|
err := abi.UnpackIntoInterface(tc.dest, "multi", data)
|
||||||
|
|
||||||
if tc.error == "" {
|
if tc.error == "" {
|
||||||
require.Nil(err, "Should be able to unpack method outputs.")
|
require.Nil(err, "Should be able to unpack method outputs.")
|
||||||
require.Equal(tc.expected, tc.dest)
|
require.Equal(tc.expected, tc.dest)
|
||||||
|
|
@ -378,22 +422,27 @@ func TestMethodMultiReturn(t *testing.T) {
|
||||||
|
|
||||||
func TestMultiReturnWithArray(t *testing.T) {
|
func TestMultiReturnWithArray(t *testing.T) {
|
||||||
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"type": "uint64[3]"}, {"type": "uint64"}]}]`
|
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"type": "uint64[3]"}, {"type": "uint64"}]}]`
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buff := new(bytes.Buffer)
|
buff := new(bytes.Buffer)
|
||||||
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000009"))
|
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000009"))
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000008"))
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000008"))
|
||||||
|
|
||||||
ret1, ret1Exp := new([3]uint64), [3]uint64{9, 9, 9}
|
ret1, ret1Exp := new([3]uint64), [3]uint64{9, 9, 9}
|
||||||
ret2, ret2Exp := new(uint64), uint64(8)
|
ret2, ret2Exp := new(uint64), uint64(8)
|
||||||
|
|
||||||
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
|
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
||||||
t.Error("array result", *ret1, "!= Expected", ret1Exp)
|
t.Error("array result", *ret1, "!= Expected", ret1Exp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *ret2 != ret2Exp {
|
if *ret2 != ret2Exp {
|
||||||
t.Error("int result", *ret2, "!= Expected", ret2Exp)
|
t.Error("int result", *ret2, "!= Expected", ret2Exp)
|
||||||
}
|
}
|
||||||
|
|
@ -401,29 +450,37 @@ func TestMultiReturnWithArray(t *testing.T) {
|
||||||
|
|
||||||
func TestMultiReturnWithStringArray(t *testing.T) {
|
func TestMultiReturnWithStringArray(t *testing.T) {
|
||||||
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"name": "","type": "uint256[3]"},{"name": "","type": "address"},{"name": "","type": "string[2]"},{"name": "","type": "bool"}]}]`
|
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"name": "","type": "uint256[3]"},{"name": "","type": "address"},{"name": "","type": "string[2]"},{"name": "","type": "bool"}]}]`
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buff := new(bytes.Buffer)
|
buff := new(bytes.Buffer)
|
||||||
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000005c1b78ea0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000001a055690d9db80000000000000000000000000000ab1257528b3782fb40d7ed5f72e624b744dffb2f00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001048656c6c6f2c20457468657265756d2100000000000000000000000000000000"))
|
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000005c1b78ea0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000001a055690d9db80000000000000000000000000000ab1257528b3782fb40d7ed5f72e624b744dffb2f00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001048656c6c6f2c20457468657265756d2100000000000000000000000000000000"))
|
||||||
temp, _ := big.NewInt(0).SetString("30000000000000000000", 10)
|
|
||||||
|
temp, _ := new(big.Int).SetString("30000000000000000000", 10)
|
||||||
ret1, ret1Exp := new([3]*big.Int), [3]*big.Int{big.NewInt(1545304298), big.NewInt(6), temp}
|
ret1, ret1Exp := new([3]*big.Int), [3]*big.Int{big.NewInt(1545304298), big.NewInt(6), temp}
|
||||||
ret2, ret2Exp := new(common.Address), common.HexToAddress("ab1257528b3782fb40d7ed5f72e624b744dffb2f")
|
ret2, ret2Exp := new(common.Address), common.HexToAddress("ab1257528b3782fb40d7ed5f72e624b744dffb2f")
|
||||||
ret3, ret3Exp := new([2]string), [2]string{"Ethereum", "Hello, Ethereum!"}
|
ret3, ret3Exp := new([2]string), [2]string{"Ethereum", "Hello, Ethereum!"}
|
||||||
ret4, ret4Exp := new(bool), false
|
ret4, ret4Exp := new(bool), false
|
||||||
|
|
||||||
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2, ret3, ret4}, "multi", buff.Bytes()); err != nil {
|
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2, ret3, ret4}, "multi", buff.Bytes()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
||||||
t.Error("big.Int array result", *ret1, "!= Expected", ret1Exp)
|
t.Error("big.Int array result", *ret1, "!= Expected", ret1Exp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(*ret2, ret2Exp) {
|
if !reflect.DeepEqual(*ret2, ret2Exp) {
|
||||||
t.Error("address result", *ret2, "!= Expected", ret2Exp)
|
t.Error("address result", *ret2, "!= Expected", ret2Exp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(*ret3, ret3Exp) {
|
if !reflect.DeepEqual(*ret3, ret3Exp) {
|
||||||
t.Error("string array result", *ret3, "!= Expected", ret3Exp)
|
t.Error("string array result", *ret3, "!= Expected", ret3Exp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(*ret4, ret4Exp) {
|
if !reflect.DeepEqual(*ret4, ret4Exp) {
|
||||||
t.Error("bool result", *ret4, "!= Expected", ret4Exp)
|
t.Error("bool result", *ret4, "!= Expected", ret4Exp)
|
||||||
}
|
}
|
||||||
|
|
@ -431,10 +488,12 @@ func TestMultiReturnWithStringArray(t *testing.T) {
|
||||||
|
|
||||||
func TestMultiReturnWithStringSlice(t *testing.T) {
|
func TestMultiReturnWithStringSlice(t *testing.T) {
|
||||||
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"name": "","type": "string[]"},{"name": "","type": "uint256[]"}]}]`
|
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"name": "","type": "string[]"},{"name": "","type": "uint256[]"}]}]`
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buff := new(bytes.Buffer)
|
buff := new(bytes.Buffer)
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) // output[0] offset
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) // output[0] offset
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000120")) // output[1] offset
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000120")) // output[1] offset
|
||||||
|
|
@ -448,14 +507,18 @@ func TestMultiReturnWithStringSlice(t *testing.T) {
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // output[1] length
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // output[1] length
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000064")) // output[1][0] value
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000064")) // output[1][0] value
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000065")) // output[1][1] value
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000065")) // output[1][1] value
|
||||||
|
|
||||||
ret1, ret1Exp := new([]string), []string{"ethereum", "go-ethereum"}
|
ret1, ret1Exp := new([]string), []string{"ethereum", "go-ethereum"}
|
||||||
ret2, ret2Exp := new([]*big.Int), []*big.Int{big.NewInt(100), big.NewInt(101)}
|
ret2, ret2Exp := new([]*big.Int), []*big.Int{big.NewInt(100), big.NewInt(101)}
|
||||||
|
|
||||||
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
|
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
||||||
t.Error("string slice result", *ret1, "!= Expected", ret1Exp)
|
t.Error("string slice result", *ret1, "!= Expected", ret1Exp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(*ret2, ret2Exp) {
|
if !reflect.DeepEqual(*ret2, ret2Exp) {
|
||||||
t.Error("uint256 slice result", *ret2, "!= Expected", ret2Exp)
|
t.Error("uint256 slice result", *ret2, "!= Expected", ret2Exp)
|
||||||
}
|
}
|
||||||
|
|
@ -467,10 +530,12 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
|
||||||
// after such nested array argument should be read with the correct offset,
|
// after such nested array argument should be read with the correct offset,
|
||||||
// so that it does not read content from the previous array argument.
|
// so that it does not read content from the previous array argument.
|
||||||
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"type": "uint64[3][2][4]"}, {"type": "uint64"}]}]`
|
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"type": "uint64[3][2][4]"}, {"type": "uint64"}]}]`
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buff := new(bytes.Buffer)
|
buff := new(bytes.Buffer)
|
||||||
// construct the test array, each 3 char element is joined with 61 '0' chars,
|
// construct the test array, each 3 char element is joined with 61 '0' chars,
|
||||||
// to from the ((3 + 61) * 0.5) = 32 byte elements in the array.
|
// to from the ((3 + 61) * 0.5) = 32 byte elements in the array.
|
||||||
|
|
@ -490,12 +555,15 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
|
||||||
{{0x411, 0x412, 0x413}, {0x421, 0x422, 0x423}},
|
{{0x411, 0x412, 0x413}, {0x421, 0x422, 0x423}},
|
||||||
}
|
}
|
||||||
ret2, ret2Exp := new(uint64), uint64(0x9876)
|
ret2, ret2Exp := new(uint64), uint64(0x9876)
|
||||||
|
|
||||||
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
|
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
||||||
t.Error("array result", *ret1, "!= Expected", ret1Exp)
|
t.Error("array result", *ret1, "!= Expected", ret1Exp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if *ret2 != ret2Exp {
|
if *ret2 != ret2Exp {
|
||||||
t.Error("int result", *ret2, "!= Expected", ret2Exp)
|
t.Error("int result", *ret2, "!= Expected", ret2Exp)
|
||||||
}
|
}
|
||||||
|
|
@ -517,6 +585,7 @@ func TestUnmarshal(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buff := new(bytes.Buffer)
|
buff := new(bytes.Buffer)
|
||||||
|
|
||||||
// marshall mixed bytes (mixedBytes)
|
// marshall mixed bytes (mixedBytes)
|
||||||
|
|
@ -544,6 +613,7 @@ func TestUnmarshal(t *testing.T) {
|
||||||
|
|
||||||
// marshal int
|
// marshal int
|
||||||
var Int *big.Int
|
var Int *big.Int
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
|
err = abi.UnpackIntoInterface(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -555,6 +625,7 @@ func TestUnmarshal(t *testing.T) {
|
||||||
|
|
||||||
// marshal bool
|
// marshal bool
|
||||||
var Bool bool
|
var Bool bool
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&Bool, "bool", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
|
err = abi.UnpackIntoInterface(&Bool, "bool", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -572,6 +643,7 @@ func TestUnmarshal(t *testing.T) {
|
||||||
buff.Write(bytesOut)
|
buff.Write(bytesOut)
|
||||||
|
|
||||||
var Bytes []byte
|
var Bytes []byte
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes())
|
err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -639,6 +711,7 @@ func TestUnmarshal(t *testing.T) {
|
||||||
buff.Write(common.RightPadBytes([]byte("hello"), 32))
|
buff.Write(common.RightPadBytes([]byte("hello"), 32))
|
||||||
|
|
||||||
var hash common.Hash
|
var hash common.Hash
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&hash, "fixed", buff.Bytes())
|
err = abi.UnpackIntoInterface(&hash, "fixed", buff.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
@ -652,6 +725,7 @@ func TestUnmarshal(t *testing.T) {
|
||||||
// marshal error
|
// marshal error
|
||||||
buff.Reset()
|
buff.Reset()
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes())
|
err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("expected error")
|
t.Error("expected error")
|
||||||
|
|
@ -668,10 +742,12 @@ func TestUnmarshal(t *testing.T) {
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003"))
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003"))
|
||||||
// marshal int array
|
// marshal int array
|
||||||
var intArray [3]*big.Int
|
var intArray [3]*big.Int
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&intArray, "intArraySingle", buff.Bytes())
|
err = abi.UnpackIntoInterface(&intArray, "intArraySingle", buff.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var testAgainstIntArray [3]*big.Int
|
var testAgainstIntArray [3]*big.Int
|
||||||
testAgainstIntArray[0] = big.NewInt(1)
|
testAgainstIntArray[0] = big.NewInt(1)
|
||||||
testAgainstIntArray[1] = big.NewInt(2)
|
testAgainstIntArray[1] = big.NewInt(2)
|
||||||
|
|
@ -689,6 +765,7 @@ func TestUnmarshal(t *testing.T) {
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"))
|
buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"))
|
||||||
|
|
||||||
var outAddr []common.Address
|
var outAddr []common.Address
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&outAddr, "addressSliceSingle", buff.Bytes())
|
err = abi.UnpackIntoInterface(&outAddr, "addressSliceSingle", buff.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("didn't expect error:", err)
|
t.Fatal("didn't expect error:", err)
|
||||||
|
|
@ -716,6 +793,7 @@ func TestUnmarshal(t *testing.T) {
|
||||||
A []common.Address
|
A []common.Address
|
||||||
B []common.Address
|
B []common.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
err = abi.UnpackIntoInterface(&outAddrStruct, "addressSliceDouble", buff.Bytes())
|
err = abi.UnpackIntoInterface(&outAddrStruct, "addressSliceDouble", buff.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal("didn't expect error:", err)
|
t.Fatal("didn't expect error:", err)
|
||||||
|
|
@ -736,6 +814,7 @@ func TestUnmarshal(t *testing.T) {
|
||||||
if outAddrStruct.B[0] != (common.Address{2}) {
|
if outAddrStruct.B[0] != (common.Address{2}) {
|
||||||
t.Errorf("expected %x, got %x", common.Address{2}, outAddrStruct.B[0])
|
t.Errorf("expected %x, got %x", common.Address{2}, outAddrStruct.B[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
if outAddrStruct.B[1] != (common.Address{3}) {
|
if outAddrStruct.B[1] != (common.Address{3}) {
|
||||||
t.Errorf("expected %x, got %x", common.Address{3}, outAddrStruct.B[1])
|
t.Errorf("expected %x, got %x", common.Address{3}, outAddrStruct.B[1])
|
||||||
}
|
}
|
||||||
|
|
@ -752,10 +831,12 @@ func TestUnmarshal(t *testing.T) {
|
||||||
|
|
||||||
func TestUnpackTuple(t *testing.T) {
|
func TestUnpackTuple(t *testing.T) {
|
||||||
const simpleTuple = `[{"name":"tuple","type":"function","outputs":[{"type":"tuple","name":"ret","components":[{"type":"int256","name":"a"},{"type":"int256","name":"b"}]}]}]`
|
const simpleTuple = `[{"name":"tuple","type":"function","outputs":[{"type":"tuple","name":"ret","components":[{"type":"int256","name":"a"},{"type":"int256","name":"b"}]}]}]`
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(simpleTuple))
|
abi, err := JSON(strings.NewReader(simpleTuple))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buff := new(bytes.Buffer)
|
buff := new(bytes.Buffer)
|
||||||
|
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // ret[a] = 1
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // ret[a] = 1
|
||||||
|
|
@ -766,9 +847,11 @@ func TestUnpackTuple(t *testing.T) {
|
||||||
A *big.Int
|
A *big.Int
|
||||||
B *big.Int
|
B *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
type r struct {
|
type r struct {
|
||||||
Result v
|
Result v
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret0 = new(r)
|
var ret0 = new(r)
|
||||||
err = abi.UnpackIntoInterface(ret0, "tuple", buff.Bytes())
|
err = abi.UnpackIntoInterface(ret0, "tuple", buff.Bytes())
|
||||||
|
|
||||||
|
|
@ -778,6 +861,7 @@ func TestUnpackTuple(t *testing.T) {
|
||||||
if ret0.Result.A.Cmp(big.NewInt(1)) != 0 {
|
if ret0.Result.A.Cmp(big.NewInt(1)) != 0 {
|
||||||
t.Errorf("unexpected value unpacked: want %x, got %x", 1, ret0.Result.A)
|
t.Errorf("unexpected value unpacked: want %x, got %x", 1, ret0.Result.A)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ret0.Result.B.Cmp(big.NewInt(-1)) != 0 {
|
if ret0.Result.B.Cmp(big.NewInt(-1)) != 0 {
|
||||||
t.Errorf("unexpected value unpacked: want %x, got %x", -1, ret0.Result.B)
|
t.Errorf("unexpected value unpacked: want %x, got %x", -1, ret0.Result.B)
|
||||||
}
|
}
|
||||||
|
|
@ -794,6 +878,7 @@ func TestUnpackTuple(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buff.Reset()
|
buff.Reset()
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000080")) // s offset
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000080")) // s offset
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")) // t.X = 0
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")) // t.X = 0
|
||||||
|
|
@ -827,7 +912,9 @@ func TestUnpackTuple(t *testing.T) {
|
||||||
FieldT T `abi:"t"`
|
FieldT T `abi:"t"`
|
||||||
A *big.Int
|
A *big.Int
|
||||||
}
|
}
|
||||||
|
|
||||||
var ret Ret
|
var ret Ret
|
||||||
|
|
||||||
var expected = Ret{
|
var expected = Ret{
|
||||||
FieldS: S{
|
FieldS: S{
|
||||||
A: big.NewInt(1),
|
A: big.NewInt(1),
|
||||||
|
|
@ -847,6 +934,7 @@ func TestUnpackTuple(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if reflect.DeepEqual(ret, expected) {
|
if reflect.DeepEqual(ret, expected) {
|
||||||
t.Error("unexpected unpack value")
|
t.Error("unexpected unpack value")
|
||||||
}
|
}
|
||||||
|
|
@ -908,16 +996,193 @@ func TestOOMMaliciousInput(t *testing.T) {
|
||||||
for i, test := range oomTests {
|
for i, test := range oomTests {
|
||||||
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
|
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
|
||||||
abi, err := JSON(strings.NewReader(def))
|
abi, err := JSON(strings.NewReader(def))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
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" + test.enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = abi.Methods["method"].Outputs.UnpackValues(encb)
|
_, err = abi.Methods["method"].Outputs.UnpackValues(encb)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("Expected error on malicious input, test %d", i)
|
t.Fatalf("Expected error on malicious input, test %d", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPackAndUnpackIncompatibleNumber(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
var encodeABI Arguments
|
||||||
|
|
||||||
|
uint256Ty, err := NewType("uint256", "", nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
encodeABI = Arguments{
|
||||||
|
{Type: uint256Ty},
|
||||||
|
}
|
||||||
|
|
||||||
|
maxU64, ok := new(big.Int).SetString(strconv.FormatUint(math.MaxUint64, 10), 10)
|
||||||
|
if !ok {
|
||||||
|
panic("bug")
|
||||||
|
}
|
||||||
|
|
||||||
|
maxU64Plus1 := new(big.Int).Add(maxU64, big.NewInt(1))
|
||||||
|
cases := []struct {
|
||||||
|
decodeType string
|
||||||
|
inputValue *big.Int
|
||||||
|
err error
|
||||||
|
expectValue interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
decodeType: "uint8",
|
||||||
|
inputValue: big.NewInt(math.MaxUint8 + 1),
|
||||||
|
err: errBadUint8,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "uint8",
|
||||||
|
inputValue: big.NewInt(math.MaxUint8),
|
||||||
|
err: nil,
|
||||||
|
expectValue: uint8(math.MaxUint8),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "uint16",
|
||||||
|
inputValue: big.NewInt(math.MaxUint16 + 1),
|
||||||
|
err: errBadUint16,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "uint16",
|
||||||
|
inputValue: big.NewInt(math.MaxUint16),
|
||||||
|
err: nil,
|
||||||
|
expectValue: uint16(math.MaxUint16),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "uint32",
|
||||||
|
inputValue: big.NewInt(math.MaxUint32 + 1),
|
||||||
|
err: errBadUint32,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "uint32",
|
||||||
|
inputValue: big.NewInt(math.MaxUint32),
|
||||||
|
err: nil,
|
||||||
|
expectValue: uint32(math.MaxUint32),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "uint64",
|
||||||
|
inputValue: maxU64Plus1,
|
||||||
|
err: errBadUint64,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "uint64",
|
||||||
|
inputValue: maxU64,
|
||||||
|
err: nil,
|
||||||
|
expectValue: uint64(math.MaxUint64),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "uint256",
|
||||||
|
inputValue: maxU64Plus1,
|
||||||
|
err: nil,
|
||||||
|
expectValue: maxU64Plus1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "int8",
|
||||||
|
inputValue: big.NewInt(math.MaxInt8 + 1),
|
||||||
|
err: errBadInt8,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "int8",
|
||||||
|
inputValue: big.NewInt(math.MinInt8 - 1),
|
||||||
|
err: errBadInt8,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "int8",
|
||||||
|
inputValue: big.NewInt(math.MaxInt8),
|
||||||
|
err: nil,
|
||||||
|
expectValue: int8(math.MaxInt8),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "int16",
|
||||||
|
inputValue: big.NewInt(math.MaxInt16 + 1),
|
||||||
|
err: errBadInt16,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "int16",
|
||||||
|
inputValue: big.NewInt(math.MinInt16 - 1),
|
||||||
|
err: errBadInt16,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "int16",
|
||||||
|
inputValue: big.NewInt(math.MaxInt16),
|
||||||
|
err: nil,
|
||||||
|
expectValue: int16(math.MaxInt16),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "int32",
|
||||||
|
inputValue: big.NewInt(math.MaxInt32 + 1),
|
||||||
|
err: errBadInt32,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "int32",
|
||||||
|
inputValue: big.NewInt(math.MinInt32 - 1),
|
||||||
|
err: errBadInt32,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "int32",
|
||||||
|
inputValue: big.NewInt(math.MaxInt32),
|
||||||
|
err: nil,
|
||||||
|
expectValue: int32(math.MaxInt32),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "int64",
|
||||||
|
inputValue: new(big.Int).Add(big.NewInt(math.MaxInt64), big.NewInt(1)),
|
||||||
|
err: errBadInt64,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "int64",
|
||||||
|
inputValue: new(big.Int).Sub(big.NewInt(math.MinInt64), big.NewInt(1)),
|
||||||
|
err: errBadInt64,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
decodeType: "int64",
|
||||||
|
inputValue: big.NewInt(math.MaxInt64),
|
||||||
|
err: nil,
|
||||||
|
expectValue: int64(math.MaxInt64),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, testCase := range cases {
|
||||||
|
packed, err := encodeABI.Pack(testCase.inputValue)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ty, err := NewType(testCase.decodeType, "", nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
decodeABI := Arguments{
|
||||||
|
{Type: ty},
|
||||||
|
}
|
||||||
|
decoded, err := decodeABI.Unpack(packed)
|
||||||
|
|
||||||
|
if err != testCase.err {
|
||||||
|
t.Fatalf("Expected error %v, actual error %v. case %d", testCase.err, err, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(decoded[0], testCase.expectValue) {
|
||||||
|
t.Fatalf("Expected value %v, actual value %v", testCase.expectValue, decoded[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
42
accounts/abi/utils.go
Normal file
42
accounts/abi/utils.go
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright 2022 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package abi
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// ResolveNameConflict returns the next available name for a given thing.
|
||||||
|
// This helper can be used for lots of purposes:
|
||||||
|
//
|
||||||
|
// - In solidity function overloading is supported, this function can fix
|
||||||
|
// the name conflicts of overloaded functions.
|
||||||
|
// - In golang binding generation, the parameter(in function, event, error,
|
||||||
|
// and struct definition) name will be converted to camelcase style which
|
||||||
|
// may eventually lead to name conflicts.
|
||||||
|
//
|
||||||
|
// Name conflicts are mostly resolved by adding number suffix. e.g. if the abi contains
|
||||||
|
// Methods "send" and "send1", ResolveNameConflict would return "send2" for input "send".
|
||||||
|
func ResolveNameConflict(rawName string, used func(string) bool) string {
|
||||||
|
name := rawName
|
||||||
|
ok := used(name)
|
||||||
|
|
||||||
|
for idx := 0; ok; idx++ {
|
||||||
|
name = fmt.Sprintf("%s%d", rawName, idx)
|
||||||
|
ok = used(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
@ -178,6 +178,7 @@ type Backend interface {
|
||||||
// safely used to calculate a signature from.
|
// safely used to calculate a signature from.
|
||||||
//
|
//
|
||||||
// The hash is calculated as
|
// The hash is calculated as
|
||||||
|
//
|
||||||
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
|
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
|
||||||
//
|
//
|
||||||
// This gives context to the signed message and prevents signing of transactions.
|
// This gives context to the signed message and prevents signing of transactions.
|
||||||
|
|
@ -190,6 +191,7 @@ func TextHash(data []byte) []byte {
|
||||||
// safely used to calculate a signature from.
|
// safely used to calculate a signature from.
|
||||||
//
|
//
|
||||||
// The hash is calculated as
|
// The hash is calculated as
|
||||||
|
//
|
||||||
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
|
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
|
||||||
//
|
//
|
||||||
// This gives context to the signed message and prevents signing of transactions.
|
// This gives context to the signed message and prevents signing of transactions.
|
||||||
|
|
@ -197,6 +199,7 @@ func TextAndHash(data []byte) ([]byte, string) {
|
||||||
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), string(data))
|
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), string(data))
|
||||||
hasher := sha3.NewLegacyKeccak256()
|
hasher := sha3.NewLegacyKeccak256()
|
||||||
hasher.Write([]byte(msg))
|
hasher.Write([]byte(msg))
|
||||||
|
|
||||||
return hasher.Sum(nil), msg
|
return hasher.Sum(nil), msg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
func TestTextHash(t *testing.T) {
|
func TestTextHash(t *testing.T) {
|
||||||
hash := TextHash([]byte("Hello Joe"))
|
hash := TextHash([]byte("Hello Joe"))
|
||||||
want := hexutil.MustDecode("0xa080337ae51c4e064c189e113edd0ba391df9206e2f49db658bb32cf2911730b")
|
want := hexutil.MustDecode("0xa080337ae51c4e064c189e113edd0ba391df9206e2f49db658bb32cf2911730b")
|
||||||
|
|
||||||
if !bytes.Equal(hash, want) {
|
if !bytes.Equal(hash, want) {
|
||||||
t.Fatalf("wrong hash: %x", hash)
|
t.Fatalf("wrong hash: %x", hash)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,7 @@ var ErrInvalidPassphrase = errors.New("invalid password")
|
||||||
// second time.
|
// second time.
|
||||||
var ErrWalletAlreadyOpen = errors.New("wallet already open")
|
var ErrWalletAlreadyOpen = errors.New("wallet already open")
|
||||||
|
|
||||||
// ErrWalletClosed is returned if a wallet is attempted to be opened the
|
// ErrWalletClosed is returned if a wallet is offline.
|
||||||
// second time.
|
|
||||||
var ErrWalletClosed = errors.New("wallet closed")
|
var ErrWalletClosed = errors.New("wallet closed")
|
||||||
|
|
||||||
// AuthNeededError is returned by backends for signing requests where the user
|
// AuthNeededError is returned by backends for signing requests where the user
|
||||||
|
|
|
||||||
29
accounts/external/backend.go
vendored
29
accounts/external/backend.go
vendored
|
|
@ -45,6 +45,7 @@ func NewExternalBackend(endpoint string) (*ExternalBackend, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ExternalBackend{
|
return &ExternalBackend{
|
||||||
signers: []accounts.Wallet{signer},
|
signers: []accounts.Wallet{signer},
|
||||||
}, nil
|
}, nil
|
||||||
|
|
@ -73,6 +74,7 @@ func NewExternalSigner(endpoint string) (*ExternalSigner, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
extsigner := &ExternalSigner{
|
extsigner := &ExternalSigner{
|
||||||
client: client,
|
client: client,
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
|
|
@ -82,7 +84,9 @@ func NewExternalSigner(endpoint string) (*ExternalSigner, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
extsigner.status = fmt.Sprintf("ok [version=%v]", version)
|
extsigner.status = fmt.Sprintf("ok [version=%v]", version)
|
||||||
|
|
||||||
return extsigner, nil
|
return extsigner, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,11 +111,13 @@ func (api *ExternalSigner) Close() error {
|
||||||
|
|
||||||
func (api *ExternalSigner) Accounts() []accounts.Account {
|
func (api *ExternalSigner) Accounts() []accounts.Account {
|
||||||
var accnts []accounts.Account
|
var accnts []accounts.Account
|
||||||
|
|
||||||
res, err := api.listAccounts()
|
res, err := api.listAccounts()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("account listing failed", "error", err)
|
log.Error("account listing failed", "error", err)
|
||||||
return accnts
|
return accnts
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, addr := range res {
|
for _, addr := range res {
|
||||||
accnts = append(accnts, accounts.Account{
|
accnts = append(accnts, accounts.Account{
|
||||||
URL: accounts.URL{
|
URL: accounts.URL{
|
||||||
|
|
@ -121,26 +127,31 @@ func (api *ExternalSigner) Accounts() []accounts.Account {
|
||||||
Address: addr,
|
Address: addr,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
api.cacheMu.Lock()
|
api.cacheMu.Lock()
|
||||||
api.cache = accnts
|
api.cache = accnts
|
||||||
api.cacheMu.Unlock()
|
api.cacheMu.Unlock()
|
||||||
|
|
||||||
return accnts
|
return accnts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) Contains(account accounts.Account) bool {
|
func (api *ExternalSigner) Contains(account accounts.Account) bool {
|
||||||
api.cacheMu.RLock()
|
api.cacheMu.RLock()
|
||||||
defer api.cacheMu.RUnlock()
|
defer api.cacheMu.RUnlock()
|
||||||
|
|
||||||
if api.cache == nil {
|
if api.cache == nil {
|
||||||
// If we haven't already fetched the accounts, it's time to do so now
|
// If we haven't already fetched the accounts, it's time to do so now
|
||||||
api.cacheMu.RUnlock()
|
api.cacheMu.RUnlock()
|
||||||
api.Accounts()
|
api.Accounts()
|
||||||
api.cacheMu.RLock()
|
api.cacheMu.RLock()
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, a := range api.cache {
|
for _, a := range api.cache {
|
||||||
if a.Address == account.Address && (account.URL == (accounts.URL{}) || account.URL == api.URL()) {
|
if a.Address == account.Address && (account.URL == (accounts.URL{}) || account.URL == api.URL()) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,13 +163,10 @@ func (api *ExternalSigner) SelfDerive(bases []accounts.DerivationPath, chain eth
|
||||||
log.Error("operation SelfDerive not supported on external signers")
|
log.Error("operation SelfDerive not supported on external signers")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) signHash(account accounts.Account, hash []byte) ([]byte, error) {
|
|
||||||
return []byte{}, fmt.Errorf("operation not supported on external signers")
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed
|
// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed
|
||||||
func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
|
func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
|
||||||
var res hexutil.Bytes
|
var res hexutil.Bytes
|
||||||
|
|
||||||
var signAddress = common.NewMixedcaseAddress(account.Address)
|
var signAddress = common.NewMixedcaseAddress(account.Address)
|
||||||
if err := api.client.Call(&res, "account_signData",
|
if err := api.client.Call(&res, "account_signData",
|
||||||
mimeType,
|
mimeType,
|
||||||
|
|
@ -170,11 +178,13 @@ func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, d
|
||||||
if mimeType == accounts.MimetypeClique && (res[64] == 27 || res[64] == 28) {
|
if mimeType == accounts.MimetypeClique && (res[64] == 27 || res[64] == 28) {
|
||||||
res[64] -= 27 // Transform V from 27/28 to 0/1 for Clique use
|
res[64] -= 27 // Transform V from 27/28 to 0/1 for Clique use
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]byte, error) {
|
func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]byte, error) {
|
||||||
var signature hexutil.Bytes
|
var signature hexutil.Bytes
|
||||||
|
|
||||||
var signAddress = common.NewMixedcaseAddress(account.Address)
|
var signAddress = common.NewMixedcaseAddress(account.Address)
|
||||||
if err := api.client.Call(&signature, "account_signData",
|
if err := api.client.Call(&signature, "account_signData",
|
||||||
accounts.MimetypeTextPlain,
|
accounts.MimetypeTextPlain,
|
||||||
|
|
@ -182,11 +192,13 @@ func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]by
|
||||||
hexutil.Encode(text)); err != nil {
|
hexutil.Encode(text)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if signature[64] == 27 || signature[64] == 28 {
|
if signature[64] == 27 || signature[64] == 28 {
|
||||||
// If clef is used as a backend, it may already have transformed
|
// If clef is used as a backend, it may already have transformed
|
||||||
// the signature to ethereum-type signature.
|
// the signature to ethereum-type signature.
|
||||||
signature[64] -= 27 // Transform V from Ethereum-legacy to 0/1
|
signature[64] -= 27 // Transform V from Ethereum-legacy to 0/1
|
||||||
}
|
}
|
||||||
|
|
||||||
return signature, nil
|
return signature, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -202,11 +214,14 @@ type signTransactionResult struct {
|
||||||
// transaction overrides the chainID parameter.
|
// transaction overrides the chainID parameter.
|
||||||
func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||||
data := hexutil.Bytes(tx.Data())
|
data := hexutil.Bytes(tx.Data())
|
||||||
|
|
||||||
var to *common.MixedcaseAddress
|
var to *common.MixedcaseAddress
|
||||||
|
|
||||||
if tx.To() != nil {
|
if tx.To() != nil {
|
||||||
t := common.NewMixedcaseAddress(*tx.To())
|
t := common.NewMixedcaseAddress(*tx.To())
|
||||||
to = &t
|
to = &t
|
||||||
}
|
}
|
||||||
|
|
||||||
args := &apitypes.SendTxArgs{
|
args := &apitypes.SendTxArgs{
|
||||||
Data: &data,
|
Data: &data,
|
||||||
Nonce: hexutil.Uint64(tx.Nonce()),
|
Nonce: hexutil.Uint64(tx.Nonce()),
|
||||||
|
|
@ -229,19 +244,23 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
|
||||||
if chainID != nil && chainID.Sign() != 0 {
|
if chainID != nil && chainID.Sign() != 0 {
|
||||||
args.ChainID = (*hexutil.Big)(chainID)
|
args.ChainID = (*hexutil.Big)(chainID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tx.Type() != types.LegacyTxType {
|
if tx.Type() != types.LegacyTxType {
|
||||||
// However, if the user asked for a particular chain id, then we should
|
// However, if the user asked for a particular chain id, then we should
|
||||||
// use that instead.
|
// use that instead.
|
||||||
if tx.ChainId().Sign() != 0 {
|
if tx.ChainId().Sign() != 0 {
|
||||||
args.ChainID = (*hexutil.Big)(tx.ChainId())
|
args.ChainID = (*hexutil.Big)(tx.ChainId())
|
||||||
}
|
}
|
||||||
|
|
||||||
accessList := tx.AccessList()
|
accessList := tx.AccessList()
|
||||||
args.AccessList = &accessList
|
args.AccessList = &accessList
|
||||||
}
|
}
|
||||||
|
|
||||||
var res signTransactionResult
|
var res signTransactionResult
|
||||||
if err := api.client.Call(&res, "account_signTransaction", args); err != nil {
|
if err := api.client.Call(&res, "account_signTransaction", args); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.Tx, nil
|
return res.Tx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -261,6 +280,7 @@ func (api *ExternalSigner) listAccounts() ([]common.Address, error) {
|
||||||
if err := api.client.Call(&res, "account_list"); err != nil {
|
if err := api.client.Call(&res, "account_list"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -269,5 +289,6 @@ func (api *ExternalSigner) pingVersion() (string, error) {
|
||||||
if err := api.client.Call(&v, "account_version"); err != nil {
|
if err := api.client.Call(&v, "account_version"); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ var DefaultBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60,
|
||||||
var LegacyLedgerBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}
|
var LegacyLedgerBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}
|
||||||
|
|
||||||
// DerivationPath represents the computer friendly version of a hierarchical
|
// DerivationPath represents the computer friendly version of a hierarchical
|
||||||
// deterministic wallet account derivaion path.
|
// deterministic wallet account derivation path.
|
||||||
//
|
//
|
||||||
// The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
// The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||||
// defines derivation paths to be of the form:
|
// defines derivation paths to be of the form:
|
||||||
|
|
@ -70,6 +70,7 @@ func ParseDerivationPath(path string) (DerivationPath, error) {
|
||||||
|
|
||||||
// Handle absolute or relative paths
|
// Handle absolute or relative paths
|
||||||
components := strings.Split(path, "/")
|
components := strings.Split(path, "/")
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case len(components) == 0:
|
case len(components) == 0:
|
||||||
return nil, errors.New("empty derivation path")
|
return nil, errors.New("empty derivation path")
|
||||||
|
|
@ -87,9 +88,11 @@ func ParseDerivationPath(path string) (DerivationPath, error) {
|
||||||
if len(components) == 0 {
|
if len(components) == 0 {
|
||||||
return nil, errors.New("empty derivation path") // Empty relative paths
|
return nil, errors.New("empty derivation path") // Empty relative paths
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, component := range components {
|
for _, component := range components {
|
||||||
// Ignore any user added whitespace
|
// Ignore any user added whitespace
|
||||||
component = strings.TrimSpace(component)
|
component = strings.TrimSpace(component)
|
||||||
|
|
||||||
var value uint32
|
var value uint32
|
||||||
|
|
||||||
// Handle hardened paths
|
// Handle hardened paths
|
||||||
|
|
@ -102,18 +105,22 @@ func ParseDerivationPath(path string) (DerivationPath, error) {
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("invalid component: %s", component)
|
return nil, fmt.Errorf("invalid component: %s", component)
|
||||||
}
|
}
|
||||||
|
|
||||||
max := math.MaxUint32 - value
|
max := math.MaxUint32 - value
|
||||||
if bigval.Sign() < 0 || bigval.Cmp(big.NewInt(int64(max))) > 0 {
|
if bigval.Sign() < 0 || bigval.Cmp(big.NewInt(int64(max))) > 0 {
|
||||||
if value == 0 {
|
if value == 0 {
|
||||||
return nil, fmt.Errorf("component %v out of allowed range [0, %d]", bigval, max)
|
return nil, fmt.Errorf("component %v out of allowed range [0, %d]", bigval, max)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("component %v out of allowed hardened range [0, %d]", bigval, max)
|
return nil, fmt.Errorf("component %v out of allowed hardened range [0, %d]", bigval, max)
|
||||||
}
|
}
|
||||||
|
|
||||||
value += uint32(bigval.Uint64())
|
value += uint32(bigval.Uint64())
|
||||||
|
|
||||||
// Append and repeat
|
// Append and repeat
|
||||||
result = append(result, value)
|
result = append(result, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,17 +128,21 @@ func ParseDerivationPath(path string) (DerivationPath, error) {
|
||||||
// to its canonical representation.
|
// to its canonical representation.
|
||||||
func (path DerivationPath) String() string {
|
func (path DerivationPath) String() string {
|
||||||
result := "m"
|
result := "m"
|
||||||
|
|
||||||
for _, component := range path {
|
for _, component := range path {
|
||||||
var hardened bool
|
var hardened bool
|
||||||
|
|
||||||
if component >= 0x80000000 {
|
if component >= 0x80000000 {
|
||||||
component -= 0x80000000
|
component -= 0x80000000
|
||||||
hardened = true
|
hardened = true
|
||||||
}
|
}
|
||||||
|
|
||||||
result = fmt.Sprintf("%s/%d", result, component)
|
result = fmt.Sprintf("%s/%d", result, component)
|
||||||
if hardened {
|
if hardened {
|
||||||
result += "'"
|
result += "'"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,11 +154,14 @@ func (path DerivationPath) MarshalJSON() ([]byte, error) {
|
||||||
// UnmarshalJSON a json-serialized string back into a derivation path
|
// UnmarshalJSON a json-serialized string back into a derivation path
|
||||||
func (path *DerivationPath) UnmarshalJSON(b []byte) error {
|
func (path *DerivationPath) UnmarshalJSON(b []byte) error {
|
||||||
var dp string
|
var dp string
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if err = json.Unmarshal(b, &dp); err != nil {
|
if err = json.Unmarshal(b, &dp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
*path, err = ParseDerivationPath(dp)
|
*path, err = ParseDerivationPath(dp)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,6 +172,7 @@ func DefaultIterator(base DerivationPath) func() DerivationPath {
|
||||||
copy(path[:], base[:])
|
copy(path[:], base[:])
|
||||||
// Set it back by one, so the first call gives the first result
|
// Set it back by one, so the first call gives the first result
|
||||||
path[len(path)-1]--
|
path[len(path)-1]--
|
||||||
|
|
||||||
return func() DerivationPath {
|
return func() DerivationPath {
|
||||||
path[len(path)-1]++
|
path[len(path)-1]++
|
||||||
return path
|
return path
|
||||||
|
|
@ -172,6 +187,7 @@ func LedgerLiveIterator(base DerivationPath) func() DerivationPath {
|
||||||
copy(path[:], base[:])
|
copy(path[:], base[:])
|
||||||
// Set it back by one, so the first call gives the first result
|
// Set it back by one, so the first call gives the first result
|
||||||
path[2]--
|
path[2]--
|
||||||
|
|
||||||
return func() DerivationPath {
|
return func() DerivationPath {
|
||||||
// ledgerLivePathIterator iterates on the third component
|
// ledgerLivePathIterator iterates on the third component
|
||||||
path[2]++
|
path[2]++
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ func TestHDPathParsing(t *testing.T) {
|
||||||
|
|
||||||
func testDerive(t *testing.T, next func() DerivationPath, expected []string) {
|
func testDerive(t *testing.T, next func() DerivationPath, expected []string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
for i, want := range expected {
|
for i, want := range expected {
|
||||||
if have := next(); fmt.Sprintf("%v", have) != want {
|
if have := next(); fmt.Sprintf("%v", have) != want {
|
||||||
t.Errorf("step %d, have %v, want %v", i, have, want)
|
t.Errorf("step %d, have %v, want %v", i, have, want)
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,8 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
mapset "github.com/deckarep/golang-set"
|
mapset "github.com/deckarep/golang-set/v2"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
|
@ -59,6 +60,7 @@ func (err *AmbiguousAddrError) Error() string {
|
||||||
files += ", "
|
files += ", "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("multiple keys match address (%s)", files)
|
return fmt.Sprintf("multiple keys match address (%s)", files)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,9 +81,10 @@ func newAccountCache(keydir string) (*accountCache, chan struct{}) {
|
||||||
keydir: keydir,
|
keydir: keydir,
|
||||||
byAddr: make(map[common.Address][]accounts.Account),
|
byAddr: make(map[common.Address][]accounts.Account),
|
||||||
notify: make(chan struct{}, 1),
|
notify: make(chan struct{}, 1),
|
||||||
fileC: fileCache{all: mapset.NewThreadUnsafeSet()},
|
fileC: fileCache{all: mapset.NewThreadUnsafeSet[string]()},
|
||||||
}
|
}
|
||||||
ac.watcher = newWatcher(ac)
|
ac.watcher = newWatcher(ac)
|
||||||
|
|
||||||
return ac, ac.notify
|
return ac, ac.notify
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,6 +94,7 @@ func (ac *accountCache) accounts() []accounts.Account {
|
||||||
defer ac.mu.Unlock()
|
defer ac.mu.Unlock()
|
||||||
cpy := make([]accounts.Account, len(ac.all))
|
cpy := make([]accounts.Account, len(ac.all))
|
||||||
copy(cpy, ac.all)
|
copy(cpy, ac.all)
|
||||||
|
|
||||||
return cpy
|
return cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,6 +102,7 @@ func (ac *accountCache) hasAddress(addr common.Address) bool {
|
||||||
ac.maybeReload()
|
ac.maybeReload()
|
||||||
ac.mu.Lock()
|
ac.mu.Lock()
|
||||||
defer ac.mu.Unlock()
|
defer ac.mu.Unlock()
|
||||||
|
|
||||||
return len(ac.byAddr[addr]) > 0
|
return len(ac.byAddr[addr]) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,6 +143,7 @@ func (ac *accountCache) deleteByFile(path string) {
|
||||||
if i < len(ac.all) && ac.all[i].URL.Path == path {
|
if i < len(ac.all) && ac.all[i].URL.Path == path {
|
||||||
removed := ac.all[i]
|
removed := ac.all[i]
|
||||||
ac.all = append(ac.all[:i], ac.all[i+1:]...)
|
ac.all = append(ac.all[:i], ac.all[i+1:]...)
|
||||||
|
|
||||||
if ba := removeAccount(ac.byAddr[removed.Address], removed); len(ba) == 0 {
|
if ba := removeAccount(ac.byAddr[removed.Address], removed); len(ba) == 0 {
|
||||||
delete(ac.byAddr, removed.Address)
|
delete(ac.byAddr, removed.Address)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -146,12 +152,22 @@ func (ac *accountCache) deleteByFile(path string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// watcherStarted returns true if the watcher loop started running (even if it
|
||||||
|
// has since also ended).
|
||||||
|
func (ac *accountCache) watcherStarted() bool {
|
||||||
|
ac.mu.Lock()
|
||||||
|
defer ac.mu.Unlock()
|
||||||
|
|
||||||
|
return ac.watcher.running || ac.watcher.runEnded
|
||||||
|
}
|
||||||
|
|
||||||
func removeAccount(slice []accounts.Account, elem accounts.Account) []accounts.Account {
|
func removeAccount(slice []accounts.Account, elem accounts.Account) []accounts.Account {
|
||||||
for i := range slice {
|
for i := range slice {
|
||||||
if slice[i] == elem {
|
if slice[i] == elem {
|
||||||
return append(slice[:i], slice[i+1:]...)
|
return append(slice[:i], slice[i+1:]...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return slice
|
return slice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,20 +180,24 @@ func (ac *accountCache) find(a accounts.Account) (accounts.Account, error) {
|
||||||
if (a.Address != common.Address{}) {
|
if (a.Address != common.Address{}) {
|
||||||
matches = ac.byAddr[a.Address]
|
matches = ac.byAddr[a.Address]
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.URL.Path != "" {
|
if a.URL.Path != "" {
|
||||||
// If only the basename is specified, complete the path.
|
// If only the basename is specified, complete the path.
|
||||||
if !strings.ContainsRune(a.URL.Path, filepath.Separator) {
|
if !strings.ContainsRune(a.URL.Path, filepath.Separator) {
|
||||||
a.URL.Path = filepath.Join(ac.keydir, a.URL.Path)
|
a.URL.Path = filepath.Join(ac.keydir, a.URL.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range matches {
|
for i := range matches {
|
||||||
if matches[i].URL == a.URL {
|
if matches[i].URL == a.URL {
|
||||||
return matches[i], nil
|
return matches[i], nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.Address == common.Address{}) {
|
if (a.Address == common.Address{}) {
|
||||||
return accounts.Account{}, ErrNoMatch
|
return accounts.Account{}, ErrNoMatch
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch len(matches) {
|
switch len(matches) {
|
||||||
case 1:
|
case 1:
|
||||||
return matches[0], nil
|
return matches[0], nil
|
||||||
|
|
@ -187,6 +207,7 @@ func (ac *accountCache) find(a accounts.Account) (accounts.Account, error) {
|
||||||
err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]accounts.Account, len(matches))}
|
err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]accounts.Account, len(matches))}
|
||||||
copy(err.Matches, matches)
|
copy(err.Matches, matches)
|
||||||
sort.Sort(accountsByURL(err.Matches))
|
sort.Sort(accountsByURL(err.Matches))
|
||||||
|
|
||||||
return accounts.Account{}, err
|
return accounts.Account{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -198,6 +219,7 @@ func (ac *accountCache) maybeReload() {
|
||||||
ac.mu.Unlock()
|
ac.mu.Unlock()
|
||||||
return // A watcher is running and will keep the cache up-to-date.
|
return // A watcher is running and will keep the cache up-to-date.
|
||||||
}
|
}
|
||||||
|
|
||||||
if ac.throttle == nil {
|
if ac.throttle == nil {
|
||||||
ac.throttle = time.NewTimer(0)
|
ac.throttle = time.NewTimer(0)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -218,9 +240,11 @@ func (ac *accountCache) maybeReload() {
|
||||||
func (ac *accountCache) close() {
|
func (ac *accountCache) close() {
|
||||||
ac.mu.Lock()
|
ac.mu.Lock()
|
||||||
ac.watcher.close()
|
ac.watcher.close()
|
||||||
|
|
||||||
if ac.throttle != nil {
|
if ac.throttle != nil {
|
||||||
ac.throttle.Stop()
|
ac.throttle.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
if ac.notify != nil {
|
if ac.notify != nil {
|
||||||
close(ac.notify)
|
close(ac.notify)
|
||||||
ac.notify = nil
|
ac.notify = nil
|
||||||
|
|
@ -237,6 +261,7 @@ func (ac *accountCache) scanAccounts() error {
|
||||||
log.Debug("Failed to reload keystore contents", "err", err)
|
log.Debug("Failed to reload keystore contents", "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if creates.Cardinality() == 0 && deletes.Cardinality() == 0 && updates.Cardinality() == 0 {
|
if creates.Cardinality() == 0 && deletes.Cardinality() == 0 && updates.Cardinality() == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -247,18 +272,21 @@ func (ac *accountCache) scanAccounts() error {
|
||||||
Address string `json:"address"`
|
Address string `json:"address"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
readAccount := func(path string) *accounts.Account {
|
readAccount := func(path string) *accounts.Account {
|
||||||
fd, err := os.Open(path)
|
fd, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Trace("Failed to open keystore file", "path", path, "err", err)
|
log.Trace("Failed to open keystore file", "path", path, "err", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
buf.Reset(fd)
|
buf.Reset(fd)
|
||||||
// Parse the address.
|
// Parse the address.
|
||||||
key.Address = ""
|
key.Address = ""
|
||||||
err = json.NewDecoder(buf).Decode(&key)
|
err = json.NewDecoder(buf).Decode(&key)
|
||||||
addr := common.HexToAddress(key.Address)
|
addr := common.HexToAddress(key.Address)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
log.Debug("Failed to decode keystore key", "path", path, "err", err)
|
log.Debug("Failed to decode keystore key", "path", path, "err", err)
|
||||||
|
|
@ -270,26 +298,30 @@ func (ac *accountCache) scanAccounts() error {
|
||||||
URL: accounts.URL{Scheme: KeyStoreScheme, Path: path},
|
URL: accounts.URL{Scheme: KeyStoreScheme, Path: path},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Process all the file diffs
|
// Process all the file diffs
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
for _, p := range creates.ToSlice() {
|
for _, path := range creates.ToSlice() {
|
||||||
if a := readAccount(p.(string)); a != nil {
|
|
||||||
ac.add(*a)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, p := range deletes.ToSlice() {
|
|
||||||
ac.deleteByFile(p.(string))
|
|
||||||
}
|
|
||||||
for _, p := range updates.ToSlice() {
|
|
||||||
path := p.(string)
|
|
||||||
ac.deleteByFile(path)
|
|
||||||
if a := readAccount(path); a != nil {
|
if a := readAccount(path); a != nil {
|
||||||
ac.add(*a)
|
ac.add(*a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, path := range deletes.ToSlice() {
|
||||||
|
ac.deleteByFile(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, path := range updates.ToSlice() {
|
||||||
|
ac.deleteByFile(path)
|
||||||
|
|
||||||
|
if a := readAccount(path); a != nil {
|
||||||
|
ac.add(*a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
end := time.Now()
|
end := time.Now()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
|
@ -297,5 +329,6 @@ func (ac *accountCache) scanAccounts() error {
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
log.Trace("Handled keystore changes", "time", end.Sub(start))
|
log.Trace("Handled keystore changes", "time", end.Sub(start))
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@ package keystore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -51,16 +50,52 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// waitWatcherStarts waits up to 1s for the keystore watcher to start.
|
||||||
|
func waitWatcherStart(ks *KeyStore) bool {
|
||||||
|
// On systems where file watch is not supported, just return "ok".
|
||||||
|
if !ks.cache.watcher.enabled() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// The watcher should start, and then exit.
|
||||||
|
for t0 := time.Now(); time.Since(t0) < 1*time.Second; time.Sleep(100 * time.Millisecond) {
|
||||||
|
if ks.cache.watcherStarted() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error {
|
||||||
|
var list []accounts.Account
|
||||||
|
for t0 := time.Now(); time.Since(t0) < 5*time.Second; time.Sleep(200 * time.Millisecond) {
|
||||||
|
list = ks.Accounts()
|
||||||
|
if reflect.DeepEqual(list, wantAccounts) {
|
||||||
|
// ks should have also received change notifications
|
||||||
|
select {
|
||||||
|
case <-ks.changes:
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("wasn't notified of new accounts")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("\ngot %v\nwant %v", list, wantAccounts)
|
||||||
|
}
|
||||||
|
|
||||||
func TestWatchNewFile(t *testing.T) {
|
func TestWatchNewFile(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
dir, ks := tmpKeyStore(t, false)
|
dir, ks := tmpKeyStore(t, false)
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
// Ensure the watcher is started before adding any files.
|
// Ensure the watcher is started before adding any files.
|
||||||
ks.Accounts()
|
ks.Accounts()
|
||||||
time.Sleep(1000 * time.Millisecond)
|
|
||||||
|
|
||||||
|
if !waitWatcherStart(ks) {
|
||||||
|
t.Fatal("keystore watcher didn't start in time")
|
||||||
|
}
|
||||||
// Move in the files.
|
// Move in the files.
|
||||||
wantAccounts := make([]accounts.Account, len(cachetestAccounts))
|
wantAccounts := make([]accounts.Account, len(cachetestAccounts))
|
||||||
for i := range cachetestAccounts {
|
for i := range cachetestAccounts {
|
||||||
|
|
@ -74,28 +109,14 @@ func TestWatchNewFile(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ks should see the accounts.
|
// ks should see the accounts.
|
||||||
var list []accounts.Account
|
if err := waitForAccounts(wantAccounts, ks); err != nil {
|
||||||
for d := 200 * time.Millisecond; d < 5*time.Second; d *= 2 {
|
t.Error(err)
|
||||||
list = ks.Accounts()
|
|
||||||
if reflect.DeepEqual(list, wantAccounts) {
|
|
||||||
// ks should have also received change notifications
|
|
||||||
select {
|
|
||||||
case <-ks.changes:
|
|
||||||
default:
|
|
||||||
t.Fatalf("wasn't notified of new accounts")
|
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
time.Sleep(d)
|
|
||||||
}
|
|
||||||
t.Errorf("got %s, want %s", spew.Sdump(list), spew.Sdump(wantAccounts))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWatchNoDir(t *testing.T) {
|
func TestWatchNoDir(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create ks but not the directory that it watches.
|
// Create ks but not the directory that it watches.
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watchnodir-test-%d-%d", os.Getpid(), rand.Int()))
|
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watchnodir-test-%d-%d", os.Getpid(), rand.Int()))
|
||||||
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
||||||
|
|
||||||
|
|
@ -103,11 +124,14 @@ func TestWatchNoDir(t *testing.T) {
|
||||||
if len(list) > 0 {
|
if len(list) > 0 {
|
||||||
t.Error("initial account list not empty:", list)
|
t.Error("initial account list not empty:", list)
|
||||||
}
|
}
|
||||||
time.Sleep(100 * time.Millisecond)
|
// The watcher should start, and then exit.
|
||||||
|
if !waitWatcherStart(ks) {
|
||||||
|
t.Fatal("keystore watcher didn't start in time")
|
||||||
|
}
|
||||||
// Create the directory and copy a key file into it.
|
// Create the directory and copy a key file into it.
|
||||||
os.MkdirAll(dir, 0700)
|
os.MkdirAll(dir, 0700)
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
file := filepath.Join(dir, "aaa")
|
file := filepath.Join(dir, "aaa")
|
||||||
if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil {
|
if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -116,6 +140,7 @@ func TestWatchNoDir(t *testing.T) {
|
||||||
// ks should see the account.
|
// ks should see the account.
|
||||||
wantAccounts := []accounts.Account{cachetestAccounts[0]}
|
wantAccounts := []accounts.Account{cachetestAccounts[0]}
|
||||||
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
|
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
|
||||||
|
|
||||||
for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
|
for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
|
||||||
list = ks.Accounts()
|
list = ks.Accounts()
|
||||||
if reflect.DeepEqual(list, wantAccounts) {
|
if reflect.DeepEqual(list, wantAccounts) {
|
||||||
|
|
@ -125,8 +150,10 @@ func TestWatchNoDir(t *testing.T) {
|
||||||
default:
|
default:
|
||||||
t.Fatalf("wasn't notified of new accounts")
|
t.Fatalf("wasn't notified of new accounts")
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(d)
|
time.Sleep(d)
|
||||||
}
|
}
|
||||||
t.Errorf("\ngot %v\nwant %v", list, wantAccounts)
|
t.Errorf("\ngot %v\nwant %v", list, wantAccounts)
|
||||||
|
|
@ -134,6 +161,7 @@ func TestWatchNoDir(t *testing.T) {
|
||||||
|
|
||||||
func TestCacheInitialReload(t *testing.T) {
|
func TestCacheInitialReload(t *testing.T) {
|
||||||
cache, _ := newAccountCache(cachetestDir)
|
cache, _ := newAccountCache(cachetestDir)
|
||||||
|
|
||||||
accounts := cache.accounts()
|
accounts := cache.accounts()
|
||||||
if !reflect.DeepEqual(accounts, cachetestAccounts) {
|
if !reflect.DeepEqual(accounts, cachetestAccounts) {
|
||||||
t.Fatalf("got initial accounts: %swant %s", spew.Sdump(accounts), spew.Sdump(cachetestAccounts))
|
t.Fatalf("got initial accounts: %swant %s", spew.Sdump(accounts), spew.Sdump(cachetestAccounts))
|
||||||
|
|
@ -185,15 +213,18 @@ func TestCacheAddDeleteOrder(t *testing.T) {
|
||||||
wantAccounts := make([]accounts.Account, len(accs))
|
wantAccounts := make([]accounts.Account, len(accs))
|
||||||
copy(wantAccounts, accs)
|
copy(wantAccounts, accs)
|
||||||
sort.Sort(accountsByURL(wantAccounts))
|
sort.Sort(accountsByURL(wantAccounts))
|
||||||
|
|
||||||
list := cache.accounts()
|
list := cache.accounts()
|
||||||
if !reflect.DeepEqual(list, wantAccounts) {
|
if !reflect.DeepEqual(list, wantAccounts) {
|
||||||
t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts))
|
t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, a := range accs {
|
for _, a := range accs {
|
||||||
if !cache.hasAddress(a.Address) {
|
if !cache.hasAddress(a.Address) {
|
||||||
t.Errorf("expected hasAccount(%x) to return true", a.Address)
|
t.Errorf("expected hasAccount(%x) to return true", a.Address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cache.hasAddress(common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) {
|
if cache.hasAddress(common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) {
|
||||||
t.Errorf("expected hasAccount(%x) to return false", common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"))
|
t.Errorf("expected hasAccount(%x) to return false", common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"))
|
||||||
}
|
}
|
||||||
|
|
@ -211,14 +242,17 @@ func TestCacheAddDeleteOrder(t *testing.T) {
|
||||||
wantAccounts[5],
|
wantAccounts[5],
|
||||||
}
|
}
|
||||||
list = cache.accounts()
|
list = cache.accounts()
|
||||||
|
|
||||||
if !reflect.DeepEqual(list, wantAccountsAfterDelete) {
|
if !reflect.DeepEqual(list, wantAccountsAfterDelete) {
|
||||||
t.Fatalf("got accounts after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantAccountsAfterDelete))
|
t.Fatalf("got accounts after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantAccountsAfterDelete))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, a := range wantAccountsAfterDelete {
|
for _, a := range wantAccountsAfterDelete {
|
||||||
if !cache.hasAddress(a.Address) {
|
if !cache.hasAddress(a.Address) {
|
||||||
t.Errorf("expected hasAccount(%x) to return true", a.Address)
|
t.Errorf("expected hasAccount(%x) to return true", a.Address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cache.hasAddress(wantAccounts[0].Address) {
|
if cache.hasAddress(wantAccounts[0].Address) {
|
||||||
t.Errorf("expected hasAccount(%x) to return false", wantAccounts[0].Address)
|
t.Errorf("expected hasAccount(%x) to return false", wantAccounts[0].Address)
|
||||||
}
|
}
|
||||||
|
|
@ -255,6 +289,7 @@ func TestCacheFind(t *testing.T) {
|
||||||
Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
|
Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
|
||||||
URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "something")},
|
URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "something")},
|
||||||
}
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
Query accounts.Account
|
Query accounts.Account
|
||||||
WantResult accounts.Account
|
WantResult accounts.Account
|
||||||
|
|
@ -290,6 +325,7 @@ func TestCacheFind(t *testing.T) {
|
||||||
t.Errorf("test %d: error mismatch for query %v\ngot %q\nwant %q", i, test.Query, err, test.WantError)
|
t.Errorf("test %d: error mismatch for query %v\ngot %q\nwant %q", i, test.Query, err, test.WantError)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if a != test.WantResult {
|
if a != test.WantResult {
|
||||||
t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult)
|
t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult)
|
||||||
continue
|
continue
|
||||||
|
|
@ -297,31 +333,12 @@ func TestCacheFind(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitForAccounts(wantAccounts []accounts.Account, ks *KeyStore) error {
|
|
||||||
var list []accounts.Account
|
|
||||||
for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
|
|
||||||
list = ks.Accounts()
|
|
||||||
if reflect.DeepEqual(list, wantAccounts) {
|
|
||||||
// ks should have also received change notifications
|
|
||||||
select {
|
|
||||||
case <-ks.changes:
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("wasn't notified of new accounts")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
time.Sleep(d)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("\ngot %v\nwant %v", list, wantAccounts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestUpdatedKeyfileContents tests that updating the contents of a keystore file
|
// TestUpdatedKeyfileContents tests that updating the contents of a keystore file
|
||||||
// is noticed by the watcher, and the account cache is updated accordingly
|
// is noticed by the watcher, and the account cache is updated accordingly
|
||||||
func TestUpdatedKeyfileContents(t *testing.T) {
|
func TestUpdatedKeyfileContents(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Create a temporary kesytore to test with
|
// Create a temporary keystore to test with
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-updatedkeyfilecontents-test-%d-%d", os.Getpid(), rand.Int()))
|
dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-updatedkeyfilecontents-test-%d-%d", os.Getpid(), rand.Int()))
|
||||||
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
||||||
|
|
||||||
|
|
@ -329,8 +346,10 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
||||||
if len(list) > 0 {
|
if len(list) > 0 {
|
||||||
t.Error("initial account list not empty:", list)
|
t.Error("initial account list not empty:", list)
|
||||||
}
|
}
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
|
|
||||||
|
if !waitWatcherStart(ks) {
|
||||||
|
t.Fatal("keystore watcher didn't start in time")
|
||||||
|
}
|
||||||
// Create the directory and copy a key file into it.
|
// Create the directory and copy a key file into it.
|
||||||
os.MkdirAll(dir, 0700)
|
os.MkdirAll(dir, 0700)
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
@ -344,63 +363,72 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
||||||
// ks should see the account.
|
// ks should see the account.
|
||||||
wantAccounts := []accounts.Account{cachetestAccounts[0]}
|
wantAccounts := []accounts.Account{cachetestAccounts[0]}
|
||||||
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
|
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
|
||||||
|
|
||||||
if err := waitForAccounts(wantAccounts, ks); err != nil {
|
if err := waitForAccounts(wantAccounts, ks); err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// needed so that modTime of `file` is different to its current value after forceCopyFile
|
// needed so that modTime of `file` is different to its current value after forceCopyFile
|
||||||
time.Sleep(1000 * time.Millisecond)
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
// Now replace file contents
|
// Now replace file contents
|
||||||
if err := forceCopyFile(file, cachetestAccounts[1].URL.Path); err != nil {
|
if err := forceCopyFile(file, cachetestAccounts[1].URL.Path); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
wantAccounts = []accounts.Account{cachetestAccounts[1]}
|
wantAccounts = []accounts.Account{cachetestAccounts[1]}
|
||||||
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
|
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
|
||||||
|
|
||||||
if err := waitForAccounts(wantAccounts, ks); err != nil {
|
if err := waitForAccounts(wantAccounts, ks); err != nil {
|
||||||
t.Errorf("First replacement failed")
|
t.Errorf("First replacement failed")
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// needed so that modTime of `file` is different to its current value after forceCopyFile
|
// needed so that modTime of `file` is different to its current value after forceCopyFile
|
||||||
time.Sleep(1000 * time.Millisecond)
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
// Now replace file contents again
|
// Now replace file contents again
|
||||||
if err := forceCopyFile(file, cachetestAccounts[2].URL.Path); err != nil {
|
if err := forceCopyFile(file, cachetestAccounts[2].URL.Path); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
wantAccounts = []accounts.Account{cachetestAccounts[2]}
|
wantAccounts = []accounts.Account{cachetestAccounts[2]}
|
||||||
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
|
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
|
||||||
|
|
||||||
if err := waitForAccounts(wantAccounts, ks); err != nil {
|
if err := waitForAccounts(wantAccounts, ks); err != nil {
|
||||||
t.Errorf("Second replacement failed")
|
t.Errorf("Second replacement failed")
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// needed so that modTime of `file` is different to its current value after ioutil.WriteFile
|
// needed so that modTime of `file` is different to its current value after os.WriteFile
|
||||||
time.Sleep(1000 * time.Millisecond)
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
// Now replace file contents with crap
|
// Now replace file contents with crap
|
||||||
if err := ioutil.WriteFile(file, []byte("foo"), 0644); err != nil {
|
if err := os.WriteFile(file, []byte("foo"), 0600); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := waitForAccounts([]accounts.Account{}, ks); err != nil {
|
if err := waitForAccounts([]accounts.Account{}, ks); err != nil {
|
||||||
t.Errorf("Emptying account file failed")
|
t.Errorf("Emptying account file failed")
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// forceCopyFile is like cp.CopyFile, but doesn't complain if the destination exists.
|
// forceCopyFile is like cp.CopyFile, but doesn't complain if the destination exists.
|
||||||
func forceCopyFile(dst, src string) error {
|
func forceCopyFile(dst, src string) error {
|
||||||
data, err := ioutil.ReadFile(src)
|
data, err := os.ReadFile(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return ioutil.WriteFile(dst, data, 0644)
|
|
||||||
|
return os.WriteFile(dst, data, 0644)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,44 +17,46 @@
|
||||||
package keystore
|
package keystore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
mapset "github.com/deckarep/golang-set"
|
mapset "github.com/deckarep/golang-set/v2"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fileCache is a cache of files seen during scan of keystore.
|
// fileCache is a cache of files seen during scan of keystore.
|
||||||
type fileCache struct {
|
type fileCache struct {
|
||||||
all mapset.Set // Set of all files from the keystore folder
|
all mapset.Set[string] // Set of all files from the keystore folder
|
||||||
lastMod time.Time // Last time instance when a file was modified
|
lastMod time.Time // Last time instance when a file was modified
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// scan performs a new scan on the given directory, compares against the already
|
// scan performs a new scan on the given directory, compares against the already
|
||||||
// cached filenames, and returns file sets: creates, deletes, updates.
|
// cached filenames, and returns file sets: creates, deletes, updates.
|
||||||
func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, error) {
|
func (fc *fileCache) scan(keyDir string) (mapset.Set[string], mapset.Set[string], mapset.Set[string], error) {
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
|
|
||||||
// List all the fails from the keystore folder
|
// List all the files from the keystore folder
|
||||||
files, err := ioutil.ReadDir(keyDir)
|
files, err := os.ReadDir(keyDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
t1 := time.Now()
|
t1 := time.Now()
|
||||||
|
|
||||||
fc.mu.Lock()
|
fc.mu.Lock()
|
||||||
defer fc.mu.Unlock()
|
defer fc.mu.Unlock()
|
||||||
|
|
||||||
// Iterate all the files and gather their metadata
|
// Iterate all the files and gather their metadata
|
||||||
all := mapset.NewThreadUnsafeSet()
|
all := mapset.NewThreadUnsafeSet[string]()
|
||||||
mods := mapset.NewThreadUnsafeSet()
|
mods := mapset.NewThreadUnsafeSet[string]()
|
||||||
|
|
||||||
var newLastMod time.Time
|
var newLastMod time.Time
|
||||||
|
|
||||||
for _, fi := range files {
|
for _, fi := range files {
|
||||||
path := filepath.Join(keyDir, fi.Name())
|
path := filepath.Join(keyDir, fi.Name())
|
||||||
// Skip any non-key files from the folder
|
// Skip any non-key files from the folder
|
||||||
|
|
@ -62,17 +64,24 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, er
|
||||||
log.Trace("Ignoring file on account scan", "path", path)
|
log.Trace("Ignoring file on account scan", "path", path)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Gather the set of all and fresly modified files
|
// Gather the set of all and freshly modified files
|
||||||
all.Add(path)
|
all.Add(path)
|
||||||
|
|
||||||
modified := fi.ModTime()
|
info, err := fi.Info()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
modified := info.ModTime()
|
||||||
if modified.After(fc.lastMod) {
|
if modified.After(fc.lastMod) {
|
||||||
mods.Add(path)
|
mods.Add(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
if modified.After(newLastMod) {
|
if modified.After(newLastMod) {
|
||||||
newLastMod = modified
|
newLastMod = modified
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t2 := time.Now()
|
t2 := time.Now()
|
||||||
|
|
||||||
// Update the tracked files and return the three sets
|
// Update the tracked files and return the three sets
|
||||||
|
|
@ -85,18 +94,20 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, er
|
||||||
|
|
||||||
// Report on the scanning stats and return
|
// Report on the scanning stats and return
|
||||||
log.Debug("FS scan times", "list", t1.Sub(t0), "set", t2.Sub(t1), "diff", t3.Sub(t2))
|
log.Debug("FS scan times", "list", t1.Sub(t0), "set", t2.Sub(t1), "diff", t3.Sub(t2))
|
||||||
|
|
||||||
return creates, deletes, updates, nil
|
return creates, deletes, updates, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// nonKeyFile ignores editor backups, hidden files and folders/symlinks.
|
// nonKeyFile ignores editor backups, hidden files and folders/symlinks.
|
||||||
func nonKeyFile(fi os.FileInfo) bool {
|
func nonKeyFile(fi os.DirEntry) bool {
|
||||||
// Skip editor backups and UNIX-style hidden files.
|
// Skip editor backups and UNIX-style hidden files.
|
||||||
if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") {
|
if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// Skip misc special files, directories (yes, symlinks too).
|
// Skip misc special files, directories (yes, symlinks too).
|
||||||
if fi.IsDir() || fi.Mode()&os.ModeType != 0 {
|
if fi.IsDir() || !fi.Type().IsRegular() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -99,26 +98,32 @@ func (k *Key) MarshalJSON() (j []byte, err error) {
|
||||||
version,
|
version,
|
||||||
}
|
}
|
||||||
j, err = json.Marshal(jStruct)
|
j, err = json.Marshal(jStruct)
|
||||||
|
|
||||||
return j, err
|
return j, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *Key) UnmarshalJSON(j []byte) (err error) {
|
func (k *Key) UnmarshalJSON(j []byte) (err error) {
|
||||||
keyJSON := new(plainKeyJSON)
|
keyJSON := new(plainKeyJSON)
|
||||||
|
|
||||||
err = json.Unmarshal(j, &keyJSON)
|
err = json.Unmarshal(j, &keyJSON)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
u := new(uuid.UUID)
|
u := new(uuid.UUID)
|
||||||
|
|
||||||
*u, err = uuid.Parse(keyJSON.Id)
|
*u, err = uuid.Parse(keyJSON.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
k.Id = *u
|
k.Id = *u
|
||||||
|
|
||||||
addr, err := hex.DecodeString(keyJSON.Address)
|
addr, err := hex.DecodeString(keyJSON.Address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
privkey, err := crypto.HexToECDSA(keyJSON.PrivateKey)
|
privkey, err := crypto.HexToECDSA(keyJSON.PrivateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -135,11 +140,13 @@ func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("Could not create random uuid: %v", err))
|
panic(fmt.Sprintf("Could not create random uuid: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
key := &Key{
|
key := &Key{
|
||||||
Id: id,
|
Id: id,
|
||||||
Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
|
Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
|
||||||
PrivateKey: privateKeyECDSA,
|
PrivateKey: privateKeyECDSA,
|
||||||
}
|
}
|
||||||
|
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,19 +155,24 @@ func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
|
||||||
// retry until the first byte is 0.
|
// retry until the first byte is 0.
|
||||||
func NewKeyForDirectICAP(rand io.Reader) *Key {
|
func NewKeyForDirectICAP(rand io.Reader) *Key {
|
||||||
randBytes := make([]byte, 64)
|
randBytes := make([]byte, 64)
|
||||||
|
|
||||||
_, err := rand.Read(randBytes)
|
_, err := rand.Read(randBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("key generation: could not read from random source: " + err.Error())
|
panic("key generation: could not read from random source: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
reader := bytes.NewReader(randBytes)
|
reader := bytes.NewReader(randBytes)
|
||||||
|
|
||||||
privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader)
|
privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
|
panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
key := newKeyFromECDSA(privateKeyECDSA)
|
key := newKeyFromECDSA(privateKeyECDSA)
|
||||||
if !strings.HasPrefix(key.Address.Hex(), "0x00") {
|
if !strings.HasPrefix(key.Address.Hex(), "0x00") {
|
||||||
return NewKeyForDirectICAP(rand)
|
return NewKeyForDirectICAP(rand)
|
||||||
}
|
}
|
||||||
|
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,6 +181,7 @@ func newKey(rand io.Reader) (*Key, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return newKeyFromECDSA(privateKeyECDSA), nil
|
return newKeyFromECDSA(privateKeyECDSA), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,6 +190,7 @@ func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Accou
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, accounts.Account{}, err
|
return nil, accounts.Account{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
a := accounts.Account{
|
a := accounts.Account{
|
||||||
Address: key.Address,
|
Address: key.Address,
|
||||||
URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))},
|
URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))},
|
||||||
|
|
@ -185,6 +199,7 @@ func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Accou
|
||||||
zeroKey(key.PrivateKey)
|
zeroKey(key.PrivateKey)
|
||||||
return nil, a, err
|
return nil, a, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return key, a, err
|
return key, a, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,16 +212,20 @@ func writeTemporaryKeyFile(file string, content []byte) (string, error) {
|
||||||
}
|
}
|
||||||
// Atomic write: create a temporary hidden file first
|
// Atomic write: create a temporary hidden file first
|
||||||
// then move it into place. TempFile assigns mode 0600.
|
// then move it into place. TempFile assigns mode 0600.
|
||||||
f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp")
|
f, err := os.CreateTemp(filepath.Dir(file), "."+filepath.Base(file)+".tmp")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := f.Write(content); err != nil {
|
if _, err := f.Write(content); err != nil {
|
||||||
f.Close()
|
f.Close()
|
||||||
os.Remove(f.Name())
|
os.Remove(f.Name())
|
||||||
|
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
f.Close()
|
f.Close()
|
||||||
|
|
||||||
return f.Name(), nil
|
return f.Name(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,6 +234,7 @@ func writeKeyFile(file string, content []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return os.Rename(name, file)
|
return os.Rename(name, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,12 +247,14 @@ func keyFileName(keyAddr common.Address) string {
|
||||||
|
|
||||||
func toISO8601(t time.Time) string {
|
func toISO8601(t time.Time) string {
|
||||||
var tz string
|
var tz string
|
||||||
|
|
||||||
name, offset := t.Zone()
|
name, offset := t.Zone()
|
||||||
if name == "UTC" {
|
if name == "UTC" {
|
||||||
tz = "Z"
|
tz = "Z"
|
||||||
} else {
|
} else {
|
||||||
tz = fmt.Sprintf("%03d00", offset/3600)
|
tz = fmt.Sprintf("%03d00", offset/3600)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s",
|
return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s",
|
||||||
t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz)
|
t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore {
|
||||||
keydir, _ = filepath.Abs(keydir)
|
keydir, _ = filepath.Abs(keydir)
|
||||||
ks := &KeyStore{storage: &keyStorePassphrase{keydir, scryptN, scryptP, false}}
|
ks := &KeyStore{storage: &keyStorePassphrase{keydir, scryptN, scryptP, false}}
|
||||||
ks.init(keydir)
|
ks.init(keydir)
|
||||||
|
|
||||||
return ks
|
return ks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -93,6 +94,7 @@ func NewPlaintextKeyStore(keydir string) *KeyStore {
|
||||||
keydir, _ = filepath.Abs(keydir)
|
keydir, _ = filepath.Abs(keydir)
|
||||||
ks := &KeyStore{storage: &keyStorePlain{keydir}}
|
ks := &KeyStore{storage: &keyStorePlain{keydir}}
|
||||||
ks.init(keydir)
|
ks.init(keydir)
|
||||||
|
|
||||||
return ks
|
return ks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,6 +116,7 @@ func (ks *KeyStore) init(keydir string) {
|
||||||
// Create the initial list of wallets from the cache
|
// Create the initial list of wallets from the cache
|
||||||
accs := ks.cache.accounts()
|
accs := ks.cache.accounts()
|
||||||
ks.wallets = make([]accounts.Wallet, len(accs))
|
ks.wallets = make([]accounts.Wallet, len(accs))
|
||||||
|
|
||||||
for i := 0; i < len(accs); i++ {
|
for i := 0; i < len(accs); i++ {
|
||||||
ks.wallets[i] = &keystoreWallet{account: accs[i], keystore: ks}
|
ks.wallets[i] = &keystoreWallet{account: accs[i], keystore: ks}
|
||||||
}
|
}
|
||||||
|
|
@ -130,6 +133,7 @@ func (ks *KeyStore) Wallets() []accounts.Wallet {
|
||||||
|
|
||||||
cpy := make([]accounts.Wallet, len(ks.wallets))
|
cpy := make([]accounts.Wallet, len(ks.wallets))
|
||||||
copy(cpy, ks.wallets)
|
copy(cpy, ks.wallets)
|
||||||
|
|
||||||
return cpy
|
return cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,12 +162,14 @@ func (ks *KeyStore) refreshWallets() {
|
||||||
|
|
||||||
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
|
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
|
||||||
wallets = append(wallets, wallet)
|
wallets = append(wallets, wallet)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// If the account is the same as the first wallet, keep it
|
// If the account is the same as the first wallet, keep it
|
||||||
if ks.wallets[0].Accounts()[0] == account {
|
if ks.wallets[0].Accounts()[0] == account {
|
||||||
wallets = append(wallets, ks.wallets[0])
|
wallets = append(wallets, ks.wallets[0])
|
||||||
ks.wallets = ks.wallets[1:]
|
ks.wallets = ks.wallets[1:]
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -171,6 +177,7 @@ func (ks *KeyStore) refreshWallets() {
|
||||||
for _, wallet := range ks.wallets {
|
for _, wallet := range ks.wallets {
|
||||||
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
|
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
|
||||||
}
|
}
|
||||||
|
|
||||||
ks.wallets = wallets
|
ks.wallets = wallets
|
||||||
ks.mu.Unlock()
|
ks.mu.Unlock()
|
||||||
|
|
||||||
|
|
@ -195,6 +202,7 @@ func (ks *KeyStore) Subscribe(sink chan<- accounts.WalletEvent) event.Subscripti
|
||||||
ks.updating = true
|
ks.updating = true
|
||||||
go ks.updater()
|
go ks.updater()
|
||||||
}
|
}
|
||||||
|
|
||||||
return sub
|
return sub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -218,6 +226,7 @@ func (ks *KeyStore) updater() {
|
||||||
if ks.updateScope.Count() == 0 {
|
if ks.updateScope.Count() == 0 {
|
||||||
ks.updating = false
|
ks.updating = false
|
||||||
ks.mu.Unlock()
|
ks.mu.Unlock()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ks.mu.Unlock()
|
ks.mu.Unlock()
|
||||||
|
|
@ -244,6 +253,7 @@ func (ks *KeyStore) Delete(a accounts.Account, passphrase string) error {
|
||||||
if key != nil {
|
if key != nil {
|
||||||
zeroKey(key.PrivateKey)
|
zeroKey(key.PrivateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -255,6 +265,7 @@ func (ks *KeyStore) Delete(a accounts.Account, passphrase string) error {
|
||||||
ks.cache.delete(a)
|
ks.cache.delete(a)
|
||||||
ks.refreshWallets()
|
ks.refreshWallets()
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -285,6 +296,7 @@ func (ks *KeyStore) SignTx(a accounts.Account, tx *types.Transaction, chainID *b
|
||||||
}
|
}
|
||||||
// Depending on the presence of the chain ID, sign with 2718 or homestead
|
// Depending on the presence of the chain ID, sign with 2718 or homestead
|
||||||
signer := types.LatestSignerForChainID(chainID)
|
signer := types.LatestSignerForChainID(chainID)
|
||||||
|
|
||||||
return types.SignTx(tx, signer, unlockedKey.PrivateKey)
|
return types.SignTx(tx, signer, unlockedKey.PrivateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -296,7 +308,9 @@ func (ks *KeyStore) SignHashWithPassphrase(a accounts.Account, passphrase string
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer zeroKey(key.PrivateKey)
|
defer zeroKey(key.PrivateKey)
|
||||||
|
|
||||||
return crypto.Sign(hash, key.PrivateKey)
|
return crypto.Sign(hash, key.PrivateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -310,6 +324,7 @@ func (ks *KeyStore) SignTxWithPassphrase(a accounts.Account, passphrase string,
|
||||||
defer zeroKey(key.PrivateKey)
|
defer zeroKey(key.PrivateKey)
|
||||||
// Depending on the presence of the chain ID, sign with or without replay protection.
|
// Depending on the presence of the chain ID, sign with or without replay protection.
|
||||||
signer := types.LatestSignerForChainID(chainID)
|
signer := types.LatestSignerForChainID(chainID)
|
||||||
|
|
||||||
return types.SignTx(tx, signer, key.PrivateKey)
|
return types.SignTx(tx, signer, key.PrivateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -327,6 +342,7 @@ func (ks *KeyStore) Lock(addr common.Address) error {
|
||||||
} else {
|
} else {
|
||||||
ks.mu.Unlock()
|
ks.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -345,6 +361,7 @@ func (ks *KeyStore) TimedUnlock(a accounts.Account, passphrase string, timeout t
|
||||||
|
|
||||||
ks.mu.Lock()
|
ks.mu.Lock()
|
||||||
defer ks.mu.Unlock()
|
defer ks.mu.Unlock()
|
||||||
|
|
||||||
u, found := ks.unlocked[a.Address]
|
u, found := ks.unlocked[a.Address]
|
||||||
if found {
|
if found {
|
||||||
if u.abort == nil {
|
if u.abort == nil {
|
||||||
|
|
@ -356,13 +373,16 @@ func (ks *KeyStore) TimedUnlock(a accounts.Account, passphrase string, timeout t
|
||||||
// Terminate the expire goroutine and replace it below.
|
// Terminate the expire goroutine and replace it below.
|
||||||
close(u.abort)
|
close(u.abort)
|
||||||
}
|
}
|
||||||
|
|
||||||
if timeout > 0 {
|
if timeout > 0 {
|
||||||
u = &unlocked{Key: key, abort: make(chan struct{})}
|
u = &unlocked{Key: key, abort: make(chan struct{})}
|
||||||
go ks.expire(a.Address, u, timeout)
|
go ks.expire(a.Address, u, timeout)
|
||||||
} else {
|
} else {
|
||||||
u = &unlocked{Key: key}
|
u = &unlocked{Key: key}
|
||||||
}
|
}
|
||||||
|
|
||||||
ks.unlocked[a.Address] = u
|
ks.unlocked[a.Address] = u
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -372,6 +392,7 @@ func (ks *KeyStore) Find(a accounts.Account) (accounts.Account, error) {
|
||||||
ks.cache.mu.Lock()
|
ks.cache.mu.Lock()
|
||||||
a, err := ks.cache.find(a)
|
a, err := ks.cache.find(a)
|
||||||
ks.cache.mu.Unlock()
|
ks.cache.mu.Unlock()
|
||||||
|
|
||||||
return a, err
|
return a, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -380,7 +401,9 @@ func (ks *KeyStore) getDecryptedKey(a accounts.Account, auth string) (accounts.A
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return a, nil, err
|
return a, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := ks.storage.GetKey(a.Address, a.URL.Path, auth)
|
key, err := ks.storage.GetKey(a.Address, a.URL.Path, auth)
|
||||||
|
|
||||||
return a, key, err
|
return a, key, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -415,6 +438,7 @@ func (ks *KeyStore) NewAccount(passphrase string) (accounts.Account, error) {
|
||||||
// than waiting for file system notifications to pick it up.
|
// than waiting for file system notifications to pick it up.
|
||||||
ks.cache.add(account)
|
ks.cache.add(account)
|
||||||
ks.refreshWallets()
|
ks.refreshWallets()
|
||||||
|
|
||||||
return account, nil
|
return account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -424,12 +448,14 @@ func (ks *KeyStore) Export(a accounts.Account, passphrase, newPassphrase string)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var N, P int
|
var N, P int
|
||||||
if store, ok := ks.storage.(*keyStorePassphrase); ok {
|
if store, ok := ks.storage.(*keyStorePassphrase); ok {
|
||||||
N, P = store.scryptN, store.scryptP
|
N, P = store.scryptN, store.scryptP
|
||||||
} else {
|
} else {
|
||||||
N, P = StandardScryptN, StandardScryptP
|
N, P = StandardScryptN, StandardScryptP
|
||||||
}
|
}
|
||||||
|
|
||||||
return EncryptKey(key, newPassphrase, N, P)
|
return EncryptKey(key, newPassphrase, N, P)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -439,9 +465,11 @@ func (ks *KeyStore) Import(keyJSON []byte, passphrase, newPassphrase string) (ac
|
||||||
if key != nil && key.PrivateKey != nil {
|
if key != nil && key.PrivateKey != nil {
|
||||||
defer zeroKey(key.PrivateKey)
|
defer zeroKey(key.PrivateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return accounts.Account{}, err
|
return accounts.Account{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ks.importMu.Lock()
|
ks.importMu.Lock()
|
||||||
defer ks.importMu.Unlock()
|
defer ks.importMu.Unlock()
|
||||||
|
|
||||||
|
|
@ -450,6 +478,7 @@ func (ks *KeyStore) Import(keyJSON []byte, passphrase, newPassphrase string) (ac
|
||||||
Address: key.Address,
|
Address: key.Address,
|
||||||
}, ErrAccountAlreadyExists
|
}, ErrAccountAlreadyExists
|
||||||
}
|
}
|
||||||
|
|
||||||
return ks.importKey(key, newPassphrase)
|
return ks.importKey(key, newPassphrase)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -464,6 +493,7 @@ func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (acco
|
||||||
Address: key.Address,
|
Address: key.Address,
|
||||||
}, ErrAccountAlreadyExists
|
}, ErrAccountAlreadyExists
|
||||||
}
|
}
|
||||||
|
|
||||||
return ks.importKey(key, passphrase)
|
return ks.importKey(key, passphrase)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -472,8 +502,10 @@ func (ks *KeyStore) importKey(key *Key, passphrase string) (accounts.Account, er
|
||||||
if err := ks.storage.StoreKey(a.URL.Path, key, passphrase); err != nil {
|
if err := ks.storage.StoreKey(a.URL.Path, key, passphrase); err != nil {
|
||||||
return accounts.Account{}, err
|
return accounts.Account{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ks.cache.add(a)
|
ks.cache.add(a)
|
||||||
ks.refreshWallets()
|
ks.refreshWallets()
|
||||||
|
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -483,6 +515,7 @@ func (ks *KeyStore) Update(a accounts.Account, passphrase, newPassphrase string)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ks.storage.StoreKey(a.URL.Path, key, newPassphrase)
|
return ks.storage.StoreKey(a.URL.Path, key, newPassphrase)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -493,11 +526,22 @@ func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (account
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return a, err
|
return a, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ks.cache.add(a)
|
ks.cache.add(a)
|
||||||
ks.refreshWallets()
|
ks.refreshWallets()
|
||||||
|
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isUpdating returns whether the event notification loop is running.
|
||||||
|
// This method is mainly meant for tests.
|
||||||
|
func (ks *KeyStore) isUpdating() bool {
|
||||||
|
ks.mu.RLock()
|
||||||
|
defer ks.mu.RUnlock()
|
||||||
|
|
||||||
|
return ks.updating
|
||||||
|
}
|
||||||
|
|
||||||
// zeroKey zeroes a private key in memory.
|
// zeroKey zeroes a private key in memory.
|
||||||
func zeroKey(k *ecdsa.PrivateKey) {
|
func zeroKey(k *ecdsa.PrivateKey) {
|
||||||
b := k.D.Bits()
|
b := k.D.Bits()
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
package keystore
|
package keystore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
@ -38,44 +37,51 @@ var testSigData = make([]byte, 32)
|
||||||
|
|
||||||
func TestKeyStore(t *testing.T) {
|
func TestKeyStore(t *testing.T) {
|
||||||
dir, ks := tmpKeyStore(t, true)
|
dir, ks := tmpKeyStore(t, true)
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
a, err := ks.NewAccount("foo")
|
a, err := ks.NewAccount("foo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(a.URL.Path, dir) {
|
if !strings.HasPrefix(a.URL.Path, dir) {
|
||||||
t.Errorf("account file %s doesn't have dir prefix", a.URL)
|
t.Errorf("account file %s doesn't have dir prefix", a.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
stat, err := os.Stat(a.URL.Path)
|
stat, err := os.Stat(a.URL.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("account file %s doesn't exist (%v)", a.URL, err)
|
t.Fatalf("account file %s doesn't exist (%v)", a.URL, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if runtime.GOOS != "windows" && stat.Mode() != 0600 {
|
if runtime.GOOS != "windows" && stat.Mode() != 0600 {
|
||||||
t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600)
|
t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ks.HasAddress(a.Address) {
|
if !ks.HasAddress(a.Address) {
|
||||||
t.Errorf("HasAccount(%x) should've returned true", a.Address)
|
t.Errorf("HasAccount(%x) should've returned true", a.Address)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ks.Update(a, "foo", "bar"); err != nil {
|
if err := ks.Update(a, "foo", "bar"); err != nil {
|
||||||
t.Errorf("Update error: %v", err)
|
t.Errorf("Update error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ks.Delete(a, "bar"); err != nil {
|
if err := ks.Delete(a, "bar"); err != nil {
|
||||||
t.Errorf("Delete error: %v", err)
|
t.Errorf("Delete error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if common.FileExist(a.URL.Path) {
|
if common.FileExist(a.URL.Path) {
|
||||||
t.Errorf("account file %s should be gone after Delete", a.URL)
|
t.Errorf("account file %s should be gone after Delete", a.URL)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ks.HasAddress(a.Address) {
|
if ks.HasAddress(a.Address) {
|
||||||
t.Errorf("HasAccount(%x) should've returned true after Delete", a.Address)
|
t.Errorf("HasAccount(%x) should've returned true after Delete", a.Address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSign(t *testing.T) {
|
func TestSign(t *testing.T) {
|
||||||
dir, ks := tmpKeyStore(t, true)
|
_, ks := tmpKeyStore(t, true)
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
pass := "" // not used but required by API
|
pass := "" // not used but required by API
|
||||||
|
|
||||||
a1, err := ks.NewAccount(pass)
|
a1, err := ks.NewAccount(pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -83,16 +89,17 @@ func TestSign(t *testing.T) {
|
||||||
if err := ks.Unlock(a1, ""); err != nil {
|
if err := ks.Unlock(a1, ""); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err != nil {
|
if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSignWithPassphrase(t *testing.T) {
|
func TestSignWithPassphrase(t *testing.T) {
|
||||||
dir, ks := tmpKeyStore(t, true)
|
_, ks := tmpKeyStore(t, true)
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
pass := "passwd"
|
pass := "passwd"
|
||||||
|
|
||||||
acc, err := ks.NewAccount(pass)
|
acc, err := ks.NewAccount(pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -117,10 +124,11 @@ func TestSignWithPassphrase(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTimedUnlock(t *testing.T) {
|
func TestTimedUnlock(t *testing.T) {
|
||||||
dir, ks := tmpKeyStore(t, true)
|
t.Parallel()
|
||||||
defer os.RemoveAll(dir)
|
_, ks := tmpKeyStore(t, true)
|
||||||
|
|
||||||
pass := "foo"
|
pass := "foo"
|
||||||
|
|
||||||
a1, err := ks.NewAccount(pass)
|
a1, err := ks.NewAccount(pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -145,6 +153,7 @@ func TestTimedUnlock(t *testing.T) {
|
||||||
|
|
||||||
// Signing fails again after automatic locking
|
// Signing fails again after automatic locking
|
||||||
time.Sleep(250 * time.Millisecond)
|
time.Sleep(250 * time.Millisecond)
|
||||||
|
|
||||||
_, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
|
_, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
|
||||||
if err != ErrLocked {
|
if err != ErrLocked {
|
||||||
t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
|
t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
|
||||||
|
|
@ -152,10 +161,11 @@ func TestTimedUnlock(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOverrideUnlock(t *testing.T) {
|
func TestOverrideUnlock(t *testing.T) {
|
||||||
dir, ks := tmpKeyStore(t, false)
|
t.Parallel()
|
||||||
defer os.RemoveAll(dir)
|
_, ks := tmpKeyStore(t, false)
|
||||||
|
|
||||||
pass := "foo"
|
pass := "foo"
|
||||||
|
|
||||||
a1, err := ks.NewAccount(pass)
|
a1, err := ks.NewAccount(pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -185,6 +195,7 @@ func TestOverrideUnlock(t *testing.T) {
|
||||||
|
|
||||||
// Signing fails again after automatic locking
|
// Signing fails again after automatic locking
|
||||||
time.Sleep(250 * time.Millisecond)
|
time.Sleep(250 * time.Millisecond)
|
||||||
|
|
||||||
_, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
|
_, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
|
||||||
if err != ErrLocked {
|
if err != ErrLocked {
|
||||||
t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
|
t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
|
||||||
|
|
@ -193,8 +204,8 @@ func TestOverrideUnlock(t *testing.T) {
|
||||||
|
|
||||||
// This test should fail under -race if signing races the expiration goroutine.
|
// This test should fail under -race if signing races the expiration goroutine.
|
||||||
func TestSignRace(t *testing.T) {
|
func TestSignRace(t *testing.T) {
|
||||||
dir, ks := tmpKeyStore(t, false)
|
t.Parallel()
|
||||||
defer os.RemoveAll(dir)
|
_, ks := tmpKeyStore(t, false)
|
||||||
|
|
||||||
// Create a test account.
|
// Create a test account.
|
||||||
a1, err := ks.NewAccount("")
|
a1, err := ks.NewAccount("")
|
||||||
|
|
@ -205,6 +216,7 @@ func TestSignRace(t *testing.T) {
|
||||||
if err := ks.TimedUnlock(a1, "", 15*time.Millisecond); err != nil {
|
if err := ks.TimedUnlock(a1, "", 15*time.Millisecond); err != nil {
|
||||||
t.Fatal("could not unlock the test account", err)
|
t.Fatal("could not unlock the test account", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
end := time.Now().Add(500 * time.Millisecond)
|
end := time.Now().Add(500 * time.Millisecond)
|
||||||
for time.Now().Before(end) {
|
for time.Now().Before(end) {
|
||||||
if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err == ErrLocked {
|
if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err == ErrLocked {
|
||||||
|
|
@ -213,25 +225,41 @@ func TestSignRace(t *testing.T) {
|
||||||
t.Errorf("Sign error: %v", err)
|
t.Errorf("Sign error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
time.Sleep(1 * time.Millisecond)
|
time.Sleep(1 * time.Millisecond)
|
||||||
}
|
}
|
||||||
t.Errorf("Account did not lock within the timeout")
|
t.Errorf("Account did not lock within the timeout")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// waitForKsUpdating waits until the updating-status of the ks reaches the
|
||||||
|
// desired wantStatus.
|
||||||
|
// It waits for a maximum time of maxTime, and returns false if it does not
|
||||||
|
// finish in time
|
||||||
|
func waitForKsUpdating(t *testing.T, ks *KeyStore, wantStatus bool, maxTime time.Duration) bool {
|
||||||
|
t.Helper()
|
||||||
|
// Wait max 250 ms, then return false
|
||||||
|
for t0 := time.Now(); time.Since(t0) < maxTime; {
|
||||||
|
if ks.isUpdating() == wantStatus {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(25 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Tests that the wallet notifier loop starts and stops correctly based on the
|
// Tests that the wallet notifier loop starts and stops correctly based on the
|
||||||
// addition and removal of wallet event subscriptions.
|
// addition and removal of wallet event subscriptions.
|
||||||
func TestWalletNotifierLifecycle(t *testing.T) {
|
func TestWalletNotifierLifecycle(t *testing.T) {
|
||||||
// Create a temporary kesytore to test with
|
t.Parallel()
|
||||||
dir, ks := tmpKeyStore(t, false)
|
// Create a temporary keystore to test with
|
||||||
defer os.RemoveAll(dir)
|
_, ks := tmpKeyStore(t, false)
|
||||||
|
|
||||||
// Ensure that the notification updater is not running yet
|
// Ensure that the notification updater is not running yet
|
||||||
time.Sleep(250 * time.Millisecond)
|
time.Sleep(250 * time.Millisecond)
|
||||||
ks.mu.RLock()
|
|
||||||
updating := ks.updating
|
|
||||||
ks.mu.RUnlock()
|
|
||||||
|
|
||||||
if updating {
|
if ks.isUpdating() {
|
||||||
t.Errorf("wallet notifier running without subscribers")
|
t.Errorf("wallet notifier running without subscribers")
|
||||||
}
|
}
|
||||||
// Subscribe to the wallet feed and ensure the updater boots up
|
// Subscribe to the wallet feed and ensure the updater boots up
|
||||||
|
|
@ -242,38 +270,28 @@ func TestWalletNotifierLifecycle(t *testing.T) {
|
||||||
// Create a new subscription
|
// Create a new subscription
|
||||||
subs[i] = ks.Subscribe(updates)
|
subs[i] = ks.Subscribe(updates)
|
||||||
|
|
||||||
// Ensure the notifier comes online
|
if !waitForKsUpdating(t, ks, true, 250*time.Millisecond) {
|
||||||
time.Sleep(250 * time.Millisecond)
|
|
||||||
ks.mu.RLock()
|
|
||||||
updating = ks.updating
|
|
||||||
ks.mu.RUnlock()
|
|
||||||
|
|
||||||
if !updating {
|
|
||||||
t.Errorf("sub %d: wallet notifier not running after subscription", i)
|
t.Errorf("sub %d: wallet notifier not running after subscription", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Unsubscribe and ensure the updater terminates eventually
|
// Close all but one sub
|
||||||
for i := 0; i < len(subs); i++ {
|
for i := 0; i < len(subs)-1; i++ {
|
||||||
// Close an existing subscription
|
// Close an existing subscription
|
||||||
subs[i].Unsubscribe()
|
subs[i].Unsubscribe()
|
||||||
|
|
||||||
// Ensure the notifier shuts down at and only at the last close
|
|
||||||
for k := 0; k < int(walletRefreshCycle/(250*time.Millisecond))+2; k++ {
|
|
||||||
ks.mu.RLock()
|
|
||||||
updating = ks.updating
|
|
||||||
ks.mu.RUnlock()
|
|
||||||
|
|
||||||
if i < len(subs)-1 && !updating {
|
|
||||||
t.Fatalf("sub %d: event notifier stopped prematurely", i)
|
|
||||||
}
|
|
||||||
if i == len(subs)-1 && !updating {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
// Check that it is still running
|
||||||
time.Sleep(250 * time.Millisecond)
|
time.Sleep(250 * time.Millisecond)
|
||||||
|
|
||||||
|
if !ks.isUpdating() {
|
||||||
|
t.Fatal("event notifier stopped prematurely")
|
||||||
}
|
}
|
||||||
}
|
// Unsubscribe the last one and ensure the updater terminates eventually.
|
||||||
|
subs[len(subs)-1].Unsubscribe()
|
||||||
|
|
||||||
|
if !waitForKsUpdating(t, ks, false, 4*time.Second) {
|
||||||
t.Errorf("wallet notifier didn't terminate after unsubscribe")
|
t.Errorf("wallet notifier didn't terminate after unsubscribe")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type walletEvent struct {
|
type walletEvent struct {
|
||||||
accounts.WalletEvent
|
accounts.WalletEvent
|
||||||
|
|
@ -283,8 +301,7 @@ type walletEvent struct {
|
||||||
// Tests that wallet notifications and correctly fired when accounts are added
|
// Tests that wallet notifications and correctly fired when accounts are added
|
||||||
// or deleted from the keystore.
|
// or deleted from the keystore.
|
||||||
func TestWalletNotifications(t *testing.T) {
|
func TestWalletNotifications(t *testing.T) {
|
||||||
dir, ks := tmpKeyStore(t, false)
|
_, ks := tmpKeyStore(t, false)
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
// Subscribe to the wallet feed and collect events.
|
// Subscribe to the wallet feed and collect events.
|
||||||
var (
|
var (
|
||||||
|
|
@ -292,7 +309,9 @@ func TestWalletNotifications(t *testing.T) {
|
||||||
updates = make(chan accounts.WalletEvent)
|
updates = make(chan accounts.WalletEvent)
|
||||||
sub = ks.Subscribe(updates)
|
sub = ks.Subscribe(updates)
|
||||||
)
|
)
|
||||||
|
|
||||||
defer sub.Unsubscribe()
|
defer sub.Unsubscribe()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
|
@ -310,6 +329,7 @@ func TestWalletNotifications(t *testing.T) {
|
||||||
live = make(map[common.Address]accounts.Account)
|
live = make(map[common.Address]accounts.Account)
|
||||||
wantEvents []walletEvent
|
wantEvents []walletEvent
|
||||||
)
|
)
|
||||||
|
|
||||||
for i := 0; i < 1024; i++ {
|
for i := 0; i < 1024; i++ {
|
||||||
if create := len(live) == 0 || rand.Int()%4 > 0; create {
|
if create := len(live) == 0 || rand.Int()%4 > 0; create {
|
||||||
// Add a new account and ensure wallet notifications arrives
|
// Add a new account and ensure wallet notifications arrives
|
||||||
|
|
@ -317,6 +337,7 @@ func TestWalletNotifications(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create test account: %v", err)
|
t.Fatalf("failed to create test account: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
live[account.Address] = account
|
live[account.Address] = account
|
||||||
wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletArrived}, account})
|
wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletArrived}, account})
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -326,9 +347,11 @@ func TestWalletNotifications(t *testing.T) {
|
||||||
account = a
|
account = a
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ks.Delete(account, ""); err != nil {
|
if err := ks.Delete(account, ""); err != nil {
|
||||||
t.Fatalf("failed to delete test account: %v", err)
|
t.Fatalf("failed to delete test account: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(live, account.Address)
|
delete(live, account.Address)
|
||||||
wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletDropped}, account})
|
wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletDropped}, account})
|
||||||
}
|
}
|
||||||
|
|
@ -336,27 +359,32 @@ func TestWalletNotifications(t *testing.T) {
|
||||||
|
|
||||||
// Shut down the event collector and check events.
|
// Shut down the event collector and check events.
|
||||||
sub.Unsubscribe()
|
sub.Unsubscribe()
|
||||||
|
|
||||||
for ev := range updates {
|
for ev := range updates {
|
||||||
events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]})
|
events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]})
|
||||||
}
|
}
|
||||||
|
|
||||||
checkAccounts(t, live, ks.Wallets())
|
checkAccounts(t, live, ks.Wallets())
|
||||||
checkEvents(t, wantEvents, events)
|
checkEvents(t, wantEvents, events)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestImportExport tests the import functionality of a keystore.
|
// TestImportExport tests the import functionality of a keystore.
|
||||||
func TestImportECDSA(t *testing.T) {
|
func TestImportECDSA(t *testing.T) {
|
||||||
dir, ks := tmpKeyStore(t, true)
|
_, ks := tmpKeyStore(t, true)
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
key, err := crypto.GenerateKey()
|
key, err := crypto.GenerateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to generate key: %v", key)
|
t.Fatalf("failed to generate key: %v", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = ks.ImportECDSA(key, "old"); err != nil {
|
if _, err = ks.ImportECDSA(key, "old"); err != nil {
|
||||||
t.Errorf("importing failed: %v", err)
|
t.Errorf("importing failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = ks.ImportECDSA(key, "old"); err == nil {
|
if _, err = ks.ImportECDSA(key, "old"); err == nil {
|
||||||
t.Errorf("importing same key twice succeeded")
|
t.Errorf("importing same key twice succeeded")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = ks.ImportECDSA(key, "new"); err == nil {
|
if _, err = ks.ImportECDSA(key, "new"); err == nil {
|
||||||
t.Errorf("importing same key twice succeeded")
|
t.Errorf("importing same key twice succeeded")
|
||||||
}
|
}
|
||||||
|
|
@ -364,62 +392,71 @@ func TestImportECDSA(t *testing.T) {
|
||||||
|
|
||||||
// TestImportECDSA tests the import and export functionality of a keystore.
|
// TestImportECDSA tests the import and export functionality of a keystore.
|
||||||
func TestImportExport(t *testing.T) {
|
func TestImportExport(t *testing.T) {
|
||||||
dir, ks := tmpKeyStore(t, true)
|
_, ks := tmpKeyStore(t, true)
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
acc, err := ks.NewAccount("old")
|
acc, err := ks.NewAccount("old")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create account: %v", acc)
|
t.Fatalf("failed to create account: %v", acc)
|
||||||
}
|
}
|
||||||
|
|
||||||
json, err := ks.Export(acc, "old", "new")
|
json, err := ks.Export(acc, "old", "new")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to export account: %v", acc)
|
t.Fatalf("failed to export account: %v", acc)
|
||||||
}
|
}
|
||||||
dir2, ks2 := tmpKeyStore(t, true)
|
|
||||||
defer os.RemoveAll(dir2)
|
_, ks2 := tmpKeyStore(t, true)
|
||||||
if _, err = ks2.Import(json, "old", "old"); err == nil {
|
if _, err = ks2.Import(json, "old", "old"); err == nil {
|
||||||
t.Errorf("importing with invalid password succeeded")
|
t.Errorf("importing with invalid password succeeded")
|
||||||
}
|
}
|
||||||
|
|
||||||
acc2, err := ks2.Import(json, "new", "new")
|
acc2, err := ks2.Import(json, "new", "new")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("importing failed: %v", err)
|
t.Errorf("importing failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if acc.Address != acc2.Address {
|
if acc.Address != acc2.Address {
|
||||||
t.Error("imported account does not match exported account")
|
t.Error("imported account does not match exported account")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = ks2.Import(json, "new", "new"); err == nil {
|
if _, err = ks2.Import(json, "new", "new"); err == nil {
|
||||||
t.Errorf("importing a key twice succeeded")
|
t.Errorf("importing a key twice succeeded")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestImportRace tests the keystore on races.
|
// TestImportRace tests the keystore on races.
|
||||||
// This test should fail under -race if importing races.
|
// This test should fail under -race if importing races.
|
||||||
func TestImportRace(t *testing.T) {
|
func TestImportRace(t *testing.T) {
|
||||||
dir, ks := tmpKeyStore(t, true)
|
_, ks := tmpKeyStore(t, true)
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
acc, err := ks.NewAccount("old")
|
acc, err := ks.NewAccount("old")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create account: %v", acc)
|
t.Fatalf("failed to create account: %v", acc)
|
||||||
}
|
}
|
||||||
|
|
||||||
json, err := ks.Export(acc, "old", "new")
|
json, err := ks.Export(acc, "old", "new")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to export account: %v", acc)
|
t.Fatalf("failed to export account: %v", acc)
|
||||||
}
|
}
|
||||||
dir2, ks2 := tmpKeyStore(t, true)
|
|
||||||
defer os.RemoveAll(dir2)
|
_, ks2 := tmpKeyStore(t, true)
|
||||||
|
|
||||||
var atom uint32
|
var atom uint32
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
|
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
if _, err := ks2.Import(json, "new", "new"); err != nil {
|
if _, err := ks2.Import(json, "new", "new"); err != nil {
|
||||||
atomic.AddUint32(&atom, 1)
|
atomic.AddUint32(&atom, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
if atom != 1 {
|
if atom != 1 {
|
||||||
t.Errorf("Import is racy")
|
t.Errorf("Import is racy")
|
||||||
}
|
}
|
||||||
|
|
@ -431,11 +468,14 @@ func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, walle
|
||||||
t.Errorf("wallet list doesn't match required accounts: have %d, want %d", len(wallets), len(live))
|
t.Errorf("wallet list doesn't match required accounts: have %d, want %d", len(wallets), len(live))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
liveList := make([]accounts.Account, 0, len(live))
|
liveList := make([]accounts.Account, 0, len(live))
|
||||||
for _, account := range live {
|
for _, account := range live {
|
||||||
liveList = append(liveList, account)
|
liveList = append(liveList, account)
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Sort(accountsByURL(liveList))
|
sort.Sort(accountsByURL(liveList))
|
||||||
|
|
||||||
for j, wallet := range wallets {
|
for j, wallet := range wallets {
|
||||||
if accs := wallet.Accounts(); len(accs) != 1 {
|
if accs := wallet.Accounts(); len(accs) != 1 {
|
||||||
t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs))
|
t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs))
|
||||||
|
|
@ -449,12 +489,15 @@ func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, walle
|
||||||
func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) {
|
func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) {
|
||||||
for _, wantEv := range want {
|
for _, wantEv := range want {
|
||||||
nmatch := 0
|
nmatch := 0
|
||||||
|
|
||||||
for ; len(have) > 0; nmatch++ {
|
for ; len(have) > 0; nmatch++ {
|
||||||
if have[0].Kind != wantEv.Kind || have[0].a != wantEv.a {
|
if have[0].Kind != wantEv.Kind || have[0].a != wantEv.a {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
have = have[1:]
|
have = have[1:]
|
||||||
}
|
}
|
||||||
|
|
||||||
if nmatch == 0 {
|
if nmatch == 0 {
|
||||||
t.Fatalf("can't find event with Kind=%v for %x", wantEv.Kind, wantEv.a.Address)
|
t.Fatalf("can't find event with Kind=%v for %x", wantEv.Kind, wantEv.a.Address)
|
||||||
}
|
}
|
||||||
|
|
@ -462,13 +505,12 @@ func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) {
|
func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) {
|
||||||
d, err := ioutil.TempDir("", "eth-keystore-test")
|
d := t.TempDir()
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
newKs := NewPlaintextKeyStore
|
newKs := NewPlaintextKeyStore
|
||||||
if encrypted {
|
if encrypted {
|
||||||
newKs = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) }
|
newKs = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) }
|
||||||
}
|
}
|
||||||
|
|
||||||
return d, newKs(d)
|
return d, newKs(d)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
|
@ -82,10 +81,11 @@ type keyStorePassphrase struct {
|
||||||
|
|
||||||
func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) {
|
func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) {
|
||||||
// Load the key from the keystore and decrypt its contents
|
// Load the key from the keystore and decrypt its contents
|
||||||
keyjson, err := ioutil.ReadFile(filename)
|
keyjson, err := os.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := DecryptKey(keyjson, auth)
|
key, err := DecryptKey(keyjson, auth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -94,6 +94,7 @@ func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string)
|
||||||
if key.Address != addr {
|
if key.Address != addr {
|
||||||
return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr)
|
return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,6 +114,7 @@ func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ks.skipKeyFileVerification {
|
if !ks.skipKeyFileVerification {
|
||||||
// Verify that we can decrypt the file with the given password.
|
// Verify that we can decrypt the file with the given password.
|
||||||
_, err = ks.GetKey(key.Address, tmpName, auth)
|
_, err = ks.GetKey(key.Address, tmpName, auth)
|
||||||
|
|
@ -127,6 +129,7 @@ func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) er
|
||||||
return fmt.Errorf(msg, tmpName, err)
|
return fmt.Errorf(msg, tmpName, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return os.Rename(tmpName, filename)
|
return os.Rename(tmpName, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -134,30 +137,34 @@ func (ks keyStorePassphrase) JoinPath(filename string) string {
|
||||||
if filepath.IsAbs(filename) {
|
if filepath.IsAbs(filename) {
|
||||||
return filename
|
return filename
|
||||||
}
|
}
|
||||||
|
|
||||||
return filepath.Join(ks.keysDirPath, filename)
|
return filepath.Join(ks.keysDirPath, filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encryptdata encrypts the data given as 'data' with the password 'auth'.
|
// Encryptdata encrypts the data given as 'data' with the password 'auth'.
|
||||||
func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {
|
func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {
|
||||||
|
|
||||||
salt := make([]byte, 32)
|
salt := make([]byte, 32)
|
||||||
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
|
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
|
||||||
panic("reading from crypto/rand failed: " + err.Error())
|
panic("reading from crypto/rand failed: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen)
|
derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CryptoJSON{}, err
|
return CryptoJSON{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
encryptKey := derivedKey[:16]
|
encryptKey := derivedKey[:16]
|
||||||
|
|
||||||
iv := make([]byte, aes.BlockSize) // 16
|
iv := make([]byte, aes.BlockSize) // 16
|
||||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||||
panic("reading from crypto/rand failed: " + err.Error())
|
panic("reading from crypto/rand failed: " + err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
cipherText, err := aesCTRXOR(encryptKey, data, iv)
|
cipherText, err := aesCTRXOR(encryptKey, data, iv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CryptoJSON{}, err
|
return CryptoJSON{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
mac := crypto.Keccak256(derivedKey[16:32], cipherText)
|
mac := crypto.Keccak256(derivedKey[16:32], cipherText)
|
||||||
|
|
||||||
scryptParamsJSON := make(map[string]interface{}, 5)
|
scryptParamsJSON := make(map[string]interface{}, 5)
|
||||||
|
|
@ -178,6 +185,7 @@ func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error)
|
||||||
KDFParams: scryptParamsJSON,
|
KDFParams: scryptParamsJSON,
|
||||||
MAC: hex.EncodeToString(mac),
|
MAC: hex.EncodeToString(mac),
|
||||||
}
|
}
|
||||||
|
|
||||||
return cryptoStruct, nil
|
return cryptoStruct, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -185,16 +193,19 @@ func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error)
|
||||||
// blob that can be decrypted later on.
|
// blob that can be decrypted later on.
|
||||||
func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
|
func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
|
||||||
keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32)
|
keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32)
|
||||||
|
|
||||||
cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP)
|
cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
encryptedKeyJSONV3 := encryptedKeyJSONV3{
|
encryptedKeyJSONV3 := encryptedKeyJSONV3{
|
||||||
hex.EncodeToString(key.Address[:]),
|
hex.EncodeToString(key.Address[:]),
|
||||||
cryptoStruct,
|
cryptoStruct,
|
||||||
key.Id.String(),
|
key.Id.String(),
|
||||||
version,
|
version,
|
||||||
}
|
}
|
||||||
|
|
||||||
return json.Marshal(encryptedKeyJSONV3)
|
return json.Marshal(encryptedKeyJSONV3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -210,28 +221,34 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
|
||||||
keyBytes, keyId []byte
|
keyBytes, keyId []byte
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if version, ok := m["version"].(string); ok && version == "1" {
|
if version, ok := m["version"].(string); ok && version == "1" {
|
||||||
k := new(encryptedKeyJSONV1)
|
k := new(encryptedKeyJSONV1)
|
||||||
if err := json.Unmarshal(keyjson, k); err != nil {
|
if err := json.Unmarshal(keyjson, k); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
keyBytes, keyId, err = decryptKeyV1(k, auth)
|
keyBytes, keyId, err = decryptKeyV1(k, auth)
|
||||||
} else {
|
} else {
|
||||||
k := new(encryptedKeyJSONV3)
|
k := new(encryptedKeyJSONV3)
|
||||||
if err := json.Unmarshal(keyjson, k); err != nil {
|
if err := json.Unmarshal(keyjson, k); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
keyBytes, keyId, err = decryptKeyV3(k, auth)
|
keyBytes, keyId, err = decryptKeyV3(k, auth)
|
||||||
}
|
}
|
||||||
// Handle any decryption errors and return the key
|
// Handle any decryption errors and return the key
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
key := crypto.ToECDSAUnsafe(keyBytes)
|
key := crypto.ToECDSAUnsafe(keyBytes)
|
||||||
|
|
||||||
id, err := uuid.FromBytes(keyId)
|
id, err := uuid.FromBytes(keyId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &Key{
|
return &Key{
|
||||||
Id: id,
|
Id: id,
|
||||||
Address: crypto.PubkeyToAddress(key.PublicKey),
|
Address: crypto.PubkeyToAddress(key.PublicKey),
|
||||||
|
|
@ -243,6 +260,7 @@ func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
|
||||||
if cryptoJson.Cipher != "aes-128-ctr" {
|
if cryptoJson.Cipher != "aes-128-ctr" {
|
||||||
return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher)
|
return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher)
|
||||||
}
|
}
|
||||||
|
|
||||||
mac, err := hex.DecodeString(cryptoJson.MAC)
|
mac, err := hex.DecodeString(cryptoJson.MAC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -272,6 +290,7 @@ func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return plainText, err
|
return plainText, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -279,15 +298,19 @@ func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byt
|
||||||
if keyProtected.Version != version {
|
if keyProtected.Version != version {
|
||||||
return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version)
|
return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
keyUUID, err := uuid.Parse(keyProtected.Id)
|
keyUUID, err := uuid.Parse(keyProtected.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
keyId = keyUUID[:]
|
keyId = keyUUID[:]
|
||||||
|
|
||||||
plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
|
plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return plainText, keyId, err
|
return plainText, keyId, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -296,7 +319,9 @@ func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byt
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
keyId = keyUUID[:]
|
keyId = keyUUID[:]
|
||||||
|
|
||||||
mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
|
mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
|
@ -326,30 +351,36 @@ func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byt
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return plainText, keyId, err
|
return plainText, keyId, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
|
func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
|
||||||
authArray := []byte(auth)
|
authArray := []byte(auth)
|
||||||
|
|
||||||
salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
|
salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
|
dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
|
||||||
|
|
||||||
if cryptoJSON.KDF == keyHeaderKDF {
|
if cryptoJSON.KDF == keyHeaderKDF {
|
||||||
n := ensureInt(cryptoJSON.KDFParams["n"])
|
n := ensureInt(cryptoJSON.KDFParams["n"])
|
||||||
r := ensureInt(cryptoJSON.KDFParams["r"])
|
r := ensureInt(cryptoJSON.KDFParams["r"])
|
||||||
p := ensureInt(cryptoJSON.KDFParams["p"])
|
p := ensureInt(cryptoJSON.KDFParams["p"])
|
||||||
return scrypt.Key(authArray, salt, n, r, p, dkLen)
|
|
||||||
|
|
||||||
|
return scrypt.Key(authArray, salt, n, r, p, dkLen)
|
||||||
} else if cryptoJSON.KDF == "pbkdf2" {
|
} else if cryptoJSON.KDF == "pbkdf2" {
|
||||||
c := ensureInt(cryptoJSON.KDFParams["c"])
|
c := ensureInt(cryptoJSON.KDFParams["c"])
|
||||||
|
|
||||||
prf := cryptoJSON.KDFParams["prf"].(string)
|
prf := cryptoJSON.KDFParams["prf"].(string)
|
||||||
if prf != "hmac-sha256" {
|
if prf != "hmac-sha256" {
|
||||||
return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf)
|
return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf)
|
||||||
}
|
}
|
||||||
|
|
||||||
key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
|
key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
|
||||||
|
|
||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -364,5 +395,6 @@ func ensureInt(x interface{}) int {
|
||||||
if !ok {
|
if !ok {
|
||||||
res = int(x.(float64))
|
res = int(x.(float64))
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
package keystore
|
package keystore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/ioutil"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -30,10 +30,11 @@ const (
|
||||||
|
|
||||||
// Tests that a json key file can be decrypted and encrypted in multiple rounds.
|
// Tests that a json key file can be decrypted and encrypted in multiple rounds.
|
||||||
func TestKeyEncryptDecrypt(t *testing.T) {
|
func TestKeyEncryptDecrypt(t *testing.T) {
|
||||||
keyjson, err := ioutil.ReadFile("testdata/very-light-scrypt.json")
|
keyjson, err := os.ReadFile("testdata/very-light-scrypt.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
password := ""
|
password := ""
|
||||||
address := common.HexToAddress("45dea0fb0bba44f4fcf290bba71fd57d7117cbb8")
|
address := common.HexToAddress("45dea0fb0bba44f4fcf290bba71fd57d7117cbb8")
|
||||||
|
|
||||||
|
|
@ -48,11 +49,12 @@ func TestKeyEncryptDecrypt(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("test %d: json key failed to decrypt: %v", i, err)
|
t.Fatalf("test %d: json key failed to decrypt: %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if key.Address != address {
|
if key.Address != address {
|
||||||
t.Errorf("test %d: key address mismatch: have %x, want %x", i, key.Address, address)
|
t.Errorf("test %d: key address mismatch: have %x, want %x", i, key.Address, address)
|
||||||
}
|
}
|
||||||
// Recrypt with a new password and start over
|
// Recrypt with a new password and start over
|
||||||
password += "new data appended"
|
password += "new data appended" // nolint: gosec
|
||||||
if keyjson, err = EncryptKey(key, password, veryLightScryptN, veryLightScryptP); err != nil {
|
if keyjson, err = EncryptKey(key, password, veryLightScryptN, veryLightScryptP); err != nil {
|
||||||
t.Errorf("test %d: failed to recrypt key %v", i, err)
|
t.Errorf("test %d: failed to recrypt key %v", i, err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,14 +34,18 @@ func (ks keyStorePlain) GetKey(addr common.Address, filename, auth string) (*Key
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
|
|
||||||
key := new(Key)
|
key := new(Key)
|
||||||
if err := json.NewDecoder(fd).Decode(key); err != nil {
|
if err := json.NewDecoder(fd).Decode(key); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if key.Address != addr {
|
if key.Address != addr {
|
||||||
return nil, fmt.Errorf("key content mismatch: have address %x, want %x", key.Address, addr)
|
return nil, fmt.Errorf("key content mismatch: have address %x, want %x", key.Address, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return key, nil
|
return key, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,6 +54,7 @@ func (ks keyStorePlain) StoreKey(filename string, key *Key, auth string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return writeKeyFile(filename, content)
|
return writeKeyFile(filename, content)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,5 +62,6 @@ func (ks keyStorePlain) JoinPath(filename string) string {
|
||||||
if filepath.IsAbs(filename) {
|
if filepath.IsAbs(filename) {
|
||||||
return filename
|
return filename
|
||||||
}
|
}
|
||||||
|
|
||||||
return filepath.Join(ks.keysDirPath, filename)
|
return filepath.Join(ks.keysDirPath, filename)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,6 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -32,69 +30,74 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func tmpKeyStoreIface(t *testing.T, encrypted bool) (dir string, ks keyStore) {
|
func tmpKeyStoreIface(t *testing.T, encrypted bool) (dir string, ks keyStore) {
|
||||||
d, err := ioutil.TempDir("", "geth-keystore-test")
|
d := t.TempDir()
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if encrypted {
|
if encrypted {
|
||||||
ks = &keyStorePassphrase{d, veryLightScryptN, veryLightScryptP, true}
|
ks = &keyStorePassphrase{d, veryLightScryptN, veryLightScryptP, true}
|
||||||
} else {
|
} else {
|
||||||
ks = &keyStorePlain{d}
|
ks = &keyStorePlain{d}
|
||||||
}
|
}
|
||||||
|
|
||||||
return d, ks
|
return d, ks
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyStorePlain(t *testing.T) {
|
func TestKeyStorePlain(t *testing.T) {
|
||||||
dir, ks := tmpKeyStoreIface(t, false)
|
_, ks := tmpKeyStoreIface(t, false)
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
pass := "" // not used but required by API
|
pass := "" // not used but required by API
|
||||||
|
|
||||||
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
k2, err := ks.GetKey(k1.Address, account.URL.Path, pass)
|
k2, err := ks.GetKey(k1.Address, account.URL.Path, pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(k1.Address, k2.Address) {
|
if !reflect.DeepEqual(k1.Address, k2.Address) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
|
if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyStorePassphrase(t *testing.T) {
|
func TestKeyStorePassphrase(t *testing.T) {
|
||||||
dir, ks := tmpKeyStoreIface(t, true)
|
_, ks := tmpKeyStoreIface(t, true)
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
pass := "foo"
|
pass := "foo"
|
||||||
|
|
||||||
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
k2, err := ks.GetKey(k1.Address, account.URL.Path, pass)
|
k2, err := ks.GetKey(k1.Address, account.URL.Path, pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(k1.Address, k2.Address) {
|
if !reflect.DeepEqual(k1.Address, k2.Address) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
|
if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
|
func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
|
||||||
dir, ks := tmpKeyStoreIface(t, true)
|
_, ks := tmpKeyStoreIface(t, true)
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
pass := "foo"
|
pass := "foo"
|
||||||
|
|
||||||
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
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 password\ngot %q\nwant %q", err, ErrDecrypt)
|
t.Fatalf("wrong error for invalid password\ngot %q\nwant %q", err, ErrDecrypt)
|
||||||
}
|
}
|
||||||
|
|
@ -102,20 +105,22 @@ func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
|
||||||
|
|
||||||
func TestImportPreSaleKey(t *testing.T) {
|
func TestImportPreSaleKey(t *testing.T) {
|
||||||
dir, ks := tmpKeyStoreIface(t, true)
|
dir, ks := tmpKeyStoreIface(t, true)
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
|
|
||||||
// file content of a presale key file generated with:
|
// file content of a presale key file generated with:
|
||||||
// python pyethsaletool.py genwallet
|
// python pyethsaletool.py genwallet
|
||||||
// with password "foo"
|
// with password "foo"
|
||||||
fileContent := "{\"encseed\": \"26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba\", \"ethaddr\": \"d4584b5f6229b7be90727b0fc8c6b91bb427821f\", \"email\": \"gustav.simonsson@gmail.com\", \"btcaddr\": \"1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx\"}"
|
fileContent := "{\"encseed\": \"26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba\", \"ethaddr\": \"d4584b5f6229b7be90727b0fc8c6b91bb427821f\", \"email\": \"gustav.simonsson@gmail.com\", \"btcaddr\": \"1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx\"}"
|
||||||
pass := "foo"
|
pass := "foo"
|
||||||
|
|
||||||
account, _, err := importPreSaleKey(ks, []byte(fileContent), pass)
|
account, _, err := importPreSaleKey(ks, []byte(fileContent), pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if account.Address != common.HexToAddress("d4584b5f6229b7be90727b0fc8c6b91bb427821f") {
|
if account.Address != common.HexToAddress("d4584b5f6229b7be90727b0fc8c6b91bb427821f") {
|
||||||
t.Errorf("imported account has wrong address %x", account.Address)
|
t.Errorf("imported account has wrong address %x", account.Address)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(account.URL.Path, dir) {
|
if !strings.HasPrefix(account.URL.Path, dir) {
|
||||||
t.Errorf("imported account file not in keystore directory: %q", account.URL)
|
t.Errorf("imported account file not in keystore directory: %q", account.URL)
|
||||||
}
|
}
|
||||||
|
|
@ -191,15 +196,19 @@ func TestV1_1(t *testing.T) {
|
||||||
|
|
||||||
func TestV1_2(t *testing.T) {
|
func TestV1_2(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
ks := &keyStorePassphrase{"testdata/v1", LightScryptN, LightScryptP, true}
|
ks := &keyStorePassphrase{"testdata/v1", LightScryptN, LightScryptP, true}
|
||||||
addr := common.HexToAddress("cb61d5a9c4896fb9658090b597ef0e7be6f7b67e")
|
addr := common.HexToAddress("cb61d5a9c4896fb9658090b597ef0e7be6f7b67e")
|
||||||
file := "testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e"
|
file := "testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e"
|
||||||
|
|
||||||
k, err := ks.GetKey(addr, file, "g")
|
k, err := ks.GetKey(addr, file, "g")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
privHex := hex.EncodeToString(crypto.FromECDSA(k.PrivateKey))
|
privHex := hex.EncodeToString(crypto.FromECDSA(k.PrivateKey))
|
||||||
expectedHex := "d1b1178d3529626a1a93e073f65028370d14c7eb0936eb42abef05db6f37ad7d"
|
expectedHex := "d1b1178d3529626a1a93e073f65028370d14c7eb0936eb42abef05db6f37ad7d"
|
||||||
|
|
||||||
if privHex != expectedHex {
|
if privHex != expectedHex {
|
||||||
t.Fatal(fmt.Errorf("Unexpected privkey: %v, expected %v", privHex, expectedHex))
|
t.Fatal(fmt.Errorf("Unexpected privkey: %v, expected %v", privHex, expectedHex))
|
||||||
}
|
}
|
||||||
|
|
@ -210,6 +219,7 @@ func testDecryptV3(test KeyStoreTestV3, t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
privHex := hex.EncodeToString(privBytes)
|
privHex := hex.EncodeToString(privBytes)
|
||||||
if test.Priv != privHex {
|
if test.Priv != privHex {
|
||||||
t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex))
|
t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex))
|
||||||
|
|
@ -221,6 +231,7 @@ func testDecryptV1(test KeyStoreTestV1, t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
privHex := hex.EncodeToString(privBytes)
|
privHex := hex.EncodeToString(privBytes)
|
||||||
if test.Priv != privHex {
|
if test.Priv != privHex {
|
||||||
t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex))
|
t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex))
|
||||||
|
|
@ -229,24 +240,29 @@ func testDecryptV1(test KeyStoreTestV1, t *testing.T) {
|
||||||
|
|
||||||
func loadKeyStoreTestV3(file string, t *testing.T) map[string]KeyStoreTestV3 {
|
func loadKeyStoreTestV3(file string, t *testing.T) map[string]KeyStoreTestV3 {
|
||||||
tests := make(map[string]KeyStoreTestV3)
|
tests := make(map[string]KeyStoreTestV3)
|
||||||
|
|
||||||
err := common.LoadJSON(file, &tests)
|
err := common.LoadJSON(file, &tests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return tests
|
return tests
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadKeyStoreTestV1(file string, t *testing.T) map[string]KeyStoreTestV1 {
|
func loadKeyStoreTestV1(file string, t *testing.T) map[string]KeyStoreTestV1 {
|
||||||
tests := make(map[string]KeyStoreTestV1)
|
tests := make(map[string]KeyStoreTestV1)
|
||||||
|
|
||||||
err := common.LoadJSON(file, &tests)
|
err := common.LoadJSON(file, &tests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return tests
|
return tests
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKeyForDirectICAP(t *testing.T) {
|
func TestKeyForDirectICAP(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
key := NewKeyForDirectICAP(rand.Reader)
|
key := NewKeyForDirectICAP(rand.Reader)
|
||||||
if !strings.HasPrefix(key.Address.Hex(), "0x00") {
|
if !strings.HasPrefix(key.Address.Hex(), "0x00") {
|
||||||
t.Errorf("Expected first address byte to be zero, have: %s", key.Address.Hex())
|
t.Errorf("Expected first address byte to be zero, have: %s", key.Address.Hex())
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,12 @@ func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accou
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return accounts.Account{}, nil, err
|
return accounts.Account{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
key.Id, err = uuid.NewRandom()
|
key.Id, err = uuid.NewRandom()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return accounts.Account{}, nil, err
|
return accounts.Account{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
a := accounts.Account{
|
a := accounts.Account{
|
||||||
Address: key.Address,
|
Address: key.Address,
|
||||||
URL: accounts.URL{
|
URL: accounts.URL{
|
||||||
|
|
@ -49,6 +51,7 @@ func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accou
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err = keyStore.StoreKey(a.URL.Path, key, password)
|
err = keyStore.StoreKey(a.URL.Path, key, password)
|
||||||
|
|
||||||
return a, key, err
|
return a, key, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,17 +62,21 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
|
||||||
Email string
|
Email string
|
||||||
BtcAddr string
|
BtcAddr string
|
||||||
}{}
|
}{}
|
||||||
|
|
||||||
err = json.Unmarshal(fileContent, &preSaleKeyStruct)
|
err = json.Unmarshal(fileContent, &preSaleKeyStruct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed)
|
encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("invalid hex in encSeed")
|
return nil, errors.New("invalid hex in encSeed")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(encSeedBytes) < 16 {
|
if len(encSeedBytes) < 16 {
|
||||||
return nil, errors.New("invalid encSeed, too short")
|
return nil, errors.New("invalid encSeed, too short")
|
||||||
}
|
}
|
||||||
|
|
||||||
iv := encSeedBytes[:16]
|
iv := encSeedBytes[:16]
|
||||||
cipherText := encSeedBytes[16:]
|
cipherText := encSeedBytes[16:]
|
||||||
/*
|
/*
|
||||||
|
|
@ -81,10 +88,12 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
|
||||||
*/
|
*/
|
||||||
passBytes := []byte(password)
|
passBytes := []byte(password)
|
||||||
derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New)
|
derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New)
|
||||||
|
|
||||||
plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
|
plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ethPriv := crypto.Keccak256(plainText)
|
ethPriv := crypto.Keccak256(plainText)
|
||||||
ecKey := crypto.ToECDSAUnsafe(ethPriv)
|
ecKey := crypto.ToECDSAUnsafe(ethPriv)
|
||||||
|
|
||||||
|
|
@ -95,9 +104,11 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
|
||||||
}
|
}
|
||||||
derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x"
|
derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x"
|
||||||
expectedAddr := preSaleKeyStruct.EthAddr
|
expectedAddr := preSaleKeyStruct.EthAddr
|
||||||
|
|
||||||
if derivedAddr != expectedAddr {
|
if derivedAddr != expectedAddr {
|
||||||
err = fmt.Errorf("decrypted addr '%s' not equal to expected addr '%s'", derivedAddr, expectedAddr)
|
err = fmt.Errorf("decrypted addr '%s' not equal to expected addr '%s'", derivedAddr, expectedAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return key, err
|
return key, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,9 +118,11 @@ func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
stream := cipher.NewCTR(aesBlock, iv)
|
stream := cipher.NewCTR(aesBlock, iv)
|
||||||
outText := make([]byte, len(inText))
|
outText := make([]byte, len(inText))
|
||||||
stream.XORKeyStream(outText, inText)
|
stream.XORKeyStream(outText, inText)
|
||||||
|
|
||||||
return outText, err
|
return outText, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,13 +131,16 @@ func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
|
decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
|
||||||
paddedPlaintext := make([]byte, len(cipherText))
|
paddedPlaintext := make([]byte, len(cipherText))
|
||||||
decrypter.CryptBlocks(paddedPlaintext, cipherText)
|
decrypter.CryptBlocks(paddedPlaintext, cipherText)
|
||||||
|
|
||||||
plaintext := pkcs7Unpad(paddedPlaintext)
|
plaintext := pkcs7Unpad(paddedPlaintext)
|
||||||
if plaintext == nil {
|
if plaintext == nil {
|
||||||
return nil, ErrDecrypt
|
return nil, ErrDecrypt
|
||||||
}
|
}
|
||||||
|
|
||||||
return plaintext, err
|
return plaintext, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,5 +162,6 @@ func pkcs7Unpad(in []byte) []byte {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return in[:len(in)-int(padding)]
|
return in[:len(in)-int(padding)]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ func (w *keystoreWallet) Status() (string, error) {
|
||||||
if _, ok := w.keystore.unlocked[w.account.Address]; ok {
|
if _, ok := w.keystore.unlocked[w.account.Address]; ok {
|
||||||
return "Unlocked", nil
|
return "Unlocked", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Locked", nil
|
return "Locked", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,26 +22,29 @@ package keystore
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/rjeczalik/notify"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type watcher struct {
|
type watcher struct {
|
||||||
ac *accountCache
|
ac *accountCache
|
||||||
starting bool
|
running bool // set to true when runloop begins
|
||||||
running bool
|
runEnded bool // set to true when runloop ends
|
||||||
ev chan notify.EventInfo
|
starting bool // set to true prior to runloop starting
|
||||||
quit chan struct{}
|
quit chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newWatcher(ac *accountCache) *watcher {
|
func newWatcher(ac *accountCache) *watcher {
|
||||||
return &watcher{
|
return &watcher{
|
||||||
ac: ac,
|
ac: ac,
|
||||||
ev: make(chan notify.EventInfo, 10),
|
|
||||||
quit: make(chan struct{}),
|
quit: make(chan struct{}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// enabled returns false on systems not supported.
|
||||||
|
func (*watcher) enabled() bool { return true }
|
||||||
|
|
||||||
// starts the watcher loop in the background.
|
// starts the watcher loop in the background.
|
||||||
// Start a watcher in the background if that's not already in progress.
|
// Start a watcher in the background if that's not already in progress.
|
||||||
// The caller must hold w.ac.mu.
|
// The caller must hold w.ac.mu.
|
||||||
|
|
@ -49,6 +52,7 @@ func (w *watcher) start() {
|
||||||
if w.starting || w.running {
|
if w.starting || w.running {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
w.starting = true
|
w.starting = true
|
||||||
go w.loop()
|
go w.loop()
|
||||||
}
|
}
|
||||||
|
|
@ -62,16 +66,27 @@ func (w *watcher) loop() {
|
||||||
w.ac.mu.Lock()
|
w.ac.mu.Lock()
|
||||||
w.running = false
|
w.running = false
|
||||||
w.starting = false
|
w.starting = false
|
||||||
|
w.runEnded = true
|
||||||
w.ac.mu.Unlock()
|
w.ac.mu.Unlock()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
logger := log.New("path", w.ac.keydir)
|
logger := log.New("path", w.ac.keydir)
|
||||||
|
|
||||||
if err := notify.Watch(w.ac.keydir, w.ev, notify.All); err != nil {
|
// Create new watcher.
|
||||||
logger.Trace("Failed to watch keystore folder", "err", err)
|
watcher, err := fsnotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Failed to start filesystem watcher", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer notify.Stop(w.ev)
|
|
||||||
logger.Trace("Started watching keystore folder")
|
defer watcher.Close()
|
||||||
|
|
||||||
|
if err := watcher.Add(w.ac.keydir); err != nil {
|
||||||
|
logger.Warn("Failed to watch keystore folder", "err", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Trace("Started watching keystore folder", "folder", w.ac.keydir)
|
||||||
defer logger.Trace("Stopped watching keystore folder")
|
defer logger.Trace("Stopped watching keystore folder")
|
||||||
|
|
||||||
w.ac.mu.Lock()
|
w.ac.mu.Lock()
|
||||||
|
|
@ -91,18 +106,34 @@ func (w *watcher) loop() {
|
||||||
<-debounce.C
|
<-debounce.C
|
||||||
}
|
}
|
||||||
defer debounce.Stop()
|
defer debounce.Stop()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-w.quit:
|
case <-w.quit:
|
||||||
return
|
return
|
||||||
case <-w.ev:
|
case _, ok := <-watcher.Events:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
// Trigger the scan (with delay), if not already triggered
|
// Trigger the scan (with delay), if not already triggered
|
||||||
if !rescanTriggered {
|
if !rescanTriggered {
|
||||||
debounce.Reset(debounceDuration)
|
debounce.Reset(debounceDuration)
|
||||||
|
|
||||||
rescanTriggered = true
|
rescanTriggered = true
|
||||||
}
|
}
|
||||||
|
// The fsnotify library does provide more granular event-info, it
|
||||||
|
// would be possible to refresh individual affected files instead
|
||||||
|
// of scheduling a full rescan. For most cases though, the
|
||||||
|
// full rescan is quick and obviously simplest.
|
||||||
|
case err, ok := <-watcher.Errors:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("Filsystem watcher error", "err", err)
|
||||||
case <-debounce.C:
|
case <-debounce.C:
|
||||||
w.ac.scanAccounts()
|
w.ac.scanAccounts()
|
||||||
|
|
||||||
rescanTriggered = false
|
rescanTriggered = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,14 @@
|
||||||
|
|
||||||
package keystore
|
package keystore
|
||||||
|
|
||||||
type watcher struct{ running bool }
|
type watcher struct {
|
||||||
|
running bool
|
||||||
|
runEnded bool
|
||||||
|
}
|
||||||
|
|
||||||
func newWatcher(*accountCache) *watcher { return new(watcher) }
|
func newWatcher(*accountCache) *watcher { return new(watcher) }
|
||||||
func (*watcher) start() {}
|
func (*watcher) start() {}
|
||||||
func (*watcher) close() {}
|
func (*watcher) close() {}
|
||||||
|
|
||||||
|
// enabled returns false on systems not supported.
|
||||||
|
func (*watcher) enabled() bool { return false }
|
||||||
|
|
|
||||||
|
|
@ -87,10 +87,12 @@ func NewManager(config *Config, backends ...Backend) *Manager {
|
||||||
quit: make(chan chan error),
|
quit: make(chan chan error),
|
||||||
term: make(chan struct{}),
|
term: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, backend := range backends {
|
for _, backend := range backends {
|
||||||
kind := reflect.TypeOf(backend)
|
kind := reflect.TypeOf(backend)
|
||||||
am.backends[kind] = append(am.backends[kind], backend)
|
am.backends[kind] = append(am.backends[kind], backend)
|
||||||
}
|
}
|
||||||
|
|
||||||
go am.update()
|
go am.update()
|
||||||
|
|
||||||
return am
|
return am
|
||||||
|
|
@ -100,6 +102,7 @@ func NewManager(config *Config, backends ...Backend) *Manager {
|
||||||
func (am *Manager) Close() error {
|
func (am *Manager) Close() error {
|
||||||
errc := make(chan error)
|
errc := make(chan error)
|
||||||
am.quit <- errc
|
am.quit <- errc
|
||||||
|
|
||||||
return <-errc
|
return <-errc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,6 +116,7 @@ func (am *Manager) Config() *Config {
|
||||||
func (am *Manager) AddBackend(backend Backend) {
|
func (am *Manager) AddBackend(backend Backend) {
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
am.newBackends <- newBackendEvent{backend, done}
|
am.newBackends <- newBackendEvent{backend, done}
|
||||||
|
|
||||||
<-done
|
<-done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -125,6 +129,7 @@ func (am *Manager) update() {
|
||||||
for _, sub := range am.updaters {
|
for _, sub := range am.updaters {
|
||||||
sub.Unsubscribe()
|
sub.Unsubscribe()
|
||||||
}
|
}
|
||||||
|
|
||||||
am.updaters = nil
|
am.updaters = nil
|
||||||
am.lock.Unlock()
|
am.lock.Unlock()
|
||||||
}()
|
}()
|
||||||
|
|
@ -161,6 +166,7 @@ func (am *Manager) update() {
|
||||||
// Signals event emitters the loop is not receiving values
|
// Signals event emitters the loop is not receiving values
|
||||||
// to prevent them from getting stuck.
|
// to prevent them from getting stuck.
|
||||||
close(am.term)
|
close(am.term)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -186,6 +192,7 @@ func (am *Manager) Wallets() []Wallet {
|
||||||
func (am *Manager) walletsNoLock() []Wallet {
|
func (am *Manager) walletsNoLock() []Wallet {
|
||||||
cpy := make([]Wallet, len(am.wallets))
|
cpy := make([]Wallet, len(am.wallets))
|
||||||
copy(cpy, am.wallets)
|
copy(cpy, am.wallets)
|
||||||
|
|
||||||
return cpy
|
return cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -198,11 +205,13 @@ func (am *Manager) Wallet(url string) (Wallet, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, wallet := range am.walletsNoLock() {
|
for _, wallet := range am.walletsNoLock() {
|
||||||
if wallet.URL() == parsed {
|
if wallet.URL() == parsed {
|
||||||
return wallet, nil
|
return wallet, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, ErrUnknownWallet
|
return nil, ErrUnknownWallet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,11 +221,13 @@ func (am *Manager) Accounts() []common.Address {
|
||||||
defer am.lock.RUnlock()
|
defer am.lock.RUnlock()
|
||||||
|
|
||||||
addresses := make([]common.Address, 0) // return [] instead of nil if empty
|
addresses := make([]common.Address, 0) // return [] instead of nil if empty
|
||||||
|
|
||||||
for _, wallet := range am.wallets {
|
for _, wallet := range am.wallets {
|
||||||
for _, account := range wallet.Accounts() {
|
for _, account := range wallet.Accounts() {
|
||||||
addresses = append(addresses, account.Address)
|
addresses = append(addresses, account.Address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return addresses
|
return addresses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -232,6 +243,7 @@ func (am *Manager) Find(account Account) (Wallet, error) {
|
||||||
return wallet, nil
|
return wallet, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, ErrUnknownAccount
|
return nil, ErrUnknownAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -252,8 +264,10 @@ func merge(slice []Wallet, wallets ...Wallet) []Wallet {
|
||||||
slice = append(slice, wallet)
|
slice = append(slice, wallet)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
slice = append(slice[:n], append([]Wallet{wallet}, slice[n:]...)...)
|
slice = append(slice[:n], append([]Wallet{wallet}, slice[n:]...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return slice
|
return slice
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -266,7 +280,9 @@ func drop(slice []Wallet, wallets ...Wallet) []Wallet {
|
||||||
// Wallet not found, may happen during startup
|
// Wallet not found, may happen during startup
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
slice = append(slice[:n], slice[n+1:]...)
|
slice = append(slice[:n], slice[n+1:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return slice
|
return slice
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,26 +36,33 @@ func (ca commandAPDU) serialize() ([]byte, error) {
|
||||||
if err := binary.Write(buf, binary.BigEndian, ca.Cla); err != nil {
|
if err := binary.Write(buf, binary.BigEndian, ca.Cla); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := binary.Write(buf, binary.BigEndian, ca.Ins); err != nil {
|
if err := binary.Write(buf, binary.BigEndian, ca.Ins); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := binary.Write(buf, binary.BigEndian, ca.P1); err != nil {
|
if err := binary.Write(buf, binary.BigEndian, ca.P1); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := binary.Write(buf, binary.BigEndian, ca.P2); err != nil {
|
if err := binary.Write(buf, binary.BigEndian, ca.P2); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(ca.Data) > 0 {
|
if len(ca.Data) > 0 {
|
||||||
if err := binary.Write(buf, binary.BigEndian, uint8(len(ca.Data))); err != nil {
|
if err := binary.Write(buf, binary.BigEndian, uint8(len(ca.Data))); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := binary.Write(buf, binary.BigEndian, ca.Data); err != nil {
|
if err := binary.Write(buf, binary.BigEndian, ca.Data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := binary.Write(buf, binary.BigEndian, ca.Le); err != nil {
|
if err := binary.Write(buf, binary.BigEndian, ca.Le); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return buf.Bytes(), nil
|
return buf.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,11 +84,14 @@ func (ra *responseAPDU) deserialize(data []byte) error {
|
||||||
if err := binary.Read(buf, binary.BigEndian, &ra.Data); err != nil {
|
if err := binary.Read(buf, binary.BigEndian, &ra.Data); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := binary.Read(buf, binary.BigEndian, &ra.Sw1); err != nil {
|
if err := binary.Read(buf, binary.BigEndian, &ra.Sw1); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := binary.Read(buf, binary.BigEndian, &ra.Sw2); err != nil {
|
if err := binary.Read(buf, binary.BigEndian, &ra.Sw2); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ package scwallet
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
@ -88,18 +88,21 @@ type Hub struct {
|
||||||
|
|
||||||
func (hub *Hub) readPairings() error {
|
func (hub *Hub) readPairings() error {
|
||||||
hub.pairings = make(map[string]smartcardPairing)
|
hub.pairings = make(map[string]smartcardPairing)
|
||||||
|
|
||||||
pairingFile, err := os.Open(filepath.Join(hub.datadir, "smartcards.json"))
|
pairingFile, err := os.Open(filepath.Join(hub.datadir, "smartcards.json"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
pairingData, err := ioutil.ReadAll(pairingFile)
|
pairingData, err := io.ReadAll(pairingFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var pairings []smartcardPairing
|
var pairings []smartcardPairing
|
||||||
if err := json.Unmarshal(pairingData, &pairings); err != nil {
|
if err := json.Unmarshal(pairingData, &pairings); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -108,6 +111,7 @@ func (hub *Hub) readPairings() error {
|
||||||
for _, pairing := range pairings {
|
for _, pairing := range pairings {
|
||||||
hub.pairings[string(pairing.PublicKey)] = pairing
|
hub.pairings[string(pairing.PublicKey)] = pairing
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,6 +143,7 @@ func (hub *Hub) pairing(wallet *Wallet) *smartcardPairing {
|
||||||
if pairing, ok := hub.pairings[string(wallet.PublicKey)]; ok {
|
if pairing, ok := hub.pairings[string(wallet.PublicKey)]; ok {
|
||||||
return &pairing
|
return &pairing
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,6 +153,7 @@ func (hub *Hub) setPairing(wallet *Wallet, pairing *smartcardPairing) error {
|
||||||
} else {
|
} else {
|
||||||
hub.pairings[string(wallet.PublicKey)] = *pairing
|
hub.pairings[string(wallet.PublicKey)] = *pairing
|
||||||
}
|
}
|
||||||
|
|
||||||
return hub.writePairings()
|
return hub.writePairings()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,6 +163,7 @@ func NewHub(daemonPath string, scheme string, datadir string) (*Hub, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hub := &Hub{
|
hub := &Hub{
|
||||||
scheme: scheme,
|
scheme: scheme,
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -167,7 +174,9 @@ func NewHub(daemonPath string, scheme string, datadir string) (*Hub, error) {
|
||||||
if err := hub.readPairings(); err != nil {
|
if err := hub.readPairings(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hub.refreshWallets()
|
hub.refreshWallets()
|
||||||
|
|
||||||
return hub, nil
|
return hub, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,7 +193,9 @@ func (hub *Hub) Wallets() []accounts.Wallet {
|
||||||
for _, wallet := range hub.wallets {
|
for _, wallet := range hub.wallets {
|
||||||
cpy = append(cpy, wallet)
|
cpy = append(cpy, wallet)
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Sort(accounts.WalletsByURL(cpy))
|
sort.Sort(accounts.WalletsByURL(cpy))
|
||||||
|
|
||||||
return cpy
|
return cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -225,8 +236,10 @@ func (hub *Hub) refreshWallets() {
|
||||||
if err := wallet.ping(); err == nil {
|
if err := wallet.ping(); err == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
wallet.Close()
|
wallet.Close()
|
||||||
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
|
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
|
||||||
|
|
||||||
delete(hub.wallets, reader)
|
delete(hub.wallets, reader)
|
||||||
}
|
}
|
||||||
// New card detected, try to connect to it
|
// New card detected, try to connect to it
|
||||||
|
|
@ -235,10 +248,12 @@ func (hub *Hub) refreshWallets() {
|
||||||
log.Debug("Failed to open smart card", "reader", reader, "err", err)
|
log.Debug("Failed to open smart card", "reader", reader, "err", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
wallet := NewWallet(hub, card)
|
wallet := NewWallet(hub, card)
|
||||||
if err = wallet.connect(); err != nil {
|
if err = wallet.connect(); err != nil {
|
||||||
log.Debug("Failed to connect to smart card", "reader", reader, "err", err)
|
log.Debug("Failed to connect to smart card", "reader", reader, "err", err)
|
||||||
card.Disconnect(pcsc.LeaveCard)
|
card.Disconnect(pcsc.LeaveCard)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Card connected, start tracking in amongs the wallets
|
// Card connected, start tracking in amongs the wallets
|
||||||
|
|
@ -250,9 +265,11 @@ func (hub *Hub) refreshWallets() {
|
||||||
if _, ok := seen[reader]; !ok {
|
if _, ok := seen[reader]; !ok {
|
||||||
wallet.Close()
|
wallet.Close()
|
||||||
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
|
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
|
||||||
|
|
||||||
delete(hub.wallets, reader)
|
delete(hub.wallets, reader)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hub.refreshed = time.Now()
|
hub.refreshed = time.Now()
|
||||||
hub.stateLock.Unlock()
|
hub.stateLock.Unlock()
|
||||||
|
|
||||||
|
|
@ -276,6 +293,7 @@ func (hub *Hub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
|
||||||
hub.updating = true
|
hub.updating = true
|
||||||
go hub.updater()
|
go hub.updater()
|
||||||
}
|
}
|
||||||
|
|
||||||
return sub
|
return sub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -295,6 +313,7 @@ func (hub *Hub) updater() {
|
||||||
if hub.updateScope.Count() == 0 {
|
if hub.updateScope.Count() == 0 {
|
||||||
hub.updating = false
|
hub.updating = false
|
||||||
hub.stateLock.Unlock()
|
hub.stateLock.Unlock()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
hub.stateLock.Unlock()
|
hub.stateLock.Unlock()
|
||||||
|
|
|
||||||
|
|
@ -67,11 +67,14 @@ func NewSecureChannelSession(card *pcsc.Card, keyData []byte) (*SecureChannelSes
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
cardPublic, err := crypto.UnmarshalPubkey(keyData)
|
cardPublic, err := crypto.UnmarshalPubkey(keyData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not unmarshal public key from card: %v", err)
|
return nil, fmt.Errorf("could not unmarshal public key from card: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
secret, _ := key.Curve.ScalarMult(cardPublic.X, cardPublic.Y, key.D.Bytes())
|
secret, _ := key.Curve.ScalarMult(cardPublic.X, cardPublic.Y, key.D.Bytes())
|
||||||
|
|
||||||
return &SecureChannelSession{
|
return &SecureChannelSession{
|
||||||
card: card,
|
card: card,
|
||||||
secret: secret.Bytes(),
|
secret: secret.Bytes(),
|
||||||
|
|
@ -108,6 +111,7 @@ func (s *SecureChannelSession) Pair(pairingPassword []byte) error {
|
||||||
md.Reset()
|
md.Reset()
|
||||||
md.Write(secretHash[:])
|
md.Write(secretHash[:])
|
||||||
md.Write(cardChallenge)
|
md.Write(cardChallenge)
|
||||||
|
|
||||||
response, err = s.pair(pairP1LastStep, md.Sum(nil))
|
response, err = s.pair(pairP1LastStep, md.Sum(nil))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -132,9 +136,11 @@ func (s *SecureChannelSession) Unpair() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.PairingKey = nil
|
s.PairingKey = nil
|
||||||
// Close channel
|
// Close channel
|
||||||
s.iv = nil
|
s.iv = nil
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -177,8 +183,9 @@ func (s *SecureChannelSession) mutuallyAuthenticate() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.Sw1 != 0x90 || response.Sw2 != 0x00 {
|
if response.Sw1 != 0x90 || response.Sw2 != 0x00 {
|
||||||
return fmt.Errorf("got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2)
|
return fmt.Errorf("got unexpected response from MUTUALLY_AUTHENTICATE: %#x%x", response.Sw1, response.Sw2)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(response.Data) != scSecretLength {
|
if len(response.Data) != scSecretLength {
|
||||||
|
|
@ -222,6 +229,7 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
meta := [16]byte{cla, ins, p1, p2, byte(len(data) + scBlockSize)}
|
meta := [16]byte{cla, ins, p1, p2, byte(len(data) + scBlockSize)}
|
||||||
if err = s.updateIV(meta[:], data); err != nil {
|
if err = s.updateIV(meta[:], data); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -245,6 +253,7 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
|
||||||
rmeta := [16]byte{byte(len(response.Data))}
|
rmeta := [16]byte{byte(len(response.Data))}
|
||||||
rmac := response.Data[:len(s.iv)]
|
rmac := response.Data[:len(s.iv)]
|
||||||
rdata := response.Data[len(s.iv):]
|
rdata := response.Data[len(s.iv):]
|
||||||
|
|
||||||
plainData, err := s.decryptAPDU(rdata)
|
plainData, err := s.decryptAPDU(rdata)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -253,6 +262,7 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
|
||||||
if err = s.updateIV(rmeta[:], rdata); err != nil {
|
if err = s.updateIV(rmeta[:], rdata); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !bytes.Equal(s.iv, rmac) {
|
if !bytes.Equal(s.iv, rmac) {
|
||||||
return nil, fmt.Errorf("invalid MAC in response")
|
return nil, fmt.Errorf("invalid MAC in response")
|
||||||
}
|
}
|
||||||
|
|
@ -261,7 +271,7 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
|
||||||
rapdu.deserialize(plainData)
|
rapdu.deserialize(plainData)
|
||||||
|
|
||||||
if rapdu.Sw1 != sw1Ok {
|
if rapdu.Sw1 != sw1Ok {
|
||||||
return nil, fmt.Errorf("unexpected response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
|
return nil, fmt.Errorf("unexpected response status Cla=%#x, Ins=%#x, Sw=%#x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
|
||||||
}
|
}
|
||||||
|
|
||||||
return rapdu, nil
|
return rapdu, nil
|
||||||
|
|
@ -272,6 +282,7 @@ func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
|
||||||
if len(data) > maxPayloadSize {
|
if len(data) > maxPayloadSize {
|
||||||
return nil, fmt.Errorf("payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize)
|
return nil, fmt.Errorf("payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
data = pad(data, 0x80)
|
data = pad(data, 0x80)
|
||||||
|
|
||||||
ret := make([]byte, len(data))
|
ret := make([]byte, len(data))
|
||||||
|
|
@ -280,8 +291,10 @@ func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
crypter := cipher.NewCBCEncrypter(a, s.iv)
|
crypter := cipher.NewCBCEncrypter(a, s.iv)
|
||||||
crypter.CryptBlocks(ret, data)
|
crypter.CryptBlocks(ret, data)
|
||||||
|
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,6 +303,7 @@ func pad(data []byte, terminator byte) []byte {
|
||||||
padded := make([]byte, (len(data)/16+1)*16)
|
padded := make([]byte, (len(data)/16+1)*16)
|
||||||
copy(padded, data)
|
copy(padded, data)
|
||||||
padded[len(data)] = terminator
|
padded[len(data)] = terminator
|
||||||
|
|
||||||
return padded
|
return padded
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -304,6 +318,7 @@ func (s *SecureChannelSession) decryptAPDU(data []byte) ([]byte, error) {
|
||||||
|
|
||||||
crypter := cipher.NewCBCDecrypter(a, s.iv)
|
crypter := cipher.NewCBCDecrypter(a, s.iv)
|
||||||
crypter.CryptBlocks(ret, data)
|
crypter.CryptBlocks(ret, data)
|
||||||
|
|
||||||
return unpad(ret, 0x80)
|
return unpad(ret, 0x80)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -319,6 +334,7 @@ func unpad(data []byte, terminator byte) ([]byte, error) {
|
||||||
return nil, fmt.Errorf("expected end of padding, got %d", data[len(data)-i])
|
return nil, fmt.Errorf("expected end of padding, got %d", data[len(data)-i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("expected end of padding, got 0")
|
return nil, fmt.Errorf("expected end of padding, got 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -326,14 +342,17 @@ func unpad(data []byte, terminator byte) ([]byte, error) {
|
||||||
// each message exchanged.
|
// each message exchanged.
|
||||||
func (s *SecureChannelSession) updateIV(meta, data []byte) error {
|
func (s *SecureChannelSession) updateIV(meta, data []byte) error {
|
||||||
data = pad(data, 0)
|
data = pad(data, 0)
|
||||||
|
|
||||||
a, err := aes.NewCipher(s.sessionMacKey)
|
a, err := aes.NewCipher(s.sessionMacKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
crypter := cipher.NewCBCEncrypter(a, make([]byte, 16))
|
crypter := cipher.NewCBCEncrypter(a, make([]byte, 16))
|
||||||
crypter.CryptBlocks(meta, meta)
|
crypter.CryptBlocks(meta, meta)
|
||||||
crypter.CryptBlocks(data, data)
|
crypter.CryptBlocks(data, data)
|
||||||
// The first 16 bytes of the last block is the MAC
|
// The first 16 bytes of the last block is the MAC
|
||||||
s.iv = data[len(data)-32 : len(data)-16]
|
s.iv = data[len(data)-32 : len(data)-16]
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,8 @@ const (
|
||||||
P1DeriveKeyFromCurrent = uint8(0x10)
|
P1DeriveKeyFromCurrent = uint8(0x10)
|
||||||
statusP1WalletStatus = uint8(0x00)
|
statusP1WalletStatus = uint8(0x00)
|
||||||
statusP1Path = uint8(0x01)
|
statusP1Path = uint8(0x01)
|
||||||
signP1PrecomputedHash = uint8(0x01)
|
signP1PrecomputedHash = uint8(0x00)
|
||||||
signP2OnlyBlock = uint8(0x81)
|
signP2OnlyBlock = uint8(0x00)
|
||||||
exportP1Any = uint8(0x00)
|
exportP1Any = uint8(0x00)
|
||||||
exportP2Pubkey = uint8(0x01)
|
exportP2Pubkey = uint8(0x01)
|
||||||
)
|
)
|
||||||
|
|
@ -132,6 +132,7 @@ func NewWallet(hub *Hub, card *pcsc.Card) *Wallet {
|
||||||
Hub: hub,
|
Hub: hub,
|
||||||
card: card,
|
card: card,
|
||||||
}
|
}
|
||||||
|
|
||||||
return wallet
|
return wallet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,7 +168,7 @@ func transmit(card *pcsc.Card, command *commandAPDU) (*responseAPDU, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if response.Sw1 != sw1Ok {
|
if response.Sw1 != sw1Ok {
|
||||||
return nil, fmt.Errorf("unexpected insecure response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", command.Cla, command.Ins, response.Sw1, response.Sw2)
|
return nil, fmt.Errorf("unexpected insecure response status Cla=%#x, Ins=%#x, Sw=%#x%x", command.Cla, command.Ins, response.Sw1, response.Sw2)
|
||||||
}
|
}
|
||||||
|
|
||||||
return response, nil
|
return response, nil
|
||||||
|
|
@ -202,6 +203,7 @@ func (w *Wallet) connect() error {
|
||||||
Wallet: w,
|
Wallet: w,
|
||||||
Channel: channel,
|
Channel: channel,
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -222,6 +224,7 @@ func (w *Wallet) doselect() (*applicationInfo, error) {
|
||||||
if _, err := asn1.UnmarshalWithParams(response.Data, appinfo, "tag:4"); err != nil {
|
if _, err := asn1.UnmarshalWithParams(response.Data, appinfo, "tag:4"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return appinfo, nil
|
return appinfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,9 +237,11 @@ func (w *Wallet) ping() error {
|
||||||
if !w.session.paired() {
|
if !w.session.paired() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := w.session.walletStatus(); err != nil {
|
if _, err := w.session.walletStatus(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -245,6 +250,7 @@ func (w *Wallet) release() error {
|
||||||
if w.session != nil {
|
if w.session != nil {
|
||||||
return w.session.release()
|
return w.session.release()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -254,13 +260,16 @@ func (w *Wallet) pair(puk []byte) error {
|
||||||
if w.session.paired() {
|
if w.session.paired() {
|
||||||
return fmt.Errorf("wallet already paired")
|
return fmt.Errorf("wallet already paired")
|
||||||
}
|
}
|
||||||
|
|
||||||
pairing, err := w.session.pair(puk)
|
pairing, err := w.session.pair(puk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = w.Hub.setPairing(w, &pairing); err != nil {
|
if err = w.Hub.setPairing(w, &pairing); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return w.session.authenticate(pairing)
|
return w.session.authenticate(pairing)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,15 +281,19 @@ func (w *Wallet) Unpair(pin []byte) error {
|
||||||
if !w.session.paired() {
|
if !w.session.paired() {
|
||||||
return fmt.Errorf("wallet %x not paired", w.PublicKey)
|
return fmt.Errorf("wallet %x not paired", w.PublicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := w.session.verifyPin(pin); err != nil {
|
if err := w.session.verifyPin(pin); err != nil {
|
||||||
return fmt.Errorf("failed to verify pin: %s", err)
|
return fmt.Errorf("failed to verify pin: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := w.session.unpair(); err != nil {
|
if err := w.session.unpair(); err != nil {
|
||||||
return fmt.Errorf("failed to unpair: %s", err)
|
return fmt.Errorf("failed to unpair: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := w.Hub.setPairing(w, nil); err != nil {
|
if err := w.Hub.setPairing(w, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -310,6 +323,7 @@ func (w *Wallet) Status() (string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Sprintf("Failed: %v", err), err
|
return fmt.Sprintf("Failed: %v", err), err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case !w.session.verified && status.PinRetryCount == 0 && status.PukRetryCount == 0:
|
case !w.session.verified && status.PinRetryCount == 0 && status.PukRetryCount == 0:
|
||||||
return "Bricked, waiting for full wipe", nil
|
return "Bricked, waiting for full wipe", nil
|
||||||
|
|
@ -384,6 +398,7 @@ func (w *Wallet) Open(passphrase string) error {
|
||||||
w.log.Error("PIN needs to be at least 6 digits")
|
w.log.Error("PIN needs to be at least 6 digits")
|
||||||
return ErrPINNeeded
|
return ErrPINNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := w.session.verifyPin([]byte(passphrase)); err != nil {
|
if err := w.session.verifyPin([]byte(passphrase)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -392,6 +407,7 @@ func (w *Wallet) Open(passphrase string) error {
|
||||||
w.log.Error("PUK needs to be at least 12 digits")
|
w.log.Error("PUK needs to be at least 12 digits")
|
||||||
return ErrPINUnblockNeeded
|
return ErrPINUnblockNeeded
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := w.session.unblockPin([]byte(passphrase)); err != nil {
|
if err := w.session.unblockPin([]byte(passphrase)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -417,6 +433,7 @@ func (w *Wallet) Close() error {
|
||||||
|
|
||||||
// Terminate the self-derivations
|
// Terminate the self-derivations
|
||||||
var derr error
|
var derr error
|
||||||
|
|
||||||
if dQuit != nil {
|
if dQuit != nil {
|
||||||
errc := make(chan error)
|
errc := make(chan error)
|
||||||
dQuit <- errc
|
dQuit <- errc
|
||||||
|
|
@ -432,6 +449,7 @@ func (w *Wallet) Close() error {
|
||||||
if err := w.release(); err != nil {
|
if err := w.release(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return derr
|
return derr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -447,6 +465,7 @@ func (w *Wallet) selfDerive() {
|
||||||
errc chan error
|
errc chan error
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
for errc == nil && err == nil {
|
for errc == nil && err == nil {
|
||||||
// Wait until either derivation or termination is requested
|
// Wait until either derivation or termination is requested
|
||||||
select {
|
select {
|
||||||
|
|
@ -461,8 +480,10 @@ func (w *Wallet) selfDerive() {
|
||||||
if w.session == nil || w.deriveChain == nil {
|
if w.session == nil || w.deriveChain == nil {
|
||||||
w.lock.Unlock()
|
w.lock.Unlock()
|
||||||
reqc <- struct{}{}
|
reqc <- struct{}{}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
pairing := w.Hub.pairing(w)
|
pairing := w.Hub.pairing(w)
|
||||||
|
|
||||||
// Device lock obtained, derive the next batch of accounts
|
// Device lock obtained, derive the next batch of accounts
|
||||||
|
|
@ -475,6 +496,7 @@ func (w *Wallet) selfDerive() {
|
||||||
|
|
||||||
context = context.Background()
|
context = context.Background()
|
||||||
)
|
)
|
||||||
|
|
||||||
for i := 0; i < len(nextAddrs); i++ {
|
for i := 0; i < len(nextAddrs); i++ {
|
||||||
for empty := false; !empty; {
|
for empty := false; !empty; {
|
||||||
// Retrieve the next derived Ethereum account
|
// Retrieve the next derived Ethereum account
|
||||||
|
|
@ -483,6 +505,7 @@ func (w *Wallet) selfDerive() {
|
||||||
w.log.Warn("Smartcard wallet account derivation failed", "err", err)
|
w.log.Warn("Smartcard wallet account derivation failed", "err", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
nextAddrs[i] = nextAcc.Address
|
nextAddrs[i] = nextAcc.Address
|
||||||
}
|
}
|
||||||
// Check the account's status against the current chain state
|
// Check the account's status against the current chain state
|
||||||
|
|
@ -490,11 +513,13 @@ func (w *Wallet) selfDerive() {
|
||||||
balance *big.Int
|
balance *big.Int
|
||||||
nonce uint64
|
nonce uint64
|
||||||
)
|
)
|
||||||
|
|
||||||
balance, err = w.deriveChain.BalanceAt(context, nextAddrs[i], nil)
|
balance, err = w.deriveChain.BalanceAt(context, nextAddrs[i], nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.log.Warn("Smartcard wallet balance retrieval failed", "err", err)
|
w.log.Warn("Smartcard wallet balance retrieval failed", "err", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
nonce, err = w.deriveChain.NonceAt(context, nextAddrs[i], nil)
|
nonce, err = w.deriveChain.NonceAt(context, nextAddrs[i], nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.log.Warn("Smartcard wallet nonce retrieval failed", "err", err)
|
w.log.Warn("Smartcard wallet nonce retrieval failed", "err", err)
|
||||||
|
|
@ -503,6 +528,7 @@ func (w *Wallet) selfDerive() {
|
||||||
// If the next account is empty, stop self-derivation, but add for the last base path
|
// If the next account is empty, stop self-derivation, but add for the last base path
|
||||||
if balance.Sign() == 0 && nonce == 0 {
|
if balance.Sign() == 0 && nonce == 0 {
|
||||||
empty = true
|
empty = true
|
||||||
|
|
||||||
if i < len(nextAddrs)-1 {
|
if i < len(nextAddrs)-1 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -516,6 +542,7 @@ func (w *Wallet) selfDerive() {
|
||||||
if _, known := pairing.Accounts[nextAddrs[i]]; !known || !empty || nextAddrs[i] != w.deriveNextAddrs[i] {
|
if _, known := pairing.Accounts[nextAddrs[i]]; !known || !empty || nextAddrs[i] != w.deriveNextAddrs[i] {
|
||||||
w.log.Info("Smartcard wallet discovered new account", "address", nextAddrs[i], "path", path, "balance", balance, "nonce", nonce)
|
w.log.Info("Smartcard wallet discovered new account", "address", nextAddrs[i], "path", path, "balance", balance, "nonce", nonce)
|
||||||
}
|
}
|
||||||
|
|
||||||
pairing.Accounts[nextAddrs[i]] = path
|
pairing.Accounts[nextAddrs[i]] = path
|
||||||
|
|
||||||
// Fetch the next potential account
|
// Fetch the next potential account
|
||||||
|
|
@ -538,6 +565,7 @@ func (w *Wallet) selfDerive() {
|
||||||
|
|
||||||
// Notify the user of termination and loop after a bit of time (to avoid trashing)
|
// Notify the user of termination and loop after a bit of time (to avoid trashing)
|
||||||
reqc <- struct{}{}
|
reqc <- struct{}{}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
select {
|
select {
|
||||||
case errc = <-w.deriveQuit:
|
case errc = <-w.deriveQuit:
|
||||||
|
|
@ -577,9 +605,12 @@ func (w *Wallet) Accounts() []accounts.Account {
|
||||||
for address, path := range pairing.Accounts {
|
for address, path := range pairing.Accounts {
|
||||||
ret = append(ret, w.makeAccount(address, path))
|
ret = append(ret, w.makeAccount(address, path))
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Sort(accounts.AccountsByURL(ret))
|
sort.Sort(accounts.AccountsByURL(ret))
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -599,6 +630,7 @@ func (w *Wallet) Contains(account accounts.Account) bool {
|
||||||
_, ok := pairing.Accounts[account.Address]
|
_, ok := pairing.Accounts[account.Address]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -625,6 +657,7 @@ func (w *Wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
|
||||||
if pin {
|
if pin {
|
||||||
pairing := w.Hub.pairing(w)
|
pairing := w.Hub.pairing(w)
|
||||||
pairing.Accounts[account.Address] = path
|
pairing.Accounts[account.Address] = path
|
||||||
|
|
||||||
if err := w.Hub.setPairing(w, pairing); err != nil {
|
if err := w.Hub.setPairing(w, pairing); err != nil {
|
||||||
return accounts.Account{}, err
|
return accounts.Account{}, err
|
||||||
}
|
}
|
||||||
|
|
@ -656,6 +689,7 @@ func (w *Wallet) SelfDerive(bases []accounts.DerivationPath, chain ethereum.Chai
|
||||||
w.deriveNextPaths[i] = make(accounts.DerivationPath, len(base))
|
w.deriveNextPaths[i] = make(accounts.DerivationPath, len(base))
|
||||||
copy(w.deriveNextPaths[i][:], base[:])
|
copy(w.deriveNextPaths[i][:], base[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
w.deriveNextAddrs = make([]common.Address, len(bases))
|
w.deriveNextAddrs = make([]common.Address, len(bases))
|
||||||
w.deriveChain = chain
|
w.deriveChain = chain
|
||||||
}
|
}
|
||||||
|
|
@ -701,10 +735,12 @@ func (w *Wallet) signHash(account accounts.Account, hash []byte) ([]byte, error)
|
||||||
func (w *Wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
func (w *Wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||||
signer := types.LatestSignerForChainID(chainID)
|
signer := types.LatestSignerForChainID(chainID)
|
||||||
hash := signer.Hash(tx)
|
hash := signer.Hash(tx)
|
||||||
|
|
||||||
sig, err := w.signHash(account, hash[:])
|
sig, err := w.signHash(account, hash[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return tx.WithSignature(signer, sig)
|
return tx.WithSignature(signer, sig)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -759,6 +795,7 @@ func (w *Wallet) SignTxWithPassphrase(account accounts.Account, passphrase strin
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return w.SignTx(account, tx, chainID)
|
return w.SignTx(account, tx, chainID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -815,6 +852,7 @@ func (s *Session) unpair() error {
|
||||||
if !s.verified {
|
if !s.verified {
|
||||||
return fmt.Errorf("unpair requires that the PIN be verified")
|
return fmt.Errorf("unpair requires that the PIN be verified")
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.Channel.Unpair()
|
return s.Channel.Unpair()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -823,7 +861,9 @@ func (s *Session) verifyPin(pin []byte) error {
|
||||||
if _, err := s.Channel.transmitEncrypted(claSCWallet, insVerifyPin, 0, 0, pin); err != nil {
|
if _, err := s.Channel.transmitEncrypted(claSCWallet, insVerifyPin, 0, 0, pin); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.verified = true
|
s.verified = true
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -833,7 +873,9 @@ func (s *Session) unblockPin(pukpin []byte) error {
|
||||||
if _, err := s.Channel.transmitEncrypted(claSCWallet, insUnblockPin, 0, 0, pukpin); err != nil {
|
if _, err := s.Channel.transmitEncrypted(claSCWallet, insUnblockPin, 0, 0, pukpin); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.verified = true
|
s.verified = true
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -852,8 +894,10 @@ func (s *Session) authenticate(pairing smartcardPairing) error {
|
||||||
if !bytes.Equal(s.Wallet.PublicKey, pairing.PublicKey) {
|
if !bytes.Equal(s.Wallet.PublicKey, pairing.PublicKey) {
|
||||||
return fmt.Errorf("cannot pair using another wallet's pairing; %x != %x", s.Wallet.PublicKey, pairing.PublicKey)
|
return fmt.Errorf("cannot pair using another wallet's pairing; %x != %x", s.Wallet.PublicKey, pairing.PublicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Channel.PairingKey = pairing.PairingKey
|
s.Channel.PairingKey = pairing.PairingKey
|
||||||
s.Channel.PairingIndex = pairing.PairingIndex
|
s.Channel.PairingIndex = pairing.PairingIndex
|
||||||
|
|
||||||
return s.Channel.Open()
|
return s.Channel.Open()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -875,18 +919,22 @@ func (s *Session) walletStatus() (*walletStatus, error) {
|
||||||
if _, err := asn1.UnmarshalWithParams(response.Data, status, "tag:3"); err != nil {
|
if _, err := asn1.UnmarshalWithParams(response.Data, status, "tag:3"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return status, nil
|
return status, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// derivationPath fetches the wallet's current derivation path from the card.
|
// derivationPath fetches the wallet's current derivation path from the card.
|
||||||
|
//
|
||||||
//lint:ignore U1000 needs to be added to the console interface
|
//lint:ignore U1000 needs to be added to the console interface
|
||||||
func (s *Session) derivationPath() (accounts.DerivationPath, error) {
|
func (s *Session) derivationPath() (accounts.DerivationPath, error) {
|
||||||
response, err := s.Channel.transmitEncrypted(claSCWallet, insStatus, statusP1Path, 0, nil)
|
response, err := s.Channel.transmitEncrypted(claSCWallet, insStatus, statusP1Path, 0, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := bytes.NewReader(response.Data)
|
buf := bytes.NewReader(response.Data)
|
||||||
path := make(accounts.DerivationPath, len(response.Data)/4)
|
path := make(accounts.DerivationPath, len(response.Data)/4)
|
||||||
|
|
||||||
return path, binary.Read(buf, binary.BigEndian, &path)
|
return path, binary.Read(buf, binary.BigEndian, &path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -905,6 +953,7 @@ func (s *Session) initialize(seed []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if status == "Online" {
|
if status == "Online" {
|
||||||
return fmt.Errorf("card is already initialized, cowardly refusing to proceed")
|
return fmt.Errorf("card is already initialized, cowardly refusing to proceed")
|
||||||
}
|
}
|
||||||
|
|
@ -926,6 +975,7 @@ func (s *Session) initialize(seed []byte) error {
|
||||||
id.PublicKey = crypto.FromECDSAPub(&key.PublicKey)
|
id.PublicKey = crypto.FromECDSAPub(&key.PublicKey)
|
||||||
id.PrivateKey = seed[:32]
|
id.PrivateKey = seed[:32]
|
||||||
id.ChainCode = seed[32:]
|
id.ChainCode = seed[32:]
|
||||||
|
|
||||||
data, err := asn1.Marshal(id)
|
data, err := asn1.Marshal(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -935,6 +985,7 @@ func (s *Session) initialize(seed []byte) error {
|
||||||
data[0] = 0xA1
|
data[0] = 0xA1
|
||||||
|
|
||||||
_, err = s.Channel.transmitEncrypted(claSCWallet, insLoadKey, 0x02, 0, data)
|
_, err = s.Channel.transmitEncrypted(claSCWallet, insLoadKey, 0x02, 0, data)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -946,6 +997,7 @@ func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
var p1 uint8
|
var p1 uint8
|
||||||
|
|
||||||
switch startingPoint {
|
switch startingPoint {
|
||||||
case derivationpath.StartingPointMaster:
|
case derivationpath.StartingPointMaster:
|
||||||
p1 = P1DeriveKeyFromMaster
|
p1 = P1DeriveKeyFromMaster
|
||||||
|
|
@ -978,6 +1030,7 @@ func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error)
|
||||||
if _, err := asn1.UnmarshalWithParams(response.Data, sigdata, "tag:0"); err != nil {
|
if _, err := asn1.UnmarshalWithParams(response.Data, sigdata, "tag:0"); err != nil {
|
||||||
return accounts.Account{}, err
|
return accounts.Account{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
rbytes, sbytes := sigdata.Signature.R.Bytes(), sigdata.Signature.S.Bytes()
|
rbytes, sbytes := sigdata.Signature.R.Bytes(), sigdata.Signature.S.Bytes()
|
||||||
sig := make([]byte, 65)
|
sig := make([]byte, 65)
|
||||||
copy(sig[32-len(rbytes):32], rbytes)
|
copy(sig[32-len(rbytes):32], rbytes)
|
||||||
|
|
@ -986,14 +1039,17 @@ func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error)
|
||||||
if err := confirmPublicKey(sig, sigdata.PublicKey); err != nil {
|
if err := confirmPublicKey(sig, sigdata.PublicKey); err != nil {
|
||||||
return accounts.Account{}, err
|
return accounts.Account{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pub, err := crypto.UnmarshalPubkey(sigdata.PublicKey)
|
pub, err := crypto.UnmarshalPubkey(sigdata.PublicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return accounts.Account{}, err
|
return accounts.Account{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.Wallet.makeAccount(crypto.PubkeyToAddress(*pub), path), nil
|
return s.Wallet.makeAccount(crypto.PubkeyToAddress(*pub), path), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// keyExport contains information on an exported keypair.
|
// keyExport contains information on an exported keypair.
|
||||||
|
//
|
||||||
//lint:ignore U1000 needs to be added to the console interface
|
//lint:ignore U1000 needs to be added to the console interface
|
||||||
type keyExport struct {
|
type keyExport struct {
|
||||||
PublicKey []byte `asn1:"tag:0"`
|
PublicKey []byte `asn1:"tag:0"`
|
||||||
|
|
@ -1001,16 +1057,19 @@ type keyExport struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// publicKey returns the public key for the current derivation path.
|
// publicKey returns the public key for the current derivation path.
|
||||||
|
//
|
||||||
//lint:ignore U1000 needs to be added to the console interface
|
//lint:ignore U1000 needs to be added to the console interface
|
||||||
func (s *Session) publicKey() ([]byte, error) {
|
func (s *Session) publicKey() ([]byte, error) {
|
||||||
response, err := s.Channel.transmitEncrypted(claSCWallet, insExportKey, exportP1Any, exportP2Pubkey, nil)
|
response, err := s.Channel.transmitEncrypted(claSCWallet, insExportKey, exportP1Any, exportP2Pubkey, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
keys := new(keyExport)
|
keys := new(keyExport)
|
||||||
if _, err := asn1.UnmarshalWithParams(response.Data, keys, "tag:1"); err != nil {
|
if _, err := asn1.UnmarshalWithParams(response.Data, keys, "tag:1"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return keys.PublicKey, nil
|
return keys.PublicKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1028,16 +1087,19 @@ type signatureData struct {
|
||||||
// recovering the v value.
|
// recovering the v value.
|
||||||
func (s *Session) sign(path accounts.DerivationPath, hash []byte) ([]byte, error) {
|
func (s *Session) sign(path accounts.DerivationPath, hash []byte) ([]byte, error) {
|
||||||
startTime := time.Now()
|
startTime := time.Now()
|
||||||
|
|
||||||
_, err := s.derive(path)
|
_, err := s.derive(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
deriveTime := time.Now()
|
deriveTime := time.Now()
|
||||||
|
|
||||||
response, err := s.Channel.transmitEncrypted(claSCWallet, insSign, signP1PrecomputedHash, signP2OnlyBlock, hash)
|
response, err := s.Channel.transmitEncrypted(claSCWallet, insSign, signP1PrecomputedHash, signP2OnlyBlock, hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sigdata := new(signatureData)
|
sigdata := new(signatureData)
|
||||||
if _, err := asn1.UnmarshalWithParams(response.Data, sigdata, "tag:0"); err != nil {
|
if _, err := asn1.UnmarshalWithParams(response.Data, sigdata, "tag:0"); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -1053,6 +1115,7 @@ func (s *Session) sign(path accounts.DerivationPath, hash []byte) ([]byte, error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("Signed using smartcard", "deriveTime", deriveTime.Sub(startTime), "signingTime", time.Since(deriveTime))
|
log.Debug("Signed using smartcard", "deriveTime", deriveTime.Sub(startTime), "signingTime", time.Since(deriveTime))
|
||||||
|
|
||||||
return sig, nil
|
return sig, nil
|
||||||
|
|
@ -1068,6 +1131,7 @@ func confirmPublicKey(sig, pubkey []byte) error {
|
||||||
// recover the v value and produce a recoverable signature.
|
// recover the v value and produce a recoverable signature.
|
||||||
func makeRecoverableSignature(hash, sig, expectedPubkey []byte) ([]byte, error) {
|
func makeRecoverableSignature(hash, sig, expectedPubkey []byte) ([]byte, error) {
|
||||||
var libraryError error
|
var libraryError error
|
||||||
|
|
||||||
for v := 0; v < 2; v++ {
|
for v := 0; v < 2; v++ {
|
||||||
sig[64] = byte(v)
|
sig[64] = byte(v)
|
||||||
if pubkey, err := crypto.Ecrecover(hash, sig); err == nil {
|
if pubkey, err := crypto.Ecrecover(hash, sig); err == nil {
|
||||||
|
|
@ -1078,8 +1142,10 @@ func makeRecoverableSignature(hash, sig, expectedPubkey []byte) ([]byte, error)
|
||||||
libraryError = err
|
libraryError = err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if libraryError != nil {
|
if libraryError != nil {
|
||||||
return nil, libraryError
|
return nil, libraryError
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, ErrPubkeyMismatch
|
return nil, ErrPubkeyMismatch
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ func parseURL(url string) (URL, error) {
|
||||||
if len(parts) != 2 || parts[0] == "" {
|
if len(parts) != 2 || parts[0] == "" {
|
||||||
return URL{}, errors.New("protocol scheme missing")
|
return URL{}, errors.New("protocol scheme missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
return URL{
|
return URL{
|
||||||
Scheme: parts[0],
|
Scheme: parts[0],
|
||||||
Path: parts[1],
|
Path: parts[1],
|
||||||
|
|
@ -57,6 +58,7 @@ func (u URL) String() string {
|
||||||
if u.Scheme != "" {
|
if u.Scheme != "" {
|
||||||
return fmt.Sprintf("%s://%s", u.Scheme, u.Path)
|
return fmt.Sprintf("%s://%s", u.Scheme, u.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return u.Path
|
return u.Path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,6 +68,7 @@ func (u URL) TerminalString() string {
|
||||||
if len(url) > 32 {
|
if len(url) > 32 {
|
||||||
return url[:31] + ".."
|
return url[:31] + ".."
|
||||||
}
|
}
|
||||||
|
|
||||||
return url
|
return url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -77,16 +80,20 @@ func (u URL) MarshalJSON() ([]byte, error) {
|
||||||
// UnmarshalJSON parses url.
|
// UnmarshalJSON parses url.
|
||||||
func (u *URL) UnmarshalJSON(input []byte) error {
|
func (u *URL) UnmarshalJSON(input []byte) error {
|
||||||
var textURL string
|
var textURL string
|
||||||
|
|
||||||
err := json.Unmarshal(input, &textURL)
|
err := json.Unmarshal(input, &textURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
url, err := parseURL(textURL)
|
url, err := parseURL(textURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
u.Scheme = url.Scheme
|
u.Scheme = url.Scheme
|
||||||
u.Path = url.Path
|
u.Path = url.Path
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -95,10 +102,10 @@ func (u *URL) UnmarshalJSON(input []byte) error {
|
||||||
// -1 if x < y
|
// -1 if x < y
|
||||||
// 0 if x == y
|
// 0 if x == y
|
||||||
// +1 if x > y
|
// +1 if x > y
|
||||||
//
|
|
||||||
func (u URL) Cmp(url URL) int {
|
func (u URL) Cmp(url URL) int {
|
||||||
if u.Scheme == url.Scheme {
|
if u.Scheme == url.Scheme {
|
||||||
return strings.Compare(u.Path, url.Path)
|
return strings.Compare(u.Path, url.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.Compare(u.Scheme, url.Scheme)
|
return strings.Compare(u.Scheme, url.Scheme)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,16 +25,19 @@ func TestURLParsing(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if url.Scheme != "https" {
|
if url.Scheme != "https" {
|
||||||
t.Errorf("expected: %v, got: %v", "https", url.Scheme)
|
t.Errorf("expected: %v, got: %v", "https", url.Scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
if url.Path != "ethereum.org" {
|
if url.Path != "ethereum.org" {
|
||||||
t.Errorf("expected: %v, got: %v", "ethereum.org", url.Path)
|
t.Errorf("expected: %v, got: %v", "ethereum.org", url.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = parseURL("ethereum.org")
|
for _, u := range []string{"ethereum.org", ""} {
|
||||||
if err == nil {
|
if _, err = parseURL(u); err == nil {
|
||||||
t.Error("expected err, got: nil")
|
t.Errorf("input %v, expected err, got: nil", u)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,10 +55,12 @@ func TestURLString(t *testing.T) {
|
||||||
|
|
||||||
func TestURLMarshalJSON(t *testing.T) {
|
func TestURLMarshalJSON(t *testing.T) {
|
||||||
url := URL{Scheme: "https", Path: "ethereum.org"}
|
url := URL{Scheme: "https", Path: "ethereum.org"}
|
||||||
|
|
||||||
json, err := url.MarshalJSON()
|
json, err := url.MarshalJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if string(json) != "\"https://ethereum.org\"" {
|
if string(json) != "\"https://ethereum.org\"" {
|
||||||
t.Errorf("expected: %v, got: %v", "\"https://ethereum.org\"", string(json))
|
t.Errorf("expected: %v, got: %v", "\"https://ethereum.org\"", string(json))
|
||||||
}
|
}
|
||||||
|
|
@ -63,13 +68,16 @@ func TestURLMarshalJSON(t *testing.T) {
|
||||||
|
|
||||||
func TestURLUnmarshalJSON(t *testing.T) {
|
func TestURLUnmarshalJSON(t *testing.T) {
|
||||||
url := &URL{}
|
url := &URL{}
|
||||||
|
|
||||||
err := url.UnmarshalJSON([]byte("\"https://ethereum.org\""))
|
err := url.UnmarshalJSON([]byte("\"https://ethereum.org\""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if url.Scheme != "https" {
|
if url.Scheme != "https" {
|
||||||
t.Errorf("expected: %v, got: %v", "https", url.Scheme)
|
t.Errorf("expected: %v, got: %v", "https", url.Scheme)
|
||||||
}
|
}
|
||||||
|
|
||||||
if url.Path != "ethereum.org" {
|
if url.Path != "ethereum.org" {
|
||||||
t.Errorf("expected: %v, got: %v", "https", url.Path)
|
t.Errorf("expected: %v, got: %v", "https", url.Path)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,18 +71,28 @@ type Hub struct {
|
||||||
// NewLedgerHub creates a new hardware wallet manager for Ledger devices.
|
// NewLedgerHub creates a new hardware wallet manager for Ledger devices.
|
||||||
func NewLedgerHub() (*Hub, error) {
|
func NewLedgerHub() (*Hub, error) {
|
||||||
return newHub(LedgerScheme, 0x2c97, []uint16{
|
return newHub(LedgerScheme, 0x2c97, []uint16{
|
||||||
|
|
||||||
|
// Device definitions taken from
|
||||||
|
// https://github.com/LedgerHQ/ledger-live/blob/38012bc8899e0f07149ea9cfe7e64b2c146bc92b/libs/ledgerjs/packages/devices/src/index.ts
|
||||||
|
|
||||||
// Original product IDs
|
// Original product IDs
|
||||||
0x0000, /* Ledger Blue */
|
0x0000, /* Ledger Blue */
|
||||||
0x0001, /* Ledger Nano S */
|
0x0001, /* Ledger Nano S */
|
||||||
0x0004, /* Ledger Nano X */
|
0x0004, /* Ledger Nano X */
|
||||||
|
0x0005, /* Ledger Nano S Plus */
|
||||||
|
0x0006, /* Ledger Nano FTS */
|
||||||
|
|
||||||
// Upcoming product IDs: https://www.ledger.com/2019/05/17/windows-10-update-sunsetting-u2f-tunnel-transport-for-ledger-devices/
|
|
||||||
0x0015, /* HID + U2F + WebUSB Ledger Blue */
|
0x0015, /* HID + U2F + WebUSB Ledger Blue */
|
||||||
0x1015, /* HID + U2F + WebUSB Ledger Nano S */
|
0x1015, /* HID + U2F + WebUSB Ledger Nano S */
|
||||||
0x4015, /* HID + U2F + WebUSB Ledger Nano X */
|
0x4015, /* HID + U2F + WebUSB Ledger Nano X */
|
||||||
|
0x5015, /* HID + U2F + WebUSB Ledger Nano S Plus */
|
||||||
|
0x6015, /* HID + U2F + WebUSB Ledger Nano FTS */
|
||||||
|
|
||||||
0x0011, /* HID + WebUSB Ledger Blue */
|
0x0011, /* HID + WebUSB Ledger Blue */
|
||||||
0x1011, /* HID + WebUSB Ledger Nano S */
|
0x1011, /* HID + WebUSB Ledger Nano S */
|
||||||
0x4011, /* HID + WebUSB Ledger Nano X */
|
0x4011, /* HID + WebUSB Ledger Nano X */
|
||||||
|
0x5011, /* HID + WebUSB Ledger Nano S Plus */
|
||||||
|
0x6011, /* HID + WebUSB Ledger Nano FTS */
|
||||||
}, 0xffa0, 0, newLedgerDriver)
|
}, 0xffa0, 0, newLedgerDriver)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -102,6 +112,7 @@ func newHub(scheme string, vendorID uint16, productIDs []uint16, usageID uint16,
|
||||||
if !usb.Supported() {
|
if !usb.Supported() {
|
||||||
return nil, errors.New("unsupported platform")
|
return nil, errors.New("unsupported platform")
|
||||||
}
|
}
|
||||||
|
|
||||||
hub := &Hub{
|
hub := &Hub{
|
||||||
scheme: scheme,
|
scheme: scheme,
|
||||||
vendorID: vendorID,
|
vendorID: vendorID,
|
||||||
|
|
@ -112,6 +123,7 @@ func newHub(scheme string, vendorID uint16, productIDs []uint16, usageID uint16,
|
||||||
quit: make(chan chan error),
|
quit: make(chan chan error),
|
||||||
}
|
}
|
||||||
hub.refreshWallets()
|
hub.refreshWallets()
|
||||||
|
|
||||||
return hub, nil
|
return hub, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,6 +138,7 @@ func (hub *Hub) Wallets() []accounts.Wallet {
|
||||||
|
|
||||||
cpy := make([]accounts.Wallet, len(hub.wallets))
|
cpy := make([]accounts.Wallet, len(hub.wallets))
|
||||||
copy(cpy, hub.wallets)
|
copy(cpy, hub.wallets)
|
||||||
|
|
||||||
return cpy
|
return cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,17 +173,22 @@ func (hub *Hub) refreshWallets() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
infos, err := usb.Enumerate(hub.vendorID, 0)
|
infos, err := usb.Enumerate(hub.vendorID, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
failcount := atomic.AddUint32(&hub.enumFails, 1)
|
failcount := atomic.AddUint32(&hub.enumFails, 1)
|
||||||
|
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
// See rationale before the enumeration why this is needed and only on Linux.
|
// See rationale before the enumeration why this is needed and only on Linux.
|
||||||
hub.commsLock.Unlock()
|
hub.commsLock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Error("Failed to enumerate USB devices", "hub", hub.scheme,
|
log.Error("Failed to enumerate USB devices", "hub", hub.scheme,
|
||||||
"vendor", hub.vendorID, "failcount", failcount, "err", err)
|
"vendor", hub.vendorID, "failcount", failcount, "err", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic.StoreUint32(&hub.enumFails, 0)
|
atomic.StoreUint32(&hub.enumFails, 0)
|
||||||
|
|
||||||
for _, info := range infos {
|
for _, info := range infos {
|
||||||
|
|
@ -182,6 +200,7 @@ func (hub *Hub) refreshWallets() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if runtime.GOOS == "linux" {
|
if runtime.GOOS == "linux" {
|
||||||
// See rationale before the enumeration why this is needed and only on Linux.
|
// See rationale before the enumeration why this is needed and only on Linux.
|
||||||
hub.commsLock.Unlock()
|
hub.commsLock.Unlock()
|
||||||
|
|
@ -215,12 +234,14 @@ func (hub *Hub) refreshWallets() {
|
||||||
|
|
||||||
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
|
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
|
||||||
wallets = append(wallets, wallet)
|
wallets = append(wallets, wallet)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// If the device is the same as the first wallet, keep it
|
// If the device is the same as the first wallet, keep it
|
||||||
if hub.wallets[0].URL().Cmp(url) == 0 {
|
if hub.wallets[0].URL().Cmp(url) == 0 {
|
||||||
wallets = append(wallets, hub.wallets[0])
|
wallets = append(wallets, hub.wallets[0])
|
||||||
hub.wallets = hub.wallets[1:]
|
hub.wallets = hub.wallets[1:]
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -228,6 +249,7 @@ func (hub *Hub) refreshWallets() {
|
||||||
for _, wallet := range hub.wallets {
|
for _, wallet := range hub.wallets {
|
||||||
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
|
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
|
||||||
}
|
}
|
||||||
|
|
||||||
hub.refreshed = time.Now()
|
hub.refreshed = time.Now()
|
||||||
hub.wallets = wallets
|
hub.wallets = wallets
|
||||||
hub.stateLock.Unlock()
|
hub.stateLock.Unlock()
|
||||||
|
|
@ -253,6 +275,7 @@ func (hub *Hub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
|
||||||
hub.updating = true
|
hub.updating = true
|
||||||
go hub.updater()
|
go hub.updater()
|
||||||
}
|
}
|
||||||
|
|
||||||
return sub
|
return sub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,6 +295,7 @@ func (hub *Hub) updater() {
|
||||||
if hub.updateScope.Count() == 0 {
|
if hub.updateScope.Count() == 0 {
|
||||||
hub.updating = false
|
hub.updating = false
|
||||||
hub.stateLock.Unlock()
|
hub.stateLock.Unlock()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
hub.stateLock.Unlock()
|
hub.stateLock.Unlock()
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,8 @@ const (
|
||||||
ledgerP1InitTransactionData ledgerParam1 = 0x00 // First transaction data block for signing
|
ledgerP1InitTransactionData ledgerParam1 = 0x00 // First transaction data block for signing
|
||||||
ledgerP1ContTransactionData ledgerParam1 = 0x80 // Subsequent transaction data block for signing
|
ledgerP1ContTransactionData ledgerParam1 = 0x80 // Subsequent transaction data block for signing
|
||||||
ledgerP2DiscardAddressChainCode ledgerParam2 = 0x00 // Do not return the chain code along with the address
|
ledgerP2DiscardAddressChainCode ledgerParam2 = 0x00 // Do not return the chain code along with the address
|
||||||
|
|
||||||
|
ledgerEip155Size int = 3 // Size of the EIP-155 chain_id,r,s in unsigned transactions
|
||||||
)
|
)
|
||||||
|
|
||||||
// errLedgerReplyInvalidHeader is the error message returned by a Ledger data exchange
|
// errLedgerReplyInvalidHeader is the error message returned by a Ledger data exchange
|
||||||
|
|
@ -92,12 +94,15 @@ func (w *ledgerDriver) Status() (string, error) {
|
||||||
if w.failure != nil {
|
if w.failure != nil {
|
||||||
return fmt.Sprintf("Failed: %v", w.failure), w.failure
|
return fmt.Sprintf("Failed: %v", w.failure), w.failure
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.browser {
|
if w.browser {
|
||||||
return "Ethereum app in browser mode", w.failure
|
return "Ethereum app in browser mode", w.failure
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.offline() {
|
if w.offline() {
|
||||||
return "Ethereum app offline", w.failure
|
return "Ethereum app offline", w.failure
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("Ethereum app v%d.%d.%d online", w.version[0], w.version[1], w.version[2]), w.failure
|
return fmt.Sprintf("Ethereum app v%d.%d.%d online", w.version[0], w.version[1], w.version[2]), w.failure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,12 +125,14 @@ func (w *ledgerDriver) Open(device io.ReadWriter, passphrase string) error {
|
||||||
if err == errLedgerReplyInvalidHeader {
|
if err == errLedgerReplyInvalidHeader {
|
||||||
w.browser = true
|
w.browser = true
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// Try to resolve the Ethereum app's version, will fail prior to v1.0.2
|
// Try to resolve the Ethereum app's version, will fail prior to v1.0.2
|
||||||
if w.version, err = w.ledgerVersion(); err != nil {
|
if w.version, err = w.ledgerVersion(); err != nil {
|
||||||
w.version = [3]byte{1, 0, 0} // Assume worst case, can't verify if v1.0.0 or v1.0.1
|
w.version = [3]byte{1, 0, 0} // Assume worst case, can't verify if v1.0.0 or v1.0.1
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,6 +150,7 @@ func (w *ledgerDriver) Heartbeat() error {
|
||||||
w.failure = err
|
w.failure = err
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -213,12 +221,15 @@ func (w *ledgerDriver) ledgerVersion() ([3]byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return [3]byte{}, err
|
return [3]byte{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(reply) != 4 {
|
if len(reply) != 4 {
|
||||||
return [3]byte{}, errLedgerInvalidVersionReply
|
return [3]byte{}, errLedgerInvalidVersionReply
|
||||||
}
|
}
|
||||||
// Cache the version for future reference
|
// Cache the version for future reference
|
||||||
var version [3]byte
|
var version [3]byte
|
||||||
|
|
||||||
copy(version[:], reply[1:])
|
copy(version[:], reply[1:])
|
||||||
|
|
||||||
return version, nil
|
return version, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,6 +268,7 @@ func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, er
|
||||||
// Flatten the derivation path into the Ledger request
|
// Flatten the derivation path into the Ledger request
|
||||||
path := make([]byte, 1+4*len(derivationPath))
|
path := make([]byte, 1+4*len(derivationPath))
|
||||||
path[0] = byte(len(derivationPath))
|
path[0] = byte(len(derivationPath))
|
||||||
|
|
||||||
for i, component := range derivationPath {
|
for i, component := range derivationPath {
|
||||||
binary.BigEndian.PutUint32(path[1+4*i:], component)
|
binary.BigEndian.PutUint32(path[1+4*i:], component)
|
||||||
}
|
}
|
||||||
|
|
@ -269,12 +281,14 @@ func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, er
|
||||||
if len(reply) < 1 || len(reply) < 1+int(reply[0]) {
|
if len(reply) < 1 || len(reply) < 1+int(reply[0]) {
|
||||||
return common.Address{}, errors.New("reply lacks public key entry")
|
return common.Address{}, errors.New("reply lacks public key entry")
|
||||||
}
|
}
|
||||||
|
|
||||||
reply = reply[1+int(reply[0]):]
|
reply = reply[1+int(reply[0]):]
|
||||||
|
|
||||||
// Extract the Ethereum hex address string
|
// Extract the Ethereum hex address string
|
||||||
if len(reply) < 1 || len(reply) < 1+int(reply[0]) {
|
if len(reply) < 1 || len(reply) < 1+int(reply[0]) {
|
||||||
return common.Address{}, errors.New("reply lacks address entry")
|
return common.Address{}, errors.New("reply lacks address entry")
|
||||||
}
|
}
|
||||||
|
|
||||||
hexstr := reply[1 : 1+int(reply[0])]
|
hexstr := reply[1 : 1+int(reply[0])]
|
||||||
|
|
||||||
// Decode the hex string into an Ethereum address and return
|
// Decode the hex string into an Ethereum address and return
|
||||||
|
|
@ -282,6 +296,7 @@ func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, er
|
||||||
if _, err = hex.Decode(address[:], hexstr); err != nil {
|
if _, err = hex.Decode(address[:], hexstr); err != nil {
|
||||||
return common.Address{}, err
|
return common.Address{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return address, nil
|
return address, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -323,6 +338,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
|
||||||
// Flatten the derivation path into the Ledger request
|
// Flatten the derivation path into the Ledger request
|
||||||
path := make([]byte, 1+4*len(derivationPath))
|
path := make([]byte, 1+4*len(derivationPath))
|
||||||
path[0] = byte(len(derivationPath))
|
path[0] = byte(len(derivationPath))
|
||||||
|
|
||||||
for i, component := range derivationPath {
|
for i, component := range derivationPath {
|
||||||
binary.BigEndian.PutUint32(path[1+4*i:], component)
|
binary.BigEndian.PutUint32(path[1+4*i:], component)
|
||||||
}
|
}
|
||||||
|
|
@ -331,6 +347,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
|
||||||
txrlp []byte
|
txrlp []byte
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if chainID == nil {
|
if chainID == nil {
|
||||||
if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data()}); err != nil {
|
if txrlp, err = rlp.EncodeToBytes([]interface{}{tx.Nonce(), tx.GasPrice(), tx.Gas(), tx.To(), tx.Value(), tx.Data()}); err != nil {
|
||||||
return common.Address{}, nil, err
|
return common.Address{}, nil, err
|
||||||
|
|
@ -340,6 +357,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
|
||||||
return common.Address{}, nil, err
|
return common.Address{}, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
payload := append(path, txrlp...)
|
payload := append(path, txrlp...)
|
||||||
|
|
||||||
// Send the request and wait for the response
|
// Send the request and wait for the response
|
||||||
|
|
@ -347,9 +365,15 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
|
||||||
op = ledgerP1InitTransactionData
|
op = ledgerP1InitTransactionData
|
||||||
reply []byte
|
reply []byte
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Chunk size selection to mitigate an underlying RLP deserialization issue on the ledger app.
|
||||||
|
// https://github.com/LedgerHQ/app-ethereum/issues/409
|
||||||
|
chunk := 255
|
||||||
|
for ; len(payload)%chunk <= ledgerEip155Size; chunk-- {
|
||||||
|
}
|
||||||
|
|
||||||
for len(payload) > 0 {
|
for len(payload) > 0 {
|
||||||
// Calculate the size of the next data chunk
|
// Calculate the size of the next data chunk
|
||||||
chunk := 255
|
|
||||||
if chunk > len(payload) {
|
if chunk > len(payload) {
|
||||||
chunk = len(payload)
|
chunk = len(payload)
|
||||||
}
|
}
|
||||||
|
|
@ -366,6 +390,7 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
|
||||||
if len(reply) != crypto.SignatureLength {
|
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])
|
||||||
|
|
||||||
// Create the correct signer and signature transform based on the chain ID
|
// Create the correct signer and signature transform based on the chain ID
|
||||||
|
|
@ -376,14 +401,17 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
|
||||||
signer = types.NewEIP155Signer(chainID)
|
signer = types.NewEIP155Signer(chainID)
|
||||||
signature[64] -= byte(chainID.Uint64()*2 + 35)
|
signature[64] -= byte(chainID.Uint64()*2 + 35)
|
||||||
}
|
}
|
||||||
|
|
||||||
signed, err := tx.WithSignature(signer, signature)
|
signed, err := tx.WithSignature(signer, signature)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, nil, err
|
return common.Address{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sender, err := types.Sender(signer, signed)
|
sender, err := types.Sender(signer, signed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, nil, err
|
return common.Address{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return sender, signed, nil
|
return sender, signed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -407,8 +435,6 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
|
||||||
// domain hash | 32 bytes
|
// domain hash | 32 bytes
|
||||||
// message hash | 32 bytes
|
// message hash | 32 bytes
|
||||||
//
|
//
|
||||||
//
|
|
||||||
//
|
|
||||||
// And the output data is:
|
// And the output data is:
|
||||||
//
|
//
|
||||||
// Description | Length
|
// Description | Length
|
||||||
|
|
@ -420,6 +446,7 @@ func (w *ledgerDriver) ledgerSignTypedMessage(derivationPath []uint32, domainHas
|
||||||
// Flatten the derivation path into the Ledger request
|
// Flatten the derivation path into the Ledger request
|
||||||
path := make([]byte, 1+4*len(derivationPath))
|
path := make([]byte, 1+4*len(derivationPath))
|
||||||
path[0] = byte(len(derivationPath))
|
path[0] = byte(len(derivationPath))
|
||||||
|
|
||||||
for i, component := range derivationPath {
|
for i, component := range derivationPath {
|
||||||
binary.BigEndian.PutUint32(path[1+4*i:], component)
|
binary.BigEndian.PutUint32(path[1+4*i:], component)
|
||||||
}
|
}
|
||||||
|
|
@ -445,7 +472,9 @@ func (w *ledgerDriver) ledgerSignTypedMessage(derivationPath []uint32, domainHas
|
||||||
if len(reply) != crypto.SignatureLength {
|
if len(reply) != crypto.SignatureLength {
|
||||||
return nil, errors.New("reply lacks signature")
|
return nil, errors.New("reply lacks signature")
|
||||||
}
|
}
|
||||||
|
|
||||||
signature := append(reply[1:], reply[0])
|
signature := append(reply[1:], reply[0])
|
||||||
|
|
||||||
return signature, nil
|
return signature, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -509,18 +538,22 @@ func (w *ledgerDriver) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 l
|
||||||
}
|
}
|
||||||
// Send over to the device
|
// Send over to the device
|
||||||
w.log.Trace("Data chunk sent to the Ledger", "chunk", hexutil.Bytes(chunk))
|
w.log.Trace("Data chunk sent to the Ledger", "chunk", hexutil.Bytes(chunk))
|
||||||
|
|
||||||
if _, err := w.device.Write(chunk); err != nil {
|
if _, err := w.device.Write(chunk); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Stream the reply back from the wallet in 64 byte chunks
|
// Stream the reply back from the wallet in 64 byte chunks
|
||||||
var reply []byte
|
var reply []byte
|
||||||
|
|
||||||
chunk = chunk[:64] // Yeah, we surely have enough space
|
chunk = chunk[:64] // Yeah, we surely have enough space
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// Read the next chunk from the Ledger wallet
|
// Read the next chunk from the Ledger wallet
|
||||||
if _, err := io.ReadFull(w.device, chunk); err != nil {
|
if _, err := io.ReadFull(w.device, chunk); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
w.log.Trace("Data chunk received from the Ledger", "chunk", hexutil.Bytes(chunk))
|
w.log.Trace("Data chunk received from the Ledger", "chunk", hexutil.Bytes(chunk))
|
||||||
|
|
||||||
// Make sure the transport header matches
|
// Make sure the transport header matches
|
||||||
|
|
@ -544,5 +577,6 @@ func (w *ledgerDriver) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 l
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return reply[:len(reply)-2], nil
|
return reply[:len(reply)-2], nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,12 +73,15 @@ func (w *trezorDriver) Status() (string, error) {
|
||||||
if w.failure != nil {
|
if w.failure != nil {
|
||||||
return fmt.Sprintf("Failed: %v", w.failure), w.failure
|
return fmt.Sprintf("Failed: %v", w.failure), w.failure
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.device == nil {
|
if w.device == nil {
|
||||||
return "Closed", w.failure
|
return "Closed", w.failure
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.pinwait {
|
if w.pinwait {
|
||||||
return fmt.Sprintf("Trezor v%d.%d.%d '%s' waiting for PIN", w.version[0], w.version[1], w.version[2], w.label), w.failure
|
return fmt.Sprintf("Trezor v%d.%d.%d '%s' waiting for PIN", w.version[0], w.version[1], w.version[2], w.label), w.failure
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("Trezor v%d.%d.%d '%s' online", w.version[0], w.version[1], w.version[2], w.label), w.failure
|
return fmt.Sprintf("Trezor v%d.%d.%d '%s' online", w.version[0], w.version[1], w.version[2], w.label), w.failure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -107,12 +110,14 @@ func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
|
||||||
if _, err := w.trezorExchange(&trezor.Initialize{}, features); err != nil {
|
if _, err := w.trezorExchange(&trezor.Initialize{}, features); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()}
|
w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()}
|
||||||
w.label = features.GetLabel()
|
w.label = features.GetLabel()
|
||||||
|
|
||||||
// Do a manual ping, forcing the device to ask for its PIN and Passphrase
|
// Do a manual ping, forcing the device to ask for its PIN and Passphrase
|
||||||
askPin := true
|
askPin := true
|
||||||
askPassphrase := true
|
askPassphrase := true
|
||||||
|
|
||||||
res, err := w.trezorExchange(&trezor.Ping{PinProtection: &askPin, PassphraseProtection: &askPassphrase}, new(trezor.PinMatrixRequest), new(trezor.PassphraseRequest), new(trezor.Success))
|
res, err := w.trezorExchange(&trezor.Ping{PinProtection: &askPin, PassphraseProtection: &askPassphrase}, new(trezor.PinMatrixRequest), new(trezor.PassphraseRequest), new(trezor.Success))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -125,6 +130,7 @@ func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
|
||||||
case 1:
|
case 1:
|
||||||
w.pinwait = false
|
w.pinwait = false
|
||||||
w.passphrasewait = true
|
w.passphrasewait = true
|
||||||
|
|
||||||
return ErrTrezorPassphraseNeeded
|
return ErrTrezorPassphraseNeeded
|
||||||
case 2:
|
case 2:
|
||||||
return nil // responded with trezor.Success
|
return nil // responded with trezor.Success
|
||||||
|
|
@ -134,10 +140,12 @@ func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
|
||||||
if w.pinwait {
|
if w.pinwait {
|
||||||
w.pinwait = false
|
w.pinwait = false
|
||||||
res, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success), new(trezor.PassphraseRequest))
|
res, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success), new(trezor.PassphraseRequest))
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.failure = err
|
w.failure = err
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if res == 1 {
|
if res == 1 {
|
||||||
w.passphrasewait = true
|
w.passphrasewait = true
|
||||||
return ErrTrezorPassphraseNeeded
|
return ErrTrezorPassphraseNeeded
|
||||||
|
|
@ -167,6 +175,7 @@ func (w *trezorDriver) Heartbeat() error {
|
||||||
w.failure = err
|
w.failure = err
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,6 +191,7 @@ func (w *trezorDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio
|
||||||
if w.device == nil {
|
if w.device == nil {
|
||||||
return common.Address{}, nil, accounts.ErrWalletClosed
|
return common.Address{}, nil, accounts.ErrWalletClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
return w.trezorSign(path, tx, chainID)
|
return w.trezorSign(path, tx, chainID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,6 +214,7 @@ func (w *trezorDriver) trezorDerive(derivationPath []uint32) (common.Address, er
|
||||||
if addr := address.GetAddressHex(); len(addr) > 0 { // Newer firmwares use hexadecimal formats
|
if addr := address.GetAddressHex(); len(addr) > 0 { // Newer firmwares use hexadecimal formats
|
||||||
return common.HexToAddress(addr), nil
|
return common.HexToAddress(addr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return common.Address{}, errors.New("missing derived address")
|
return common.Address{}, errors.New("missing derived address")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -222,17 +233,20 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
|
||||||
Value: tx.Value().Bytes(),
|
Value: tx.Value().Bytes(),
|
||||||
DataLength: &length,
|
DataLength: &length,
|
||||||
}
|
}
|
||||||
|
|
||||||
if to := tx.To(); to != nil {
|
if to := tx.To(); to != nil {
|
||||||
// Non contract deploy, set recipient explicitly
|
// Non contract deploy, set recipient explicitly
|
||||||
hex := to.Hex()
|
hex := to.Hex()
|
||||||
request.ToHex = &hex // Newer firmwares (old will ignore)
|
request.ToHex = &hex // Newer firmwares (old will ignore)
|
||||||
request.ToBin = (*to)[:] // Older firmwares (new will ignore)
|
request.ToBin = (*to)[:] // Older firmwares (new will ignore)
|
||||||
}
|
}
|
||||||
|
|
||||||
if length > 1024 { // Send the data chunked if that was requested
|
if length > 1024 { // Send the data chunked if that was requested
|
||||||
request.DataInitialChunk, data = data[:1024], data[1024:]
|
request.DataInitialChunk, data = data[:1024], data[1024:]
|
||||||
} else {
|
} else {
|
||||||
request.DataInitialChunk, data = data, nil
|
request.DataInitialChunk, data = data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if chainID != nil { // EIP-155 transaction, set chain ID explicitly (only 32 bit is supported!?)
|
if chainID != nil { // EIP-155 transaction, set chain ID explicitly (only 32 bit is supported!?)
|
||||||
id := uint32(chainID.Int64())
|
id := uint32(chainID.Int64())
|
||||||
request.ChainId = &id
|
request.ChainId = &id
|
||||||
|
|
@ -242,6 +256,7 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
|
||||||
if _, err := w.trezorExchange(request, response); err != nil {
|
if _, err := w.trezorExchange(request, response); err != nil {
|
||||||
return common.Address{}, nil, err
|
return common.Address{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for response.DataLength != nil && int(*response.DataLength) <= len(data) {
|
for response.DataLength != nil && int(*response.DataLength) <= len(data) {
|
||||||
chunk := data[:*response.DataLength]
|
chunk := data[:*response.DataLength]
|
||||||
data = data[*response.DataLength:]
|
data = data[*response.DataLength:]
|
||||||
|
|
@ -254,6 +269,7 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
|
||||||
if len(response.GetSignatureR()) == 0 || len(response.GetSignatureS()) == 0 || response.GetSignatureV() == 0 {
|
if len(response.GetSignatureR()) == 0 || len(response.GetSignatureS()) == 0 || response.GetSignatureV() == 0 {
|
||||||
return common.Address{}, nil, errors.New("reply lacks signature")
|
return common.Address{}, nil, errors.New("reply lacks signature")
|
||||||
}
|
}
|
||||||
|
|
||||||
signature := append(append(response.GetSignatureR(), response.GetSignatureS()...), byte(response.GetSignatureV()))
|
signature := append(append(response.GetSignatureR(), response.GetSignatureS()...), byte(response.GetSignatureV()))
|
||||||
|
|
||||||
// Create the correct signer and signature transform based on the chain ID
|
// Create the correct signer and signature transform based on the chain ID
|
||||||
|
|
@ -271,10 +287,12 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, nil, err
|
return common.Address{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
sender, err := types.Sender(signer, signed)
|
sender, err := types.Sender(signer, signed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Address{}, nil, err
|
return common.Address{}, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return sender, signed, nil
|
return sender, signed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,6 +305,7 @@ func (w *trezorDriver) trezorExchange(req proto.Message, results ...proto.Messag
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
payload := make([]byte, 8+len(data))
|
payload := make([]byte, 8+len(data))
|
||||||
copy(payload, []byte{0x23, 0x23})
|
copy(payload, []byte{0x23, 0x23})
|
||||||
binary.BigEndian.PutUint16(payload[2:], trezor.Type(req))
|
binary.BigEndian.PutUint16(payload[2:], trezor.Type(req))
|
||||||
|
|
@ -309,6 +328,7 @@ func (w *trezorDriver) trezorExchange(req proto.Message, results ...proto.Messag
|
||||||
}
|
}
|
||||||
// Send over to the device
|
// Send over to the device
|
||||||
w.log.Trace("Data chunk sent to the Trezor", "chunk", hexutil.Bytes(chunk))
|
w.log.Trace("Data chunk sent to the Trezor", "chunk", hexutil.Bytes(chunk))
|
||||||
|
|
||||||
if _, err := w.device.Write(chunk); err != nil {
|
if _, err := w.device.Write(chunk); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
@ -318,11 +338,13 @@ func (w *trezorDriver) trezorExchange(req proto.Message, results ...proto.Messag
|
||||||
kind uint16
|
kind uint16
|
||||||
reply []byte
|
reply []byte
|
||||||
)
|
)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// Read the next chunk from the Trezor wallet
|
// Read the next chunk from the Trezor wallet
|
||||||
if _, err := io.ReadFull(w.device, chunk); err != nil {
|
if _, err := io.ReadFull(w.device, chunk); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
w.log.Trace("Data chunk received from the Trezor", "chunk", hexutil.Bytes(chunk))
|
w.log.Trace("Data chunk received from the Trezor", "chunk", hexutil.Bytes(chunk))
|
||||||
|
|
||||||
// Make sure the transport header matches
|
// Make sure the transport header matches
|
||||||
|
|
@ -354,20 +376,25 @@ func (w *trezorDriver) trezorExchange(req proto.Message, results ...proto.Messag
|
||||||
if err := proto.Unmarshal(reply, failure); err != nil {
|
if err := proto.Unmarshal(reply, failure); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0, errors.New("trezor: " + failure.GetMessage())
|
return 0, errors.New("trezor: " + failure.GetMessage())
|
||||||
}
|
}
|
||||||
|
|
||||||
if kind == uint16(trezor.MessageType_MessageType_ButtonRequest) {
|
if kind == uint16(trezor.MessageType_MessageType_ButtonRequest) {
|
||||||
// Trezor is waiting for user confirmation, ack and wait for the next message
|
// Trezor is waiting for user confirmation, ack and wait for the next message
|
||||||
return w.trezorExchange(&trezor.ButtonAck{}, results...)
|
return w.trezorExchange(&trezor.ButtonAck{}, results...)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, res := range results {
|
for i, res := range results {
|
||||||
if trezor.Type(res) == kind {
|
if trezor.Type(res) == kind {
|
||||||
return i, proto.Unmarshal(reply, res)
|
return i, proto.Unmarshal(reply, res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
expected := make([]string, len(results))
|
expected := make([]string, len(results))
|
||||||
for i, res := range results {
|
for i, res := range results {
|
||||||
expected[i] = trezor.Name(trezor.Type(res))
|
expected[i] = trezor.Name(trezor.Type(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0, fmt.Errorf("trezor: expected reply types %s, got %s", expected, trezor.Name(kind))
|
return 0, fmt.Errorf("trezor: expected reply types %s, got %s", expected, trezor.Name(kind))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ var Failure_FailureType_value = map[string]int32{
|
||||||
func (x Failure_FailureType) Enum() *Failure_FailureType {
|
func (x Failure_FailureType) Enum() *Failure_FailureType {
|
||||||
p := new(Failure_FailureType)
|
p := new(Failure_FailureType)
|
||||||
*p = x
|
*p = x
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,7 +87,9 @@ func (x *Failure_FailureType) UnmarshalJSON(data []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
*x = Failure_FailureType(value)
|
*x = Failure_FailureType(value)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -155,6 +158,7 @@ var ButtonRequest_ButtonRequestType_value = map[string]int32{
|
||||||
func (x ButtonRequest_ButtonRequestType) Enum() *ButtonRequest_ButtonRequestType {
|
func (x ButtonRequest_ButtonRequestType) Enum() *ButtonRequest_ButtonRequestType {
|
||||||
p := new(ButtonRequest_ButtonRequestType)
|
p := new(ButtonRequest_ButtonRequestType)
|
||||||
*p = x
|
*p = x
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,7 +171,9 @@ func (x *ButtonRequest_ButtonRequestType) UnmarshalJSON(data []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
*x = ButtonRequest_ButtonRequestType(value)
|
*x = ButtonRequest_ButtonRequestType(value)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,6 +206,7 @@ var PinMatrixRequest_PinMatrixRequestType_value = map[string]int32{
|
||||||
func (x PinMatrixRequest_PinMatrixRequestType) Enum() *PinMatrixRequest_PinMatrixRequestType {
|
func (x PinMatrixRequest_PinMatrixRequestType) Enum() *PinMatrixRequest_PinMatrixRequestType {
|
||||||
p := new(PinMatrixRequest_PinMatrixRequestType)
|
p := new(PinMatrixRequest_PinMatrixRequestType)
|
||||||
*p = x
|
*p = x
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,7 +219,9 @@ func (x *PinMatrixRequest_PinMatrixRequestType) UnmarshalJSON(data []byte) error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
*x = PinMatrixRequest_PinMatrixRequestType(value)
|
*x = PinMatrixRequest_PinMatrixRequestType(value)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,6 +268,7 @@ func (m *Success) GetMessage() string {
|
||||||
if m != nil && m.Message != nil {
|
if m != nil && m.Message != nil {
|
||||||
return *m.Message
|
return *m.Message
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -302,6 +312,7 @@ func (m *Failure) GetCode() Failure_FailureType {
|
||||||
if m != nil && m.Code != nil {
|
if m != nil && m.Code != nil {
|
||||||
return *m.Code
|
return *m.Code
|
||||||
}
|
}
|
||||||
|
|
||||||
return Failure_Failure_UnexpectedMessage
|
return Failure_Failure_UnexpectedMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -309,6 +320,7 @@ func (m *Failure) GetMessage() string {
|
||||||
if m != nil && m.Message != nil {
|
if m != nil && m.Message != nil {
|
||||||
return *m.Message
|
return *m.Message
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -353,6 +365,7 @@ func (m *ButtonRequest) GetCode() ButtonRequest_ButtonRequestType {
|
||||||
if m != nil && m.Code != nil {
|
if m != nil && m.Code != nil {
|
||||||
return *m.Code
|
return *m.Code
|
||||||
}
|
}
|
||||||
|
|
||||||
return ButtonRequest_ButtonRequest_Other
|
return ButtonRequest_ButtonRequest_Other
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -360,6 +373,7 @@ func (m *ButtonRequest) GetData() string {
|
||||||
if m != nil && m.Data != nil {
|
if m != nil && m.Data != nil {
|
||||||
return *m.Data
|
return *m.Data
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -437,6 +451,7 @@ func (m *PinMatrixRequest) GetType() PinMatrixRequest_PinMatrixRequestType {
|
||||||
if m != nil && m.Type != nil {
|
if m != nil && m.Type != nil {
|
||||||
return *m.Type
|
return *m.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
return PinMatrixRequest_PinMatrixRequestType_Current
|
return PinMatrixRequest_PinMatrixRequestType_Current
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -479,6 +494,7 @@ func (m *PinMatrixAck) GetPin() string {
|
||||||
if m != nil && m.Pin != nil {
|
if m != nil && m.Pin != nil {
|
||||||
return *m.Pin
|
return *m.Pin
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -522,6 +538,7 @@ func (m *PassphraseRequest) GetOnDevice() bool {
|
||||||
if m != nil && m.OnDevice != nil {
|
if m != nil && m.OnDevice != nil {
|
||||||
return *m.OnDevice
|
return *m.OnDevice
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -565,6 +582,7 @@ func (m *PassphraseAck) GetPassphrase() string {
|
||||||
if m != nil && m.Passphrase != nil {
|
if m != nil && m.Passphrase != nil {
|
||||||
return *m.Passphrase
|
return *m.Passphrase
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -572,6 +590,7 @@ func (m *PassphraseAck) GetState() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.State
|
return m.State
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -614,6 +633,7 @@ func (m *PassphraseStateRequest) GetState() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.State
|
return m.State
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -696,6 +716,7 @@ func (m *HDNodeType) GetDepth() uint32 {
|
||||||
if m != nil && m.Depth != nil {
|
if m != nil && m.Depth != nil {
|
||||||
return *m.Depth
|
return *m.Depth
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -703,6 +724,7 @@ func (m *HDNodeType) GetFingerprint() uint32 {
|
||||||
if m != nil && m.Fingerprint != nil {
|
if m != nil && m.Fingerprint != nil {
|
||||||
return *m.Fingerprint
|
return *m.Fingerprint
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -710,6 +732,7 @@ func (m *HDNodeType) GetChildNum() uint32 {
|
||||||
if m != nil && m.ChildNum != nil {
|
if m != nil && m.ChildNum != nil {
|
||||||
return *m.ChildNum
|
return *m.ChildNum
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -717,6 +740,7 @@ func (m *HDNodeType) GetChainCode() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.ChainCode
|
return m.ChainCode
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -724,6 +748,7 @@ func (m *HDNodeType) GetPrivateKey() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.PrivateKey
|
return m.PrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -731,6 +756,7 @@ func (m *HDNodeType) GetPublicKey() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.PublicKey
|
return m.PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ func (m *EthereumGetPublicKey) GetAddressN() []uint32 {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.AddressN
|
return m.AddressN
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,6 +71,7 @@ func (m *EthereumGetPublicKey) GetShowDisplay() bool {
|
||||||
if m != nil && m.ShowDisplay != nil {
|
if m != nil && m.ShowDisplay != nil {
|
||||||
return *m.ShowDisplay
|
return *m.ShowDisplay
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,6 +115,7 @@ func (m *EthereumPublicKey) GetNode() *HDNodeType {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.Node
|
return m.Node
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,6 +123,7 @@ func (m *EthereumPublicKey) GetXpub() string {
|
||||||
if m != nil && m.Xpub != nil {
|
if m != nil && m.Xpub != nil {
|
||||||
return *m.Xpub
|
return *m.Xpub
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -165,6 +169,7 @@ func (m *EthereumGetAddress) GetAddressN() []uint32 {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.AddressN
|
return m.AddressN
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,6 +177,7 @@ func (m *EthereumGetAddress) GetShowDisplay() bool {
|
||||||
if m != nil && m.ShowDisplay != nil {
|
if m != nil && m.ShowDisplay != nil {
|
||||||
return *m.ShowDisplay
|
return *m.ShowDisplay
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,6 +221,7 @@ func (m *EthereumAddress) GetAddressBin() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.AddressBin
|
return m.AddressBin
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -222,6 +229,7 @@ func (m *EthereumAddress) GetAddressHex() string {
|
||||||
if m != nil && m.AddressHex != nil {
|
if m != nil && m.AddressHex != nil {
|
||||||
return *m.AddressHex
|
return *m.AddressHex
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -278,6 +286,7 @@ func (m *EthereumSignTx) GetAddressN() []uint32 {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.AddressN
|
return m.AddressN
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -285,6 +294,7 @@ func (m *EthereumSignTx) GetNonce() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.Nonce
|
return m.Nonce
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -292,6 +302,7 @@ func (m *EthereumSignTx) GetGasPrice() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.GasPrice
|
return m.GasPrice
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -299,6 +310,7 @@ func (m *EthereumSignTx) GetGasLimit() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.GasLimit
|
return m.GasLimit
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -306,6 +318,7 @@ func (m *EthereumSignTx) GetToBin() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.ToBin
|
return m.ToBin
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -313,6 +326,7 @@ func (m *EthereumSignTx) GetToHex() string {
|
||||||
if m != nil && m.ToHex != nil {
|
if m != nil && m.ToHex != nil {
|
||||||
return *m.ToHex
|
return *m.ToHex
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -320,6 +334,7 @@ func (m *EthereumSignTx) GetValue() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.Value
|
return m.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -327,6 +342,7 @@ func (m *EthereumSignTx) GetDataInitialChunk() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.DataInitialChunk
|
return m.DataInitialChunk
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -334,6 +350,7 @@ func (m *EthereumSignTx) GetDataLength() uint32 {
|
||||||
if m != nil && m.DataLength != nil {
|
if m != nil && m.DataLength != nil {
|
||||||
return *m.DataLength
|
return *m.DataLength
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -341,6 +358,7 @@ func (m *EthereumSignTx) GetChainId() uint32 {
|
||||||
if m != nil && m.ChainId != nil {
|
if m != nil && m.ChainId != nil {
|
||||||
return *m.ChainId
|
return *m.ChainId
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -348,6 +366,7 @@ func (m *EthereumSignTx) GetTxType() uint32 {
|
||||||
if m != nil && m.TxType != nil {
|
if m != nil && m.TxType != nil {
|
||||||
return *m.TxType
|
return *m.TxType
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -396,6 +415,7 @@ func (m *EthereumTxRequest) GetDataLength() uint32 {
|
||||||
if m != nil && m.DataLength != nil {
|
if m != nil && m.DataLength != nil {
|
||||||
return *m.DataLength
|
return *m.DataLength
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -403,6 +423,7 @@ func (m *EthereumTxRequest) GetSignatureV() uint32 {
|
||||||
if m != nil && m.SignatureV != nil {
|
if m != nil && m.SignatureV != nil {
|
||||||
return *m.SignatureV
|
return *m.SignatureV
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -410,6 +431,7 @@ func (m *EthereumTxRequest) GetSignatureR() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.SignatureR
|
return m.SignatureR
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -417,6 +439,7 @@ func (m *EthereumTxRequest) GetSignatureS() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.SignatureS
|
return m.SignatureS
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -459,6 +482,7 @@ func (m *EthereumTxAck) GetDataChunk() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.DataChunk
|
return m.DataChunk
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -504,6 +528,7 @@ func (m *EthereumSignMessage) GetAddressN() []uint32 {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.AddressN
|
return m.AddressN
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -511,6 +536,7 @@ func (m *EthereumSignMessage) GetMessage() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.Message
|
return m.Message
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -555,6 +581,7 @@ func (m *EthereumMessageSignature) GetAddressBin() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.AddressBin
|
return m.AddressBin
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -562,6 +589,7 @@ func (m *EthereumMessageSignature) GetSignature() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.Signature
|
return m.Signature
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -569,6 +597,7 @@ func (m *EthereumMessageSignature) GetAddressHex() string {
|
||||||
if m != nil && m.AddressHex != nil {
|
if m != nil && m.AddressHex != nil {
|
||||||
return *m.AddressHex
|
return *m.AddressHex
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -616,6 +645,7 @@ func (m *EthereumVerifyMessage) GetAddressBin() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.AddressBin
|
return m.AddressBin
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -623,6 +653,7 @@ func (m *EthereumVerifyMessage) GetSignature() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.Signature
|
return m.Signature
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -630,6 +661,7 @@ func (m *EthereumVerifyMessage) GetMessage() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.Message
|
return m.Message
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -637,6 +669,7 @@ func (m *EthereumVerifyMessage) GetAddressHex() string {
|
||||||
if m != nil && m.AddressHex != nil {
|
if m != nil && m.AddressHex != nil {
|
||||||
return *m.AddressHex
|
return *m.AddressHex
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ var ApplySettings_PassphraseSourceType_value = map[string]int32{
|
||||||
func (x ApplySettings_PassphraseSourceType) Enum() *ApplySettings_PassphraseSourceType {
|
func (x ApplySettings_PassphraseSourceType) Enum() *ApplySettings_PassphraseSourceType {
|
||||||
p := new(ApplySettings_PassphraseSourceType)
|
p := new(ApplySettings_PassphraseSourceType)
|
||||||
*p = x
|
*p = x
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,7 +59,9 @@ func (x *ApplySettings_PassphraseSourceType) UnmarshalJSON(data []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
*x = ApplySettings_PassphraseSourceType(value)
|
*x = ApplySettings_PassphraseSourceType(value)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,6 +97,7 @@ var RecoveryDevice_RecoveryDeviceType_value = map[string]int32{
|
||||||
func (x RecoveryDevice_RecoveryDeviceType) Enum() *RecoveryDevice_RecoveryDeviceType {
|
func (x RecoveryDevice_RecoveryDeviceType) Enum() *RecoveryDevice_RecoveryDeviceType {
|
||||||
p := new(RecoveryDevice_RecoveryDeviceType)
|
p := new(RecoveryDevice_RecoveryDeviceType)
|
||||||
*p = x
|
*p = x
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,7 +110,9 @@ func (x *RecoveryDevice_RecoveryDeviceType) UnmarshalJSON(data []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
*x = RecoveryDevice_RecoveryDeviceType(value)
|
*x = RecoveryDevice_RecoveryDeviceType(value)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,6 +145,7 @@ var WordRequest_WordRequestType_value = map[string]int32{
|
||||||
func (x WordRequest_WordRequestType) Enum() *WordRequest_WordRequestType {
|
func (x WordRequest_WordRequestType) Enum() *WordRequest_WordRequestType {
|
||||||
p := new(WordRequest_WordRequestType)
|
p := new(WordRequest_WordRequestType)
|
||||||
*p = x
|
*p = x
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -151,7 +158,9 @@ func (x *WordRequest_WordRequestType) UnmarshalJSON(data []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
*x = WordRequest_WordRequestType(value)
|
*x = WordRequest_WordRequestType(value)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,6 +209,7 @@ func (m *Initialize) GetState() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.State
|
return m.State
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -207,6 +217,7 @@ func (m *Initialize) GetSkipPassphrase() bool {
|
||||||
if m != nil && m.SkipPassphrase != nil {
|
if m != nil && m.SkipPassphrase != nil {
|
||||||
return *m.SkipPassphrase
|
return *m.SkipPassphrase
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -310,6 +321,7 @@ func (m *Features) GetVendor() string {
|
||||||
if m != nil && m.Vendor != nil {
|
if m != nil && m.Vendor != nil {
|
||||||
return *m.Vendor
|
return *m.Vendor
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -317,6 +329,7 @@ func (m *Features) GetMajorVersion() uint32 {
|
||||||
if m != nil && m.MajorVersion != nil {
|
if m != nil && m.MajorVersion != nil {
|
||||||
return *m.MajorVersion
|
return *m.MajorVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -324,6 +337,7 @@ func (m *Features) GetMinorVersion() uint32 {
|
||||||
if m != nil && m.MinorVersion != nil {
|
if m != nil && m.MinorVersion != nil {
|
||||||
return *m.MinorVersion
|
return *m.MinorVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -331,6 +345,7 @@ func (m *Features) GetPatchVersion() uint32 {
|
||||||
if m != nil && m.PatchVersion != nil {
|
if m != nil && m.PatchVersion != nil {
|
||||||
return *m.PatchVersion
|
return *m.PatchVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -338,6 +353,7 @@ func (m *Features) GetBootloaderMode() bool {
|
||||||
if m != nil && m.BootloaderMode != nil {
|
if m != nil && m.BootloaderMode != nil {
|
||||||
return *m.BootloaderMode
|
return *m.BootloaderMode
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -345,6 +361,7 @@ func (m *Features) GetDeviceId() string {
|
||||||
if m != nil && m.DeviceId != nil {
|
if m != nil && m.DeviceId != nil {
|
||||||
return *m.DeviceId
|
return *m.DeviceId
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -352,6 +369,7 @@ func (m *Features) GetPinProtection() bool {
|
||||||
if m != nil && m.PinProtection != nil {
|
if m != nil && m.PinProtection != nil {
|
||||||
return *m.PinProtection
|
return *m.PinProtection
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -359,6 +377,7 @@ func (m *Features) GetPassphraseProtection() bool {
|
||||||
if m != nil && m.PassphraseProtection != nil {
|
if m != nil && m.PassphraseProtection != nil {
|
||||||
return *m.PassphraseProtection
|
return *m.PassphraseProtection
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -366,6 +385,7 @@ func (m *Features) GetLanguage() string {
|
||||||
if m != nil && m.Language != nil {
|
if m != nil && m.Language != nil {
|
||||||
return *m.Language
|
return *m.Language
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -373,6 +393,7 @@ func (m *Features) GetLabel() string {
|
||||||
if m != nil && m.Label != nil {
|
if m != nil && m.Label != nil {
|
||||||
return *m.Label
|
return *m.Label
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -380,6 +401,7 @@ func (m *Features) GetInitialized() bool {
|
||||||
if m != nil && m.Initialized != nil {
|
if m != nil && m.Initialized != nil {
|
||||||
return *m.Initialized
|
return *m.Initialized
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -387,6 +409,7 @@ func (m *Features) GetRevision() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.Revision
|
return m.Revision
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -394,6 +417,7 @@ func (m *Features) GetBootloaderHash() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.BootloaderHash
|
return m.BootloaderHash
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -401,6 +425,7 @@ func (m *Features) GetImported() bool {
|
||||||
if m != nil && m.Imported != nil {
|
if m != nil && m.Imported != nil {
|
||||||
return *m.Imported
|
return *m.Imported
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -408,6 +433,7 @@ func (m *Features) GetPinCached() bool {
|
||||||
if m != nil && m.PinCached != nil {
|
if m != nil && m.PinCached != nil {
|
||||||
return *m.PinCached
|
return *m.PinCached
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -415,6 +441,7 @@ func (m *Features) GetPassphraseCached() bool {
|
||||||
if m != nil && m.PassphraseCached != nil {
|
if m != nil && m.PassphraseCached != nil {
|
||||||
return *m.PassphraseCached
|
return *m.PassphraseCached
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -422,6 +449,7 @@ func (m *Features) GetFirmwarePresent() bool {
|
||||||
if m != nil && m.FirmwarePresent != nil {
|
if m != nil && m.FirmwarePresent != nil {
|
||||||
return *m.FirmwarePresent
|
return *m.FirmwarePresent
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -429,6 +457,7 @@ func (m *Features) GetNeedsBackup() bool {
|
||||||
if m != nil && m.NeedsBackup != nil {
|
if m != nil && m.NeedsBackup != nil {
|
||||||
return *m.NeedsBackup
|
return *m.NeedsBackup
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -436,6 +465,7 @@ func (m *Features) GetFlags() uint32 {
|
||||||
if m != nil && m.Flags != nil {
|
if m != nil && m.Flags != nil {
|
||||||
return *m.Flags
|
return *m.Flags
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -443,6 +473,7 @@ func (m *Features) GetModel() string {
|
||||||
if m != nil && m.Model != nil {
|
if m != nil && m.Model != nil {
|
||||||
return *m.Model
|
return *m.Model
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -450,6 +481,7 @@ func (m *Features) GetFwMajor() uint32 {
|
||||||
if m != nil && m.FwMajor != nil {
|
if m != nil && m.FwMajor != nil {
|
||||||
return *m.FwMajor
|
return *m.FwMajor
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -457,6 +489,7 @@ func (m *Features) GetFwMinor() uint32 {
|
||||||
if m != nil && m.FwMinor != nil {
|
if m != nil && m.FwMinor != nil {
|
||||||
return *m.FwMinor
|
return *m.FwMinor
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -464,6 +497,7 @@ func (m *Features) GetFwPatch() uint32 {
|
||||||
if m != nil && m.FwPatch != nil {
|
if m != nil && m.FwPatch != nil {
|
||||||
return *m.FwPatch
|
return *m.FwPatch
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -471,6 +505,7 @@ func (m *Features) GetFwVendor() string {
|
||||||
if m != nil && m.FwVendor != nil {
|
if m != nil && m.FwVendor != nil {
|
||||||
return *m.FwVendor
|
return *m.FwVendor
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -478,6 +513,7 @@ func (m *Features) GetFwVendorKeys() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.FwVendorKeys
|
return m.FwVendorKeys
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -485,6 +521,7 @@ func (m *Features) GetUnfinishedBackup() bool {
|
||||||
if m != nil && m.UnfinishedBackup != nil {
|
if m != nil && m.UnfinishedBackup != nil {
|
||||||
return *m.UnfinishedBackup
|
return *m.UnfinishedBackup
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -492,6 +529,7 @@ func (m *Features) GetNoBackup() bool {
|
||||||
if m != nil && m.NoBackup != nil {
|
if m != nil && m.NoBackup != nil {
|
||||||
return *m.NoBackup
|
return *m.NoBackup
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -577,6 +615,7 @@ func (m *ApplySettings) GetLanguage() string {
|
||||||
if m != nil && m.Language != nil {
|
if m != nil && m.Language != nil {
|
||||||
return *m.Language
|
return *m.Language
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -584,6 +623,7 @@ func (m *ApplySettings) GetLabel() string {
|
||||||
if m != nil && m.Label != nil {
|
if m != nil && m.Label != nil {
|
||||||
return *m.Label
|
return *m.Label
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -591,6 +631,7 @@ func (m *ApplySettings) GetUsePassphrase() bool {
|
||||||
if m != nil && m.UsePassphrase != nil {
|
if m != nil && m.UsePassphrase != nil {
|
||||||
return *m.UsePassphrase
|
return *m.UsePassphrase
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -598,6 +639,7 @@ func (m *ApplySettings) GetHomescreen() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.Homescreen
|
return m.Homescreen
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -605,6 +647,7 @@ func (m *ApplySettings) GetPassphraseSource() ApplySettings_PassphraseSourceType
|
||||||
if m != nil && m.PassphraseSource != nil {
|
if m != nil && m.PassphraseSource != nil {
|
||||||
return *m.PassphraseSource
|
return *m.PassphraseSource
|
||||||
}
|
}
|
||||||
|
|
||||||
return ApplySettings_ASK
|
return ApplySettings_ASK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -612,6 +655,7 @@ func (m *ApplySettings) GetAutoLockDelayMs() uint32 {
|
||||||
if m != nil && m.AutoLockDelayMs != nil {
|
if m != nil && m.AutoLockDelayMs != nil {
|
||||||
return *m.AutoLockDelayMs
|
return *m.AutoLockDelayMs
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -619,6 +663,7 @@ func (m *ApplySettings) GetDisplayRotation() uint32 {
|
||||||
if m != nil && m.DisplayRotation != nil {
|
if m != nil && m.DisplayRotation != nil {
|
||||||
return *m.DisplayRotation
|
return *m.DisplayRotation
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -663,6 +708,7 @@ func (m *ApplyFlags) GetFlags() uint32 {
|
||||||
if m != nil && m.Flags != nil {
|
if m != nil && m.Flags != nil {
|
||||||
return *m.Flags
|
return *m.Flags
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -707,6 +753,7 @@ func (m *ChangePin) GetRemove() bool {
|
||||||
if m != nil && m.Remove != nil {
|
if m != nil && m.Remove != nil {
|
||||||
return *m.Remove
|
return *m.Remove
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -753,6 +800,7 @@ func (m *Ping) GetMessage() string {
|
||||||
if m != nil && m.Message != nil {
|
if m != nil && m.Message != nil {
|
||||||
return *m.Message
|
return *m.Message
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -760,6 +808,7 @@ func (m *Ping) GetButtonProtection() bool {
|
||||||
if m != nil && m.ButtonProtection != nil {
|
if m != nil && m.ButtonProtection != nil {
|
||||||
return *m.ButtonProtection
|
return *m.ButtonProtection
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -767,6 +816,7 @@ func (m *Ping) GetPinProtection() bool {
|
||||||
if m != nil && m.PinProtection != nil {
|
if m != nil && m.PinProtection != nil {
|
||||||
return *m.PinProtection
|
return *m.PinProtection
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -774,6 +824,7 @@ func (m *Ping) GetPassphraseProtection() bool {
|
||||||
if m != nil && m.PassphraseProtection != nil {
|
if m != nil && m.PassphraseProtection != nil {
|
||||||
return *m.PassphraseProtection
|
return *m.PassphraseProtection
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -853,6 +904,7 @@ func (m *GetEntropy) GetSize() uint32 {
|
||||||
if m != nil && m.Size != nil {
|
if m != nil && m.Size != nil {
|
||||||
return *m.Size
|
return *m.Size
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -895,6 +947,7 @@ func (m *Entropy) GetEntropy() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.Entropy
|
return m.Entropy
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -984,6 +1037,7 @@ func (m *LoadDevice) GetMnemonic() string {
|
||||||
if m != nil && m.Mnemonic != nil {
|
if m != nil && m.Mnemonic != nil {
|
||||||
return *m.Mnemonic
|
return *m.Mnemonic
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -991,6 +1045,7 @@ func (m *LoadDevice) GetNode() *HDNodeType {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.Node
|
return m.Node
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -998,6 +1053,7 @@ func (m *LoadDevice) GetPin() string {
|
||||||
if m != nil && m.Pin != nil {
|
if m != nil && m.Pin != nil {
|
||||||
return *m.Pin
|
return *m.Pin
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1005,6 +1061,7 @@ func (m *LoadDevice) GetPassphraseProtection() bool {
|
||||||
if m != nil && m.PassphraseProtection != nil {
|
if m != nil && m.PassphraseProtection != nil {
|
||||||
return *m.PassphraseProtection
|
return *m.PassphraseProtection
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1012,6 +1069,7 @@ func (m *LoadDevice) GetLanguage() string {
|
||||||
if m != nil && m.Language != nil {
|
if m != nil && m.Language != nil {
|
||||||
return *m.Language
|
return *m.Language
|
||||||
}
|
}
|
||||||
|
|
||||||
return Default_LoadDevice_Language
|
return Default_LoadDevice_Language
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1019,6 +1077,7 @@ func (m *LoadDevice) GetLabel() string {
|
||||||
if m != nil && m.Label != nil {
|
if m != nil && m.Label != nil {
|
||||||
return *m.Label
|
return *m.Label
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1026,6 +1085,7 @@ func (m *LoadDevice) GetSkipChecksum() bool {
|
||||||
if m != nil && m.SkipChecksum != nil {
|
if m != nil && m.SkipChecksum != nil {
|
||||||
return *m.SkipChecksum
|
return *m.SkipChecksum
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1033,6 +1093,7 @@ func (m *LoadDevice) GetU2FCounter() uint32 {
|
||||||
if m != nil && m.U2FCounter != nil {
|
if m != nil && m.U2FCounter != nil {
|
||||||
return *m.U2FCounter
|
return *m.U2FCounter
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1088,6 +1149,7 @@ func (m *ResetDevice) GetDisplayRandom() bool {
|
||||||
if m != nil && m.DisplayRandom != nil {
|
if m != nil && m.DisplayRandom != nil {
|
||||||
return *m.DisplayRandom
|
return *m.DisplayRandom
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1095,6 +1157,7 @@ func (m *ResetDevice) GetStrength() uint32 {
|
||||||
if m != nil && m.Strength != nil {
|
if m != nil && m.Strength != nil {
|
||||||
return *m.Strength
|
return *m.Strength
|
||||||
}
|
}
|
||||||
|
|
||||||
return Default_ResetDevice_Strength
|
return Default_ResetDevice_Strength
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1102,6 +1165,7 @@ func (m *ResetDevice) GetPassphraseProtection() bool {
|
||||||
if m != nil && m.PassphraseProtection != nil {
|
if m != nil && m.PassphraseProtection != nil {
|
||||||
return *m.PassphraseProtection
|
return *m.PassphraseProtection
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1109,6 +1173,7 @@ func (m *ResetDevice) GetPinProtection() bool {
|
||||||
if m != nil && m.PinProtection != nil {
|
if m != nil && m.PinProtection != nil {
|
||||||
return *m.PinProtection
|
return *m.PinProtection
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1116,6 +1181,7 @@ func (m *ResetDevice) GetLanguage() string {
|
||||||
if m != nil && m.Language != nil {
|
if m != nil && m.Language != nil {
|
||||||
return *m.Language
|
return *m.Language
|
||||||
}
|
}
|
||||||
|
|
||||||
return Default_ResetDevice_Language
|
return Default_ResetDevice_Language
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1123,6 +1189,7 @@ func (m *ResetDevice) GetLabel() string {
|
||||||
if m != nil && m.Label != nil {
|
if m != nil && m.Label != nil {
|
||||||
return *m.Label
|
return *m.Label
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1130,6 +1197,7 @@ func (m *ResetDevice) GetU2FCounter() uint32 {
|
||||||
if m != nil && m.U2FCounter != nil {
|
if m != nil && m.U2FCounter != nil {
|
||||||
return *m.U2FCounter
|
return *m.U2FCounter
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1137,6 +1205,7 @@ func (m *ResetDevice) GetSkipBackup() bool {
|
||||||
if m != nil && m.SkipBackup != nil {
|
if m != nil && m.SkipBackup != nil {
|
||||||
return *m.SkipBackup
|
return *m.SkipBackup
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1144,6 +1213,7 @@ func (m *ResetDevice) GetNoBackup() bool {
|
||||||
if m != nil && m.NoBackup != nil {
|
if m != nil && m.NoBackup != nil {
|
||||||
return *m.NoBackup
|
return *m.NoBackup
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1255,6 +1325,7 @@ func (m *EntropyAck) GetEntropy() []byte {
|
||||||
if m != nil {
|
if m != nil {
|
||||||
return m.Entropy
|
return m.Entropy
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1310,6 +1381,7 @@ func (m *RecoveryDevice) GetWordCount() uint32 {
|
||||||
if m != nil && m.WordCount != nil {
|
if m != nil && m.WordCount != nil {
|
||||||
return *m.WordCount
|
return *m.WordCount
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1317,6 +1389,7 @@ func (m *RecoveryDevice) GetPassphraseProtection() bool {
|
||||||
if m != nil && m.PassphraseProtection != nil {
|
if m != nil && m.PassphraseProtection != nil {
|
||||||
return *m.PassphraseProtection
|
return *m.PassphraseProtection
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1324,6 +1397,7 @@ func (m *RecoveryDevice) GetPinProtection() bool {
|
||||||
if m != nil && m.PinProtection != nil {
|
if m != nil && m.PinProtection != nil {
|
||||||
return *m.PinProtection
|
return *m.PinProtection
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1331,6 +1405,7 @@ func (m *RecoveryDevice) GetLanguage() string {
|
||||||
if m != nil && m.Language != nil {
|
if m != nil && m.Language != nil {
|
||||||
return *m.Language
|
return *m.Language
|
||||||
}
|
}
|
||||||
|
|
||||||
return Default_RecoveryDevice_Language
|
return Default_RecoveryDevice_Language
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1338,6 +1413,7 @@ func (m *RecoveryDevice) GetLabel() string {
|
||||||
if m != nil && m.Label != nil {
|
if m != nil && m.Label != nil {
|
||||||
return *m.Label
|
return *m.Label
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1345,6 +1421,7 @@ func (m *RecoveryDevice) GetEnforceWordlist() bool {
|
||||||
if m != nil && m.EnforceWordlist != nil {
|
if m != nil && m.EnforceWordlist != nil {
|
||||||
return *m.EnforceWordlist
|
return *m.EnforceWordlist
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1352,6 +1429,7 @@ func (m *RecoveryDevice) GetType() RecoveryDevice_RecoveryDeviceType {
|
||||||
if m != nil && m.Type != nil {
|
if m != nil && m.Type != nil {
|
||||||
return *m.Type
|
return *m.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
return RecoveryDevice_RecoveryDeviceType_ScrambledWords
|
return RecoveryDevice_RecoveryDeviceType_ScrambledWords
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1359,6 +1437,7 @@ func (m *RecoveryDevice) GetU2FCounter() uint32 {
|
||||||
if m != nil && m.U2FCounter != nil {
|
if m != nil && m.U2FCounter != nil {
|
||||||
return *m.U2FCounter
|
return *m.U2FCounter
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1366,6 +1445,7 @@ func (m *RecoveryDevice) GetDryRun() bool {
|
||||||
if m != nil && m.DryRun != nil {
|
if m != nil && m.DryRun != nil {
|
||||||
return *m.DryRun
|
return *m.DryRun
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1409,6 +1489,7 @@ func (m *WordRequest) GetType() WordRequest_WordRequestType {
|
||||||
if m != nil && m.Type != nil {
|
if m != nil && m.Type != nil {
|
||||||
return *m.Type
|
return *m.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
return WordRequest_WordRequestType_Plain
|
return WordRequest_WordRequestType_Plain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1453,6 +1534,7 @@ func (m *WordAck) GetWord() string {
|
||||||
if m != nil && m.Word != nil {
|
if m != nil && m.Word != nil {
|
||||||
return *m.Word
|
return *m.Word
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1496,6 +1578,7 @@ func (m *SetU2FCounter) GetU2FCounter() uint32 {
|
||||||
if m != nil && m.U2FCounter != nil {
|
if m != nil && m.U2FCounter != nil {
|
||||||
return *m.U2FCounter
|
return *m.U2FCounter
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -636,6 +636,7 @@ var MessageType_value = map[string]int32{
|
||||||
func (x MessageType) Enum() *MessageType {
|
func (x MessageType) Enum() *MessageType {
|
||||||
p := new(MessageType)
|
p := new(MessageType)
|
||||||
*p = x
|
*p = x
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -648,7 +649,9 @@ func (x *MessageType) UnmarshalJSON(data []byte) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
*x = MessageType(value)
|
*x = MessageType(value)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,5 +66,6 @@ func Name(kind uint16) string {
|
||||||
if len(name) < 12 {
|
if len(name) < 12 {
|
||||||
return name
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
return name[12:]
|
return name[12:]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,7 @@ func (w *wallet) Status() (string, error) {
|
||||||
if w.device == nil {
|
if w.device == nil {
|
||||||
return "Closed", failure
|
return "Closed", failure
|
||||||
}
|
}
|
||||||
|
|
||||||
return status, failure
|
return status, failure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,6 +153,7 @@ func (w *wallet) Open(passphrase string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
w.device = device
|
w.device = device
|
||||||
w.commsLock = make(chan struct{}, 1)
|
w.commsLock = make(chan struct{}, 1)
|
||||||
w.commsLock <- struct{}{} // Enable lock
|
w.commsLock <- struct{}{} // Enable lock
|
||||||
|
|
@ -187,6 +189,7 @@ func (w *wallet) heartbeat() {
|
||||||
errc chan error
|
errc chan error
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
for errc == nil && err == nil {
|
for errc == nil && err == nil {
|
||||||
// Wait until termination is requested or the heartbeat cycle arrives
|
// Wait until termination is requested or the heartbeat cycle arrives
|
||||||
select {
|
select {
|
||||||
|
|
@ -203,6 +206,7 @@ func (w *wallet) heartbeat() {
|
||||||
w.stateLock.RUnlock()
|
w.stateLock.RUnlock()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
<-w.commsLock // Don't lock state while resolving version
|
<-w.commsLock // Don't lock state while resolving version
|
||||||
err = w.driver.Heartbeat()
|
err = w.driver.Heartbeat()
|
||||||
w.commsLock <- struct{}{}
|
w.commsLock <- struct{}{}
|
||||||
|
|
@ -233,6 +237,7 @@ func (w *wallet) Close() error {
|
||||||
|
|
||||||
// Terminate the health checks
|
// Terminate the health checks
|
||||||
var herr error
|
var herr error
|
||||||
|
|
||||||
if hQuit != nil {
|
if hQuit != nil {
|
||||||
errc := make(chan error)
|
errc := make(chan error)
|
||||||
hQuit <- errc
|
hQuit <- errc
|
||||||
|
|
@ -240,6 +245,7 @@ func (w *wallet) Close() error {
|
||||||
}
|
}
|
||||||
// Terminate the self-derivations
|
// Terminate the self-derivations
|
||||||
var derr error
|
var derr error
|
||||||
|
|
||||||
if dQuit != nil {
|
if dQuit != nil {
|
||||||
errc := make(chan error)
|
errc := make(chan error)
|
||||||
dQuit <- errc
|
dQuit <- errc
|
||||||
|
|
@ -256,9 +262,11 @@ func (w *wallet) Close() error {
|
||||||
if err := w.close(); err != nil {
|
if err := w.close(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if herr != nil {
|
if herr != nil {
|
||||||
return herr
|
return herr
|
||||||
}
|
}
|
||||||
|
|
||||||
return derr
|
return derr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -276,6 +284,7 @@ func (w *wallet) close() error {
|
||||||
w.device = nil
|
w.device = nil
|
||||||
|
|
||||||
w.accounts, w.paths = nil, nil
|
w.accounts, w.paths = nil, nil
|
||||||
|
|
||||||
return w.driver.Close()
|
return w.driver.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -298,6 +307,7 @@ func (w *wallet) Accounts() []accounts.Account {
|
||||||
|
|
||||||
cpy := make([]accounts.Account, len(w.accounts))
|
cpy := make([]accounts.Account, len(w.accounts))
|
||||||
copy(cpy, w.accounts)
|
copy(cpy, w.accounts)
|
||||||
|
|
||||||
return cpy
|
return cpy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -313,6 +323,7 @@ func (w *wallet) selfDerive() {
|
||||||
errc chan error
|
errc chan error
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
for errc == nil && err == nil {
|
for errc == nil && err == nil {
|
||||||
// Wait until either derivation or termination is requested
|
// Wait until either derivation or termination is requested
|
||||||
select {
|
select {
|
||||||
|
|
@ -327,6 +338,7 @@ func (w *wallet) selfDerive() {
|
||||||
if w.device == nil || w.deriveChain == nil {
|
if w.device == nil || w.deriveChain == nil {
|
||||||
w.stateLock.RUnlock()
|
w.stateLock.RUnlock()
|
||||||
reqc <- struct{}{}
|
reqc <- struct{}{}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
|
|
@ -334,6 +346,7 @@ func (w *wallet) selfDerive() {
|
||||||
default:
|
default:
|
||||||
w.stateLock.RUnlock()
|
w.stateLock.RUnlock()
|
||||||
reqc <- struct{}{}
|
reqc <- struct{}{}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Device lock obtained, derive the next batch of accounts
|
// Device lock obtained, derive the next batch of accounts
|
||||||
|
|
@ -346,6 +359,7 @@ func (w *wallet) selfDerive() {
|
||||||
|
|
||||||
context = context.Background()
|
context = context.Background()
|
||||||
)
|
)
|
||||||
|
|
||||||
for i := 0; i < len(nextAddrs); i++ {
|
for i := 0; i < len(nextAddrs); i++ {
|
||||||
for empty := false; !empty; {
|
for empty := false; !empty; {
|
||||||
// Retrieve the next derived Ethereum account
|
// Retrieve the next derived Ethereum account
|
||||||
|
|
@ -360,11 +374,13 @@ func (w *wallet) selfDerive() {
|
||||||
balance *big.Int
|
balance *big.Int
|
||||||
nonce uint64
|
nonce uint64
|
||||||
)
|
)
|
||||||
|
|
||||||
balance, err = w.deriveChain.BalanceAt(context, nextAddrs[i], nil)
|
balance, err = w.deriveChain.BalanceAt(context, nextAddrs[i], nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.log.Warn("USB wallet balance retrieval failed", "err", err)
|
w.log.Warn("USB wallet balance retrieval failed", "err", err)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
nonce, err = w.deriveChain.NonceAt(context, nextAddrs[i], nil)
|
nonce, err = w.deriveChain.NonceAt(context, nextAddrs[i], nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.log.Warn("USB wallet nonce retrieval failed", "err", err)
|
w.log.Warn("USB wallet nonce retrieval failed", "err", err)
|
||||||
|
|
@ -374,17 +390,19 @@ func (w *wallet) selfDerive() {
|
||||||
// unless the account was empty.
|
// unless the account was empty.
|
||||||
path := make(accounts.DerivationPath, len(nextPaths[i]))
|
path := make(accounts.DerivationPath, len(nextPaths[i]))
|
||||||
copy(path[:], nextPaths[i][:])
|
copy(path[:], nextPaths[i][:])
|
||||||
|
|
||||||
if balance.Sign() == 0 && nonce == 0 {
|
if balance.Sign() == 0 && nonce == 0 {
|
||||||
empty = true
|
empty = true
|
||||||
// If it indeed was empty, make a log output for it anyway. In the case
|
// If it indeed was empty, make a log output for it anyway. In the case
|
||||||
// of legacy-ledger, the first account on the legacy-path will
|
// of legacy-ledger, the first account on the legacy-path will
|
||||||
// be shown to the user, even if we don't actively track it
|
// be shown to the user, even if we don't actively track it
|
||||||
if i < len(nextAddrs)-1 {
|
if i < len(nextAddrs)-1 {
|
||||||
w.log.Info("Skipping trakcking first account on legacy path, use personal.deriveAccount(<url>,<path>, false) to track",
|
w.log.Info("Skipping tracking first account on legacy path, use personal.deriveAccount(<url>,<path>, false) to track",
|
||||||
"path", path, "address", nextAddrs[i])
|
"path", path, "address", nextAddrs[i])
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
paths = append(paths, path)
|
paths = append(paths, path)
|
||||||
account := accounts.Account{
|
account := accounts.Account{
|
||||||
Address: nextAddrs[i],
|
Address: nextAddrs[i],
|
||||||
|
|
@ -423,6 +441,7 @@ func (w *wallet) selfDerive() {
|
||||||
|
|
||||||
// Notify the user of termination and loop after a bit of time (to avoid trashing)
|
// Notify the user of termination and loop after a bit of time (to avoid trashing)
|
||||||
reqc <- struct{}{}
|
reqc <- struct{}{}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
select {
|
select {
|
||||||
case errc = <-w.deriveQuit:
|
case errc = <-w.deriveQuit:
|
||||||
|
|
@ -448,6 +467,7 @@ func (w *wallet) Contains(account accounts.Account) bool {
|
||||||
defer w.stateLock.RUnlock()
|
defer w.stateLock.RUnlock()
|
||||||
|
|
||||||
_, exists := w.paths[account.Address]
|
_, exists := w.paths[account.Address]
|
||||||
|
|
||||||
return exists
|
return exists
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -462,6 +482,7 @@ func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
|
||||||
w.stateLock.RUnlock()
|
w.stateLock.RUnlock()
|
||||||
return accounts.Account{}, accounts.ErrWalletClosed
|
return accounts.Account{}, accounts.ErrWalletClosed
|
||||||
}
|
}
|
||||||
|
|
||||||
<-w.commsLock // Avoid concurrent hardware access
|
<-w.commsLock // Avoid concurrent hardware access
|
||||||
address, err := w.driver.Derive(path)
|
address, err := w.driver.Derive(path)
|
||||||
w.commsLock <- struct{}{}
|
w.commsLock <- struct{}{}
|
||||||
|
|
@ -472,6 +493,7 @@ func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return accounts.Account{}, err
|
return accounts.Account{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
account := accounts.Account{
|
account := accounts.Account{
|
||||||
Address: address,
|
Address: address,
|
||||||
URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)},
|
URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)},
|
||||||
|
|
@ -488,6 +510,7 @@ func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
|
||||||
w.paths[address] = make(accounts.DerivationPath, len(path))
|
w.paths[address] = make(accounts.DerivationPath, len(path))
|
||||||
copy(w.paths[address], path)
|
copy(w.paths[address], path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return account, nil
|
return account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -514,6 +537,7 @@ func (w *wallet) SelfDerive(bases []accounts.DerivationPath, chain ethereum.Chai
|
||||||
w.deriveNextPaths[i] = make(accounts.DerivationPath, len(base))
|
w.deriveNextPaths[i] = make(accounts.DerivationPath, len(base))
|
||||||
copy(w.deriveNextPaths[i][:], base[:])
|
copy(w.deriveNextPaths[i][:], base[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
w.deriveNextAddrs = make([]common.Address, len(bases))
|
w.deriveNextAddrs = make([]common.Address, len(bases))
|
||||||
w.deriveChain = chain
|
w.deriveChain = chain
|
||||||
}
|
}
|
||||||
|
|
@ -526,7 +550,6 @@ func (w *wallet) signHash(account accounts.Account, hash []byte) ([]byte, error)
|
||||||
|
|
||||||
// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed
|
// SignData signs keccak256(data). The mimetype parameter describes the type of data being signed
|
||||||
func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
|
func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
|
||||||
|
|
||||||
// Unless we are doing 712 signing, simply dispatch to signHash
|
// Unless we are doing 712 signing, simply dispatch to signHash
|
||||||
if !(mimeType == accounts.MimetypeTypedData && len(data) == 66 && data[0] == 0x19 && data[1] == 0x01) {
|
if !(mimeType == accounts.MimetypeTypedData && len(data) == 66 && data[0] == 0x19 && data[1] == 0x01) {
|
||||||
return w.signHash(account, crypto.Keccak256(data))
|
return w.signHash(account, crypto.Keccak256(data))
|
||||||
|
|
@ -547,6 +570,7 @@ func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte
|
||||||
}
|
}
|
||||||
// All infos gathered and metadata checks out, request signing
|
// All infos gathered and metadata checks out, request signing
|
||||||
<-w.commsLock
|
<-w.commsLock
|
||||||
|
|
||||||
defer func() { w.commsLock <- struct{}{} }()
|
defer func() { w.commsLock <- struct{}{} }()
|
||||||
|
|
||||||
// Ensure the device isn't screwed with while user confirmation is pending
|
// Ensure the device isn't screwed with while user confirmation is pending
|
||||||
|
|
@ -565,6 +589,7 @@ func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return signature, nil
|
return signature, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -601,6 +626,7 @@ func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID
|
||||||
}
|
}
|
||||||
// All infos gathered and metadata checks out, request signing
|
// All infos gathered and metadata checks out, request signing
|
||||||
<-w.commsLock
|
<-w.commsLock
|
||||||
|
|
||||||
defer func() { w.commsLock <- struct{}{} }()
|
defer func() { w.commsLock <- struct{}{} }()
|
||||||
|
|
||||||
// Ensure the device isn't screwed with while user confirmation is pending
|
// Ensure the device isn't screwed with while user confirmation is pending
|
||||||
|
|
@ -619,9 +645,11 @@ func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if sender != account.Address {
|
if sender != account.Address {
|
||||||
return nil, fmt.Errorf("signer mismatch: expected %s, got %s", account.Address.Hex(), sender.Hex())
|
return nil, fmt.Errorf("signer mismatch: expected %s, got %s", account.Address.Hex(), sender.Hex())
|
||||||
}
|
}
|
||||||
|
|
||||||
return signed, nil
|
return signed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ for:
|
||||||
- go run build/ci.go lint
|
- go run build/ci.go lint
|
||||||
- go run build/ci.go install -dlgo
|
- go run build/ci.go install -dlgo
|
||||||
test_script:
|
test_script:
|
||||||
- go run build/ci.go test -dlgo -coverage
|
- go run build/ci.go test -dlgo
|
||||||
|
|
||||||
# linux/386 is disabled.
|
# linux/386 is disabled.
|
||||||
- matrix:
|
- matrix:
|
||||||
|
|
@ -54,4 +54,4 @@ for:
|
||||||
- go run build/ci.go archive -arch %GETH_ARCH% -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
- go run build/ci.go archive -arch %GETH_ARCH% -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
||||||
- go run build/ci.go nsis -arch %GETH_ARCH% -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
- go run build/ci.go nsis -arch %GETH_ARCH% -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
||||||
test_script:
|
test_script:
|
||||||
- go run build/ci.go test -dlgo -arch %GETH_ARCH% -cc %GETH_CC% -coverage
|
- go run build/ci.go test -dlgo -arch %GETH_ARCH% -cc %GETH_CC%
|
||||||
|
|
|
||||||
89
beacon/engine/errors.go
Normal file
89
beacon/engine/errors.go
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
// Copyright 2022 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EngineAPIError is a standardized error message between consensus and execution
|
||||||
|
// clients, also containing any custom error message Geth might include.
|
||||||
|
type EngineAPIError struct {
|
||||||
|
code int
|
||||||
|
msg string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EngineAPIError) ErrorCode() int { return e.code }
|
||||||
|
func (e *EngineAPIError) Error() string { return e.msg }
|
||||||
|
func (e *EngineAPIError) ErrorData() interface{} {
|
||||||
|
if e.err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return struct {
|
||||||
|
Error string `json:"err"`
|
||||||
|
}{e.err.Error()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// With returns a copy of the error with a new embedded custom data field.
|
||||||
|
func (e *EngineAPIError) With(err error) *EngineAPIError {
|
||||||
|
return &EngineAPIError{
|
||||||
|
code: e.code,
|
||||||
|
msg: e.msg,
|
||||||
|
err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ rpc.Error = new(EngineAPIError)
|
||||||
|
_ rpc.DataError = new(EngineAPIError)
|
||||||
|
)
|
||||||
|
|
||||||
|
// nolint : errname
|
||||||
|
var (
|
||||||
|
// VALID is returned by the engine API in the following calls:
|
||||||
|
// - newPayloadV1: if the payload was already known or was just validated and executed
|
||||||
|
// - forkchoiceUpdateV1: if the chain accepted the reorg (might ignore if it's stale)
|
||||||
|
VALID = "VALID"
|
||||||
|
|
||||||
|
// INVALID is returned by the engine API in the following calls:
|
||||||
|
// - newPayloadV1: if the payload failed to execute on top of the local chain
|
||||||
|
// - forkchoiceUpdateV1: if the new head is unknown, pre-merge, or reorg to it fails
|
||||||
|
INVALID = "INVALID"
|
||||||
|
|
||||||
|
// SYNCING is returned by the engine API in the following calls:
|
||||||
|
// - newPayloadV1: if the payload was accepted on top of an active sync
|
||||||
|
// - forkchoiceUpdateV1: if the new head was seen before, but not part of the chain
|
||||||
|
SYNCING = "SYNCING"
|
||||||
|
|
||||||
|
// ACCEPTED is returned by the engine API in the following calls:
|
||||||
|
// - newPayloadV1: if the payload was accepted, but not processed (side chain)
|
||||||
|
ACCEPTED = "ACCEPTED"
|
||||||
|
|
||||||
|
GenericServerError = &EngineAPIError{code: -32000, msg: "Server error"}
|
||||||
|
UnknownPayload = &EngineAPIError{code: -38001, msg: "Unknown payload"}
|
||||||
|
InvalidForkChoiceState = &EngineAPIError{code: -38002, msg: "Invalid forkchoice state"}
|
||||||
|
InvalidPayloadAttributes = &EngineAPIError{code: -38003, msg: "Invalid payload attributes"}
|
||||||
|
TooLargeRequest = &EngineAPIError{code: -38004, msg: "Too large request"}
|
||||||
|
InvalidParams = &EngineAPIError{code: -32602, msg: "Invalid parameters"}
|
||||||
|
|
||||||
|
STATUS_INVALID = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: INVALID}, PayloadID: nil}
|
||||||
|
STATUS_SYNCING = ForkChoiceResponse{PayloadStatus: PayloadStatusV1{Status: SYNCING}, PayloadID: nil}
|
||||||
|
INVALID_TERMINAL_BLOCK = PayloadStatusV1{Status: INVALID, LatestValidHash: &common.Hash{}}
|
||||||
|
)
|
||||||
70
beacon/engine/gen_blockparams.go
Normal file
70
beacon/engine/gen_blockparams.go
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = (*payloadAttributesMarshaling)(nil)
|
||||||
|
|
||||||
|
// MarshalJSON marshals as JSON.
|
||||||
|
func (p PayloadAttributes) MarshalJSON() ([]byte, error) {
|
||||||
|
type PayloadAttributes struct {
|
||||||
|
Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||||
|
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||||
|
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
|
||||||
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var enc PayloadAttributes
|
||||||
|
enc.Timestamp = hexutil.Uint64(p.Timestamp)
|
||||||
|
enc.Random = p.Random
|
||||||
|
enc.SuggestedFeeRecipient = p.SuggestedFeeRecipient
|
||||||
|
enc.Withdrawals = p.Withdrawals
|
||||||
|
|
||||||
|
return json.Marshal(&enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
|
func (p *PayloadAttributes) UnmarshalJSON(input []byte) error {
|
||||||
|
type PayloadAttributes struct {
|
||||||
|
Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||||
|
Random *common.Hash `json:"prevRandao" gencodec:"required"`
|
||||||
|
SuggestedFeeRecipient *common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
|
||||||
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var dec PayloadAttributes
|
||||||
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if dec.Timestamp == nil {
|
||||||
|
return errors.New("missing required field 'timestamp' for PayloadAttributes")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Timestamp = uint64(*dec.Timestamp)
|
||||||
|
|
||||||
|
if dec.Random == nil {
|
||||||
|
return errors.New("missing required field 'prevRandao' for PayloadAttributes")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Random = *dec.Random
|
||||||
|
|
||||||
|
if dec.SuggestedFeeRecipient == nil {
|
||||||
|
return errors.New("missing required field 'suggestedFeeRecipient' for PayloadAttributes")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.SuggestedFeeRecipient = *dec.SuggestedFeeRecipient
|
||||||
|
if dec.Withdrawals != nil {
|
||||||
|
p.Withdrawals = dec.Withdrawals
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
181
beacon/engine/gen_ed.go
Normal file
181
beacon/engine/gen_ed.go
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = (*executableDataMarshaling)(nil)
|
||||||
|
|
||||||
|
// MarshalJSON marshals as JSON.
|
||||||
|
func (e ExecutableData) MarshalJSON() ([]byte, error) {
|
||||||
|
type ExecutableData struct {
|
||||||
|
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||||
|
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
|
||||||
|
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
|
||||||
|
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||||
|
LogsBloom hexutil.Bytes `json:"logsBloom" gencodec:"required"`
|
||||||
|
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||||
|
Number hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
|
||||||
|
GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
||||||
|
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||||
|
Timestamp hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||||
|
ExtraData hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||||
|
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
|
||||||
|
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
||||||
|
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
|
||||||
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var enc ExecutableData
|
||||||
|
enc.ParentHash = e.ParentHash
|
||||||
|
enc.FeeRecipient = e.FeeRecipient
|
||||||
|
enc.StateRoot = e.StateRoot
|
||||||
|
enc.ReceiptsRoot = e.ReceiptsRoot
|
||||||
|
enc.LogsBloom = e.LogsBloom
|
||||||
|
enc.Random = e.Random
|
||||||
|
enc.Number = hexutil.Uint64(e.Number)
|
||||||
|
enc.GasLimit = hexutil.Uint64(e.GasLimit)
|
||||||
|
enc.GasUsed = hexutil.Uint64(e.GasUsed)
|
||||||
|
enc.Timestamp = hexutil.Uint64(e.Timestamp)
|
||||||
|
enc.ExtraData = e.ExtraData
|
||||||
|
enc.BaseFeePerGas = (*hexutil.Big)(e.BaseFeePerGas)
|
||||||
|
enc.BlockHash = e.BlockHash
|
||||||
|
|
||||||
|
if e.Transactions != nil {
|
||||||
|
enc.Transactions = make([]hexutil.Bytes, len(e.Transactions))
|
||||||
|
for k, v := range e.Transactions {
|
||||||
|
enc.Transactions[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enc.Withdrawals = e.Withdrawals
|
||||||
|
|
||||||
|
return json.Marshal(&enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
|
func (e *ExecutableData) UnmarshalJSON(input []byte) error {
|
||||||
|
type ExecutableData struct {
|
||||||
|
ParentHash *common.Hash `json:"parentHash" gencodec:"required"`
|
||||||
|
FeeRecipient *common.Address `json:"feeRecipient" gencodec:"required"`
|
||||||
|
StateRoot *common.Hash `json:"stateRoot" gencodec:"required"`
|
||||||
|
ReceiptsRoot *common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||||
|
LogsBloom *hexutil.Bytes `json:"logsBloom" gencodec:"required"`
|
||||||
|
Random *common.Hash `json:"prevRandao" gencodec:"required"`
|
||||||
|
Number *hexutil.Uint64 `json:"blockNumber" gencodec:"required"`
|
||||||
|
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"`
|
||||||
|
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"`
|
||||||
|
Timestamp *hexutil.Uint64 `json:"timestamp" gencodec:"required"`
|
||||||
|
ExtraData *hexutil.Bytes `json:"extraData" gencodec:"required"`
|
||||||
|
BaseFeePerGas *hexutil.Big `json:"baseFeePerGas" gencodec:"required"`
|
||||||
|
BlockHash *common.Hash `json:"blockHash" gencodec:"required"`
|
||||||
|
Transactions []hexutil.Bytes `json:"transactions" gencodec:"required"`
|
||||||
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var dec ExecutableData
|
||||||
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if dec.ParentHash == nil {
|
||||||
|
return errors.New("missing required field 'parentHash' for ExecutableData")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.ParentHash = *dec.ParentHash
|
||||||
|
|
||||||
|
if dec.FeeRecipient == nil {
|
||||||
|
return errors.New("missing required field 'feeRecipient' for ExecutableData")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.FeeRecipient = *dec.FeeRecipient
|
||||||
|
|
||||||
|
if dec.StateRoot == nil {
|
||||||
|
return errors.New("missing required field 'stateRoot' for ExecutableData")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.StateRoot = *dec.StateRoot
|
||||||
|
|
||||||
|
if dec.ReceiptsRoot == nil {
|
||||||
|
return errors.New("missing required field 'receiptsRoot' for ExecutableData")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.ReceiptsRoot = *dec.ReceiptsRoot
|
||||||
|
|
||||||
|
if dec.LogsBloom == nil {
|
||||||
|
return errors.New("missing required field 'logsBloom' for ExecutableData")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.LogsBloom = *dec.LogsBloom
|
||||||
|
|
||||||
|
if dec.Random == nil {
|
||||||
|
return errors.New("missing required field 'prevRandao' for ExecutableData")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Random = *dec.Random
|
||||||
|
|
||||||
|
if dec.Number == nil {
|
||||||
|
return errors.New("missing required field 'blockNumber' for ExecutableData")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Number = uint64(*dec.Number)
|
||||||
|
|
||||||
|
if dec.GasLimit == nil {
|
||||||
|
return errors.New("missing required field 'gasLimit' for ExecutableData")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.GasLimit = uint64(*dec.GasLimit)
|
||||||
|
|
||||||
|
if dec.GasUsed == nil {
|
||||||
|
return errors.New("missing required field 'gasUsed' for ExecutableData")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.GasUsed = uint64(*dec.GasUsed)
|
||||||
|
|
||||||
|
if dec.Timestamp == nil {
|
||||||
|
return errors.New("missing required field 'timestamp' for ExecutableData")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Timestamp = uint64(*dec.Timestamp)
|
||||||
|
|
||||||
|
if dec.ExtraData == nil {
|
||||||
|
return errors.New("missing required field 'extraData' for ExecutableData")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.ExtraData = *dec.ExtraData
|
||||||
|
|
||||||
|
if dec.BaseFeePerGas == nil {
|
||||||
|
return errors.New("missing required field 'baseFeePerGas' for ExecutableData")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.BaseFeePerGas = (*big.Int)(dec.BaseFeePerGas)
|
||||||
|
|
||||||
|
if dec.BlockHash == nil {
|
||||||
|
return errors.New("missing required field 'blockHash' for ExecutableData")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.BlockHash = *dec.BlockHash
|
||||||
|
|
||||||
|
if dec.Transactions == nil {
|
||||||
|
return errors.New("missing required field 'transactions' for ExecutableData")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Transactions = make([][]byte, len(dec.Transactions))
|
||||||
|
for k, v := range dec.Transactions {
|
||||||
|
e.Transactions[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
if dec.Withdrawals != nil {
|
||||||
|
e.Withdrawals = dec.Withdrawals
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
54
beacon/engine/gen_epe.go
Normal file
54
beacon/engine/gen_epe.go
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = (*executionPayloadEnvelopeMarshaling)(nil)
|
||||||
|
|
||||||
|
// MarshalJSON marshals as JSON.
|
||||||
|
func (e ExecutionPayloadEnvelope) MarshalJSON() ([]byte, error) {
|
||||||
|
type ExecutionPayloadEnvelope struct {
|
||||||
|
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
|
||||||
|
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var enc ExecutionPayloadEnvelope
|
||||||
|
enc.ExecutionPayload = e.ExecutionPayload
|
||||||
|
enc.BlockValue = (*hexutil.Big)(e.BlockValue)
|
||||||
|
|
||||||
|
return json.Marshal(&enc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
|
func (e *ExecutionPayloadEnvelope) UnmarshalJSON(input []byte) error {
|
||||||
|
type ExecutionPayloadEnvelope struct {
|
||||||
|
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
|
||||||
|
BlockValue *hexutil.Big `json:"blockValue" gencodec:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var dec ExecutionPayloadEnvelope
|
||||||
|
if err := json.Unmarshal(input, &dec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if dec.ExecutionPayload == nil {
|
||||||
|
return errors.New("missing required field 'executionPayload' for ExecutionPayloadEnvelope")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.ExecutionPayload = dec.ExecutionPayload
|
||||||
|
|
||||||
|
if dec.BlockValue == nil {
|
||||||
|
return errors.New("missing required field 'blockValue' for ExecutionPayloadEnvelope")
|
||||||
|
}
|
||||||
|
|
||||||
|
e.BlockValue = (*big.Int)(dec.BlockValue)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
249
beacon/engine/types.go
Normal file
249
beacon/engine/types.go
Normal file
|
|
@ -0,0 +1,249 @@
|
||||||
|
// Copyright 2022 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package engine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run github.com/fjl/gencodec -type PayloadAttributes -field-override payloadAttributesMarshaling -out gen_blockparams.go
|
||||||
|
|
||||||
|
// PayloadAttributes describes the environment context in which a block should
|
||||||
|
// be built.
|
||||||
|
type PayloadAttributes struct {
|
||||||
|
Timestamp uint64 `json:"timestamp" gencodec:"required"`
|
||||||
|
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||||
|
SuggestedFeeRecipient common.Address `json:"suggestedFeeRecipient" gencodec:"required"`
|
||||||
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON type overrides for PayloadAttributes.
|
||||||
|
type payloadAttributesMarshaling struct {
|
||||||
|
Timestamp hexutil.Uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:generate go run github.com/fjl/gencodec -type ExecutableData -field-override executableDataMarshaling -out gen_ed.go
|
||||||
|
|
||||||
|
// ExecutableData is the data necessary to execute an EL payload.
|
||||||
|
type ExecutableData struct {
|
||||||
|
ParentHash common.Hash `json:"parentHash" gencodec:"required"`
|
||||||
|
FeeRecipient common.Address `json:"feeRecipient" gencodec:"required"`
|
||||||
|
StateRoot common.Hash `json:"stateRoot" gencodec:"required"`
|
||||||
|
ReceiptsRoot common.Hash `json:"receiptsRoot" gencodec:"required"`
|
||||||
|
LogsBloom []byte `json:"logsBloom" gencodec:"required"`
|
||||||
|
Random common.Hash `json:"prevRandao" gencodec:"required"`
|
||||||
|
Number uint64 `json:"blockNumber" gencodec:"required"`
|
||||||
|
GasLimit uint64 `json:"gasLimit" gencodec:"required"`
|
||||||
|
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
|
||||||
|
Timestamp uint64 `json:"timestamp" gencodec:"required"`
|
||||||
|
ExtraData []byte `json:"extraData" gencodec:"required"`
|
||||||
|
BaseFeePerGas *big.Int `json:"baseFeePerGas" gencodec:"required"`
|
||||||
|
BlockHash common.Hash `json:"blockHash" gencodec:"required"`
|
||||||
|
Transactions [][]byte `json:"transactions" gencodec:"required"`
|
||||||
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON type overrides for executableData.
|
||||||
|
type executableDataMarshaling struct {
|
||||||
|
Number hexutil.Uint64
|
||||||
|
GasLimit hexutil.Uint64
|
||||||
|
GasUsed hexutil.Uint64
|
||||||
|
Timestamp hexutil.Uint64
|
||||||
|
BaseFeePerGas *hexutil.Big
|
||||||
|
ExtraData hexutil.Bytes
|
||||||
|
LogsBloom hexutil.Bytes
|
||||||
|
Transactions []hexutil.Bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:generate go run github.com/fjl/gencodec -type ExecutionPayloadEnvelope -field-override executionPayloadEnvelopeMarshaling -out gen_epe.go
|
||||||
|
|
||||||
|
type ExecutionPayloadEnvelope struct {
|
||||||
|
ExecutionPayload *ExecutableData `json:"executionPayload" gencodec:"required"`
|
||||||
|
BlockValue *big.Int `json:"blockValue" gencodec:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON type overrides for ExecutionPayloadEnvelope.
|
||||||
|
type executionPayloadEnvelopeMarshaling struct {
|
||||||
|
BlockValue *hexutil.Big
|
||||||
|
}
|
||||||
|
|
||||||
|
type PayloadStatusV1 struct {
|
||||||
|
Status string `json:"status"`
|
||||||
|
LatestValidHash *common.Hash `json:"latestValidHash"`
|
||||||
|
ValidationError *string `json:"validationError"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TransitionConfigurationV1 struct {
|
||||||
|
TerminalTotalDifficulty *hexutil.Big `json:"terminalTotalDifficulty"`
|
||||||
|
TerminalBlockHash common.Hash `json:"terminalBlockHash"`
|
||||||
|
TerminalBlockNumber hexutil.Uint64 `json:"terminalBlockNumber"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PayloadID is an identifier of the payload build process
|
||||||
|
type PayloadID [8]byte
|
||||||
|
|
||||||
|
func (b PayloadID) String() string {
|
||||||
|
return hexutil.Encode(b[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b PayloadID) MarshalText() ([]byte, error) {
|
||||||
|
return hexutil.Bytes(b[:]).MarshalText()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *PayloadID) UnmarshalText(input []byte) error {
|
||||||
|
err := hexutil.UnmarshalFixedText("PayloadID", input, b[:])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid payload id %q: %w", input, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ForkChoiceResponse struct {
|
||||||
|
PayloadStatus PayloadStatusV1 `json:"payloadStatus"`
|
||||||
|
PayloadID *PayloadID `json:"payloadId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ForkchoiceStateV1 struct {
|
||||||
|
HeadBlockHash common.Hash `json:"headBlockHash"`
|
||||||
|
SafeBlockHash common.Hash `json:"safeBlockHash"`
|
||||||
|
FinalizedBlockHash common.Hash `json:"finalizedBlockHash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeTransactions(txs []*types.Transaction) [][]byte {
|
||||||
|
var enc = make([][]byte, len(txs))
|
||||||
|
for i, tx := range txs {
|
||||||
|
enc[i], _ = tx.MarshalBinary()
|
||||||
|
}
|
||||||
|
|
||||||
|
return enc
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeTransactions(enc [][]byte) ([]*types.Transaction, error) {
|
||||||
|
var txs = make([]*types.Transaction, len(enc))
|
||||||
|
|
||||||
|
for i, encTx := range enc {
|
||||||
|
var tx types.Transaction
|
||||||
|
if err := tx.UnmarshalBinary(encTx); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid transaction %d: %v", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
txs[i] = &tx
|
||||||
|
}
|
||||||
|
|
||||||
|
return txs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecutableDataToBlock constructs a block from executable data.
|
||||||
|
// It verifies that the following fields:
|
||||||
|
//
|
||||||
|
// len(extraData) <= 32
|
||||||
|
// uncleHash = emptyUncleHash
|
||||||
|
// difficulty = 0
|
||||||
|
//
|
||||||
|
// and that the blockhash of the constructed block matches the parameters. Nil
|
||||||
|
// Withdrawals value will propagate through the returned block. Empty
|
||||||
|
// Withdrawals value must be passed via non-nil, length 0 value in params.
|
||||||
|
func ExecutableDataToBlock(params ExecutableData) (*types.Block, error) {
|
||||||
|
txs, err := decodeTransactions(params.Transactions)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(params.ExtraData) > 32 {
|
||||||
|
return nil, fmt.Errorf("invalid extradata length: %v", len(params.ExtraData))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(params.LogsBloom) != 256 {
|
||||||
|
return nil, fmt.Errorf("invalid logsBloom length: %v", len(params.LogsBloom))
|
||||||
|
}
|
||||||
|
// Check that baseFeePerGas is not negative or too big
|
||||||
|
if params.BaseFeePerGas != nil && (params.BaseFeePerGas.Sign() == -1 || params.BaseFeePerGas.BitLen() > 256) {
|
||||||
|
return nil, fmt.Errorf("invalid baseFeePerGas: %v", params.BaseFeePerGas)
|
||||||
|
}
|
||||||
|
// Only set withdrawalsRoot if it is non-nil. This allows CLs to use
|
||||||
|
// ExecutableData before withdrawals are enabled by marshaling
|
||||||
|
// Withdrawals as the json null value.
|
||||||
|
var withdrawalsRoot *common.Hash
|
||||||
|
|
||||||
|
if params.Withdrawals != nil {
|
||||||
|
h := types.DeriveSha(types.Withdrawals(params.Withdrawals), trie.NewStackTrie(nil))
|
||||||
|
withdrawalsRoot = &h
|
||||||
|
}
|
||||||
|
|
||||||
|
header := &types.Header{
|
||||||
|
ParentHash: params.ParentHash,
|
||||||
|
UncleHash: types.EmptyUncleHash,
|
||||||
|
Coinbase: params.FeeRecipient,
|
||||||
|
Root: params.StateRoot,
|
||||||
|
TxHash: types.DeriveSha(types.Transactions(txs), trie.NewStackTrie(nil)),
|
||||||
|
ReceiptHash: params.ReceiptsRoot,
|
||||||
|
Bloom: types.BytesToBloom(params.LogsBloom),
|
||||||
|
Difficulty: common.Big0,
|
||||||
|
Number: new(big.Int).SetUint64(params.Number),
|
||||||
|
GasLimit: params.GasLimit,
|
||||||
|
GasUsed: params.GasUsed,
|
||||||
|
Time: params.Timestamp,
|
||||||
|
BaseFee: params.BaseFeePerGas,
|
||||||
|
Extra: params.ExtraData,
|
||||||
|
MixDigest: params.Random,
|
||||||
|
WithdrawalsHash: withdrawalsRoot,
|
||||||
|
}
|
||||||
|
|
||||||
|
block := types.NewBlockWithHeader(header).WithBody(txs, nil /* uncles */).WithWithdrawals(params.Withdrawals)
|
||||||
|
if block.Hash() != params.BlockHash {
|
||||||
|
return nil, fmt.Errorf("blockhash mismatch, want %x, got %x", params.BlockHash, block.Hash())
|
||||||
|
}
|
||||||
|
|
||||||
|
return block, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockToExecutableData constructs the ExecutableData structure by filling the
|
||||||
|
// fields from the given block. It assumes the given block is post-merge block.
|
||||||
|
func BlockToExecutableData(block *types.Block, fees *big.Int) *ExecutionPayloadEnvelope {
|
||||||
|
data := &ExecutableData{
|
||||||
|
BlockHash: block.Hash(),
|
||||||
|
ParentHash: block.ParentHash(),
|
||||||
|
FeeRecipient: block.Coinbase(),
|
||||||
|
StateRoot: block.Root(),
|
||||||
|
Number: block.NumberU64(),
|
||||||
|
GasLimit: block.GasLimit(),
|
||||||
|
GasUsed: block.GasUsed(),
|
||||||
|
BaseFeePerGas: block.BaseFee(),
|
||||||
|
Timestamp: block.Time(),
|
||||||
|
ReceiptsRoot: block.ReceiptHash(),
|
||||||
|
LogsBloom: block.Bloom().Bytes(),
|
||||||
|
Transactions: encodeTransactions(block.Transactions()),
|
||||||
|
Random: block.MixDigest(),
|
||||||
|
ExtraData: block.Extra(),
|
||||||
|
Withdrawals: block.Withdrawals(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ExecutionPayloadEnvelope{ExecutionPayload: data, BlockValue: fees}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecutionPayloadBodyV1 is used in the response to GetPayloadBodiesByHashV1 and GetPayloadBodiesByRangeV1
|
||||||
|
type ExecutionPayloadBodyV1 struct {
|
||||||
|
TransactionData []hexutil.Bytes `json:"transactions"`
|
||||||
|
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||||
|
}
|
||||||
22
build/bot/macos-build.sh
Normal file
22
build/bot/macos-build.sh
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e -x
|
||||||
|
|
||||||
|
# -- Check XCode version
|
||||||
|
xcodebuild -version
|
||||||
|
# xcrun simctl list
|
||||||
|
|
||||||
|
# -- Build for macOS and upload to Azure
|
||||||
|
go run build/ci.go install -dlgo
|
||||||
|
go run build/ci.go archive -type tar # -signer OSX_SIGNING_KEY -upload gethstore/builds
|
||||||
|
|
||||||
|
# # -- CocoaPods
|
||||||
|
# gem uninstall cocoapods -a -x
|
||||||
|
# gem install cocoapods
|
||||||
|
# mv ~/.cocoapods/repos/master ~/.cocoapods/repos/master.bak
|
||||||
|
# sed -i '.bak' 's/repo.join/!repo.join/g' $(dirname `gem which cocoapods`)/cocoapods/sources_manager.rb
|
||||||
|
# git clone --depth=1 https://github.com/CocoaPods/Specs.git ~/.cocoapods/repos/master
|
||||||
|
# pod setup --verbose
|
||||||
|
|
||||||
|
# # -- Build for iOS and upload to Azure
|
||||||
|
# go run build/ci.go xcode -signer IOS_SIGNING_KEY -upload gethstore/builds
|
||||||
17
build/bot/ppa-build.sh
Normal file
17
build/bot/ppa-build.sh
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e -x
|
||||||
|
|
||||||
|
# Note: this script is meant to be run in a Debian/Ubuntu docker container,
|
||||||
|
# as user 'root'.
|
||||||
|
|
||||||
|
# Install the required tools for creating source packages.
|
||||||
|
apt-get -yq --no-install-suggests --no-install-recommends install\
|
||||||
|
devscripts debhelper dput fakeroot
|
||||||
|
|
||||||
|
# Add the SSH key of ppa.launchpad.net to known_hosts.
|
||||||
|
mkdir -p ~/.ssh
|
||||||
|
echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts
|
||||||
|
|
||||||
|
# Build the source package and upload.
|
||||||
|
go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"
|
||||||
318
build/ci.go
318
build/ci.go
|
|
@ -31,8 +31,6 @@ Available commands are:
|
||||||
importkeys -- imports signing keys from env
|
importkeys -- imports signing keys from env
|
||||||
debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package
|
debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package
|
||||||
nsis -- creates a Windows NSIS installer
|
nsis -- creates a Windows NSIS installer
|
||||||
aar [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an Android archive
|
|
||||||
xcode [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an iOS XCode framework
|
|
||||||
purge [ -store blobstore ] [ -days threshold ] -- purges old archives from the blobstore
|
purge [ -store blobstore ] [ -days threshold ] -- purges old archives from the blobstore
|
||||||
|
|
||||||
For all commands, -n prevents execution of external programs (dry run mode).
|
For all commands, -n prevents execution of external programs (dry run mode).
|
||||||
|
|
@ -40,18 +38,15 @@ For all commands, -n prevents execution of external programs (dry run mode).
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -78,7 +73,6 @@ var (
|
||||||
executablePath("bootnode"),
|
executablePath("bootnode"),
|
||||||
executablePath("evm"),
|
executablePath("evm"),
|
||||||
executablePath("geth"),
|
executablePath("geth"),
|
||||||
executablePath("puppeth"),
|
|
||||||
executablePath("rlpdump"),
|
executablePath("rlpdump"),
|
||||||
executablePath("clef"),
|
executablePath("clef"),
|
||||||
}
|
}
|
||||||
|
|
@ -101,10 +95,6 @@ var (
|
||||||
BinaryName: "geth",
|
BinaryName: "geth",
|
||||||
Description: "Ethereum CLI client.",
|
Description: "Ethereum CLI client.",
|
||||||
},
|
},
|
||||||
{
|
|
||||||
BinaryName: "puppeth",
|
|
||||||
Description: "Ethereum private network manager.",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
BinaryName: "rlpdump",
|
BinaryName: "rlpdump",
|
||||||
Description: "Developer utility tool that prints RLP structures.",
|
Description: "Developer utility tool that prints RLP structures.",
|
||||||
|
|
@ -130,14 +120,15 @@ var (
|
||||||
// Distros for which packages are created.
|
// Distros for which packages are created.
|
||||||
// Note: vivid is unsupported because there is no golang-1.6 package for it.
|
// Note: vivid is unsupported because there is no golang-1.6 package for it.
|
||||||
// Note: the following Ubuntu releases have been officially deprecated on Launchpad:
|
// Note: the following Ubuntu releases have been officially deprecated on Launchpad:
|
||||||
// wily, yakkety, zesty, artful, cosmic, disco, eoan, groovy, hirsuite
|
// wily, yakkety, zesty, artful, cosmic, disco, eoan, groovy, hirsuite, impish
|
||||||
debDistroGoBoots = map[string]string{
|
debDistroGoBoots = map[string]string{
|
||||||
"trusty": "golang-1.11", // EOL: 04/2024
|
"trusty": "golang-1.11", // EOL: 04/2024
|
||||||
"xenial": "golang-go", // EOL: 04/2026
|
"xenial": "golang-go", // EOL: 04/2026
|
||||||
"bionic": "golang-go", // EOL: 04/2028
|
"bionic": "golang-go", // EOL: 04/2028
|
||||||
"focal": "golang-go", // EOL: 04/2030
|
"focal": "golang-go", // EOL: 04/2030
|
||||||
"impish": "golang-go", // EOL: 07/2022
|
"jammy": "golang-go", // EOL: 04/2032
|
||||||
// "jammy": "golang-go", // EOL: 04/2027
|
"kinetic": "golang-go", // EOL: 07/2023
|
||||||
|
"lunar": "golang-go", // EOL: 01/2024
|
||||||
}
|
}
|
||||||
|
|
||||||
debGoBootPaths = map[string]string{
|
debGoBootPaths = map[string]string{
|
||||||
|
|
@ -145,7 +136,7 @@ var (
|
||||||
"golang-go": "/usr/lib/go",
|
"golang-go": "/usr/lib/go",
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the version of go that will be downloaded by
|
// This is the version of Go that will be downloaded by
|
||||||
//
|
//
|
||||||
// go run ci.go install -dlgo
|
// go run ci.go install -dlgo
|
||||||
dlgoVersion = "1.20"
|
dlgoVersion = "1.20"
|
||||||
|
|
@ -163,7 +154,7 @@ func executablePath(name string) string {
|
||||||
func main() {
|
func main() {
|
||||||
log.SetFlags(log.Lshortfile)
|
log.SetFlags(log.Lshortfile)
|
||||||
|
|
||||||
if _, err := os.Stat(filepath.Join("build", "ci.go")); os.IsNotExist(err) {
|
if !common.FileExist(filepath.Join("build", "ci.go")) {
|
||||||
log.Fatal("this script must be run from the root of the repository")
|
log.Fatal("this script must be run from the root of the repository")
|
||||||
}
|
}
|
||||||
if len(os.Args) < 2 {
|
if len(os.Args) < 2 {
|
||||||
|
|
@ -184,10 +175,6 @@ func main() {
|
||||||
doDebianSource(os.Args[2:])
|
doDebianSource(os.Args[2:])
|
||||||
case "nsis":
|
case "nsis":
|
||||||
doWindowsInstaller(os.Args[2:])
|
doWindowsInstaller(os.Args[2:])
|
||||||
case "aar":
|
|
||||||
doAndroidArchive(os.Args[2:])
|
|
||||||
case "xcode":
|
|
||||||
doXCodeFramework(os.Args[2:])
|
|
||||||
case "purge":
|
case "purge":
|
||||||
doPurge(os.Args[2:])
|
doPurge(os.Args[2:])
|
||||||
default:
|
default:
|
||||||
|
|
@ -202,6 +189,7 @@ func doInstall(cmdline []string) {
|
||||||
dlgo = flag.Bool("dlgo", false, "Download Go and build with it")
|
dlgo = flag.Bool("dlgo", false, "Download Go and build with it")
|
||||||
arch = flag.String("arch", "", "Architecture to cross build for")
|
arch = flag.String("arch", "", "Architecture to cross build for")
|
||||||
cc = flag.String("cc", "", "C compiler to cross build with")
|
cc = flag.String("cc", "", "C compiler to cross build with")
|
||||||
|
staticlink = flag.Bool("static", false, "Create statically-linked executable")
|
||||||
)
|
)
|
||||||
flag.CommandLine.Parse(cmdline)
|
flag.CommandLine.Parse(cmdline)
|
||||||
|
|
||||||
|
|
@ -212,9 +200,12 @@ func doInstall(cmdline []string) {
|
||||||
tc.Root = build.DownloadGo(csdb, dlgoVersion)
|
tc.Root = build.DownloadGo(csdb, dlgoVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disable CLI markdown doc generation in release builds.
|
||||||
|
buildTags := []string{"urfave_cli_no_docs"}
|
||||||
|
|
||||||
// Configure the build.
|
// Configure the build.
|
||||||
env := build.Env()
|
env := build.Env()
|
||||||
gobuild := tc.Go("build", buildFlags(env)...)
|
gobuild := tc.Go("build", buildFlags(env, *staticlink, buildTags)...)
|
||||||
|
|
||||||
// arm64 CI builders are memory-constrained and can't handle concurrent builds,
|
// arm64 CI builders are memory-constrained and can't handle concurrent builds,
|
||||||
// better disable it. This check isn't the best, it should probably
|
// better disable it. This check isn't the best, it should probably
|
||||||
|
|
@ -247,25 +238,35 @@ func doInstall(cmdline []string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildFlags returns the go tool flags for building.
|
// buildFlags returns the go tool flags for building.
|
||||||
func buildFlags(env build.Environment) (flags []string) {
|
func buildFlags(env build.Environment, staticLinking bool, buildTags []string) (flags []string) {
|
||||||
var ld []string
|
var ld []string
|
||||||
if env.Commit != "" {
|
if env.Commit != "" {
|
||||||
ld = append(ld, "-X", "main.gitCommit="+env.Commit)
|
ld = append(ld, "-X", "github.com/maticnetwork/bor/internal/version.gitCommit="+env.Commit)
|
||||||
ld = append(ld, "-X", "main.gitDate="+env.Date)
|
ld = append(ld, "-X", "github.com/maticnetwork/bor/internal/version.gitDate="+env.Date)
|
||||||
}
|
}
|
||||||
// Strip DWARF on darwin. This used to be required for certain things,
|
// Strip DWARF on darwin. This used to be required for certain things,
|
||||||
// and there is no downside to this, so we just keep doing it.
|
// and there is no downside to this, so we just keep doing it.
|
||||||
if runtime.GOOS == "darwin" {
|
if runtime.GOOS == "darwin" {
|
||||||
ld = append(ld, "-s")
|
ld = append(ld, "-s")
|
||||||
}
|
}
|
||||||
|
if runtime.GOOS == "linux" {
|
||||||
// Enforce the stacksize to 8M, which is the case on most platforms apart from
|
// Enforce the stacksize to 8M, which is the case on most platforms apart from
|
||||||
// alpine Linux.
|
// alpine Linux.
|
||||||
if runtime.GOOS == "linux" {
|
extld := []string{"-Wl,-z,stack-size=0x800000"}
|
||||||
ld = append(ld, "-extldflags", "-Wl,-z,stack-size=0x800000")
|
if staticLinking {
|
||||||
|
extld = append(extld, "-static")
|
||||||
|
// Under static linking, use of certain glibc features must be
|
||||||
|
// disabled to avoid shared library dependencies.
|
||||||
|
buildTags = append(buildTags, "osusergo", "netgo")
|
||||||
|
}
|
||||||
|
ld = append(ld, "-extldflags", "'"+strings.Join(extld, " ")+"'")
|
||||||
}
|
}
|
||||||
if len(ld) > 0 {
|
if len(ld) > 0 {
|
||||||
flags = append(flags, "-ldflags", strings.Join(ld, " "))
|
flags = append(flags, "-ldflags", strings.Join(ld, " "))
|
||||||
}
|
}
|
||||||
|
if len(buildTags) > 0 {
|
||||||
|
flags = append(flags, "-tags", strings.Join(buildTags, ","))
|
||||||
|
}
|
||||||
return flags
|
return flags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -456,10 +457,6 @@ func maybeSkipArchive(env build.Environment) {
|
||||||
log.Printf("skipping archive creation because this is a PR build")
|
log.Printf("skipping archive creation because this is a PR build")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
if env.IsCronJob {
|
|
||||||
log.Printf("skipping archive creation because this is a cron job")
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
if env.Branch != "master" && !strings.HasPrefix(env.Tag, "v1.") {
|
if env.Branch != "master" && !strings.HasPrefix(env.Tag, "v1.") {
|
||||||
log.Printf("skipping archive creation because branch %q, tag %q is not on the inclusion list", env.Branch, env.Tag)
|
log.Printf("skipping archive creation because branch %q, tag %q is not on the inclusion list", env.Branch, env.Tag)
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
|
|
@ -654,10 +651,10 @@ 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 packages.
|
||||||
// Download and verify the Go source package.
|
var (
|
||||||
gobundle := downloadGoSources(*cachedir)
|
gobundle = downloadGoSources(*cachedir)
|
||||||
|
)
|
||||||
// Download all the dependencies needed to build the sources and run the ci script
|
// Download all the dependencies needed to build the sources and run the ci script
|
||||||
srcdepfetch := tc.Go("mod", "download")
|
srcdepfetch := tc.Go("mod", "download")
|
||||||
srcdepfetch.Env = append(srcdepfetch.Env, "GOPATH="+filepath.Join(*workdir, "modgopath"))
|
srcdepfetch.Env = append(srcdepfetch.Env, "GOPATH="+filepath.Join(*workdir, "modgopath"))
|
||||||
|
|
@ -680,12 +677,12 @@ func doDebianSource(cmdline []string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Go source code
|
// Add builder Go source code
|
||||||
if err := build.ExtractArchive(gobundle, canonicalPath); err != nil {
|
if err := build.ExtractArchive(gobundle, canonicalPath); err != nil {
|
||||||
log.Fatalf("Failed to extract Go sources: %v", err)
|
log.Fatalf("Failed to extract builder Go sources: %v", err)
|
||||||
}
|
}
|
||||||
if err := os.Rename(filepath.Join(canonicalPath, "go"), filepath.Join(canonicalPath, ".go")); err != nil {
|
if err := os.Rename(filepath.Join(canonicalPath, "go"), filepath.Join(canonicalPath, ".go")); err != nil {
|
||||||
log.Fatalf("Failed to rename Go source folder: %v", err)
|
log.Fatalf("Failed to rename builder Go source folder: %v", err)
|
||||||
}
|
}
|
||||||
// Add all dependency modules in compressed form
|
// Add all dependency modules in compressed form
|
||||||
os.MkdirAll(filepath.Join(canonicalPath, ".mod", "cache"), 0755)
|
os.MkdirAll(filepath.Join(canonicalPath, ".mod", "cache"), 0755)
|
||||||
|
|
@ -739,8 +736,8 @@ func ppaUpload(workdir, ppa, sshUser string, files []string) {
|
||||||
var idfile string
|
var idfile string
|
||||||
if sshkey := getenvBase64("PPA_SSH_KEY"); len(sshkey) > 0 {
|
if sshkey := getenvBase64("PPA_SSH_KEY"); len(sshkey) > 0 {
|
||||||
idfile = filepath.Join(workdir, "sshkey")
|
idfile = filepath.Join(workdir, "sshkey")
|
||||||
if _, err := os.Stat(idfile); os.IsNotExist(err) {
|
if !common.FileExist(idfile) {
|
||||||
ioutil.WriteFile(idfile, sshkey, 0600)
|
os.WriteFile(idfile, sshkey, 0600)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Upload
|
// Upload
|
||||||
|
|
@ -763,7 +760,7 @@ func makeWorkdir(wdflag string) string {
|
||||||
if wdflag != "" {
|
if wdflag != "" {
|
||||||
err = os.MkdirAll(wdflag, 0744)
|
err = os.MkdirAll(wdflag, 0744)
|
||||||
} else {
|
} else {
|
||||||
wdflag, err = ioutil.TempDir("", "geth-build-")
|
wdflag, err = os.MkdirTemp("", "geth-build-")
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
@ -961,10 +958,10 @@ func doWindowsInstaller(cmdline []string) {
|
||||||
build.Render("build/nsis.pathupdate.nsh", filepath.Join(*workdir, "PathUpdate.nsh"), 0644, nil)
|
build.Render("build/nsis.pathupdate.nsh", filepath.Join(*workdir, "PathUpdate.nsh"), 0644, nil)
|
||||||
build.Render("build/nsis.envvarupdate.nsh", filepath.Join(*workdir, "EnvVarUpdate.nsh"), 0644, nil)
|
build.Render("build/nsis.envvarupdate.nsh", filepath.Join(*workdir, "EnvVarUpdate.nsh"), 0644, nil)
|
||||||
if err := cp.CopyFile(filepath.Join(*workdir, "SimpleFC.dll"), "build/nsis.simplefc.dll"); err != nil {
|
if err := cp.CopyFile(filepath.Join(*workdir, "SimpleFC.dll"), "build/nsis.simplefc.dll"); err != nil {
|
||||||
log.Fatal("Failed to copy SimpleFC.dll: %v", err)
|
log.Fatalf("Failed to copy SimpleFC.dll: %v", err)
|
||||||
}
|
}
|
||||||
if err := cp.CopyFile(filepath.Join(*workdir, "COPYING"), "COPYING"); err != nil {
|
if err := cp.CopyFile(filepath.Join(*workdir, "COPYING"), "COPYING"); err != nil {
|
||||||
log.Fatal("Failed to copy copyright note: %v", err)
|
log.Fatalf("Failed to copy copyright note: %v", err)
|
||||||
}
|
}
|
||||||
// Build the installer. This assumes that all the needed files have been previously
|
// Build the installer. This assumes that all the needed files have been previously
|
||||||
// built (don't mix building and packaging to keep cross compilation complexity to a
|
// built (don't mix building and packaging to keep cross compilation complexity to a
|
||||||
|
|
@ -973,7 +970,10 @@ func doWindowsInstaller(cmdline []string) {
|
||||||
if env.Commit != "" {
|
if env.Commit != "" {
|
||||||
version[2] += "-" + env.Commit[:8]
|
version[2] += "-" + env.Commit[:8]
|
||||||
}
|
}
|
||||||
installer, _ := filepath.Abs("geth-" + archiveBasename(*arch, params.ArchiveVersion(env.Commit)) + ".exe")
|
installer, err := filepath.Abs("geth-" + archiveBasename(*arch, params.ArchiveVersion(env.Commit)) + ".exe")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to convert installer file path: %v", err)
|
||||||
|
}
|
||||||
build.MustRunCommand("makensis.exe",
|
build.MustRunCommand("makensis.exe",
|
||||||
"/DOUTPUTFILE="+installer,
|
"/DOUTPUTFILE="+installer,
|
||||||
"/DMAJORVERSION="+version[0],
|
"/DMAJORVERSION="+version[0],
|
||||||
|
|
@ -988,240 +988,6 @@ func doWindowsInstaller(cmdline []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Android archives
|
|
||||||
|
|
||||||
func doAndroidArchive(cmdline []string) {
|
|
||||||
var (
|
|
||||||
local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`)
|
|
||||||
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. ANDROID_SIGNING_KEY)`)
|
|
||||||
signify = flag.String("signify", "", `Environment variable holding the signify signing key (e.g. ANDROID_SIGNIFY_KEY)`)
|
|
||||||
deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "https://oss.sonatype.org")`)
|
|
||||||
upload = flag.String("upload", "", `Destination to upload the archive (usually "gethstore/builds")`)
|
|
||||||
)
|
|
||||||
flag.CommandLine.Parse(cmdline)
|
|
||||||
env := build.Env()
|
|
||||||
tc := new(build.GoToolchain)
|
|
||||||
|
|
||||||
// Sanity check that the SDK and NDK are installed and set
|
|
||||||
if os.Getenv("ANDROID_HOME") == "" {
|
|
||||||
log.Fatal("Please ensure ANDROID_HOME points to your Android SDK")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build gomobile.
|
|
||||||
install := tc.Install(GOBIN, "golang.org/x/mobile/cmd/gomobile@latest", "golang.org/x/mobile/cmd/gobind@latest")
|
|
||||||
install.Env = append(install.Env)
|
|
||||||
build.MustRun(install)
|
|
||||||
|
|
||||||
// Ensure all dependencies are available. This is required to make
|
|
||||||
// gomobile bind work because it expects go.sum to contain all checksums.
|
|
||||||
build.MustRun(tc.Go("mod", "download"))
|
|
||||||
|
|
||||||
// Build the Android archive and Maven resources
|
|
||||||
build.MustRun(gomobileTool("bind", "-ldflags", "-s -w", "--target", "android", "--javapkg", "org.ethereum", "-v", "github.com/ethereum/go-ethereum/mobile"))
|
|
||||||
|
|
||||||
if *local {
|
|
||||||
// If we're building locally, copy bundle to build dir and skip Maven
|
|
||||||
os.Rename("geth.aar", filepath.Join(GOBIN, "geth.aar"))
|
|
||||||
os.Rename("geth-sources.jar", filepath.Join(GOBIN, "geth-sources.jar"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
meta := newMavenMetadata(env)
|
|
||||||
build.Render("build/mvn.pom", meta.Package+".pom", 0755, meta)
|
|
||||||
|
|
||||||
// Skip Maven deploy and Azure upload for PR builds
|
|
||||||
maybeSkipArchive(env)
|
|
||||||
|
|
||||||
// Sign and upload the archive to Azure
|
|
||||||
archive := "geth-" + archiveBasename("android", params.ArchiveVersion(env.Commit)) + ".aar"
|
|
||||||
os.Rename("geth.aar", archive)
|
|
||||||
|
|
||||||
if err := archiveUpload(archive, *upload, *signer, *signify); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
// Sign and upload all the artifacts to Maven Central
|
|
||||||
os.Rename(archive, meta.Package+".aar")
|
|
||||||
if *signer != "" && *deploy != "" {
|
|
||||||
// Import the signing key into the local GPG instance
|
|
||||||
key := getenvBase64(*signer)
|
|
||||||
gpg := exec.Command("gpg", "--import")
|
|
||||||
gpg.Stdin = bytes.NewReader(key)
|
|
||||||
build.MustRun(gpg)
|
|
||||||
keyID, err := build.PGPKeyID(string(key))
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
// Upload the artifacts to Sonatype and/or Maven Central
|
|
||||||
repo := *deploy + "/service/local/staging/deploy/maven2"
|
|
||||||
if meta.Develop {
|
|
||||||
repo = *deploy + "/content/repositories/snapshots"
|
|
||||||
}
|
|
||||||
build.MustRunCommand("mvn", "gpg:sign-and-deploy-file", "-e", "-X",
|
|
||||||
"-settings=build/mvn.settings", "-Durl="+repo, "-DrepositoryId=ossrh",
|
|
||||||
"-Dgpg.keyname="+keyID,
|
|
||||||
"-DpomFile="+meta.Package+".pom", "-Dfile="+meta.Package+".aar")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func gomobileTool(subcmd string, args ...string) *exec.Cmd {
|
|
||||||
cmd := exec.Command(filepath.Join(GOBIN, "gomobile"), subcmd)
|
|
||||||
cmd.Args = append(cmd.Args, args...)
|
|
||||||
cmd.Env = []string{
|
|
||||||
"PATH=" + GOBIN + string(os.PathListSeparator) + os.Getenv("PATH"),
|
|
||||||
}
|
|
||||||
for _, e := range os.Environ() {
|
|
||||||
if strings.HasPrefix(e, "GOPATH=") || strings.HasPrefix(e, "PATH=") || strings.HasPrefix(e, "GOBIN=") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cmd.Env = append(cmd.Env, e)
|
|
||||||
}
|
|
||||||
cmd.Env = append(cmd.Env, "GOBIN="+GOBIN)
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
type mavenMetadata struct {
|
|
||||||
Version string
|
|
||||||
Package string
|
|
||||||
Develop bool
|
|
||||||
Contributors []mavenContributor
|
|
||||||
}
|
|
||||||
|
|
||||||
type mavenContributor struct {
|
|
||||||
Name string
|
|
||||||
Email string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMavenMetadata(env build.Environment) mavenMetadata {
|
|
||||||
// Collect the list of authors from the repo root
|
|
||||||
contribs := []mavenContributor{}
|
|
||||||
if authors, err := os.Open("AUTHORS"); err == nil {
|
|
||||||
defer authors.Close()
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(authors)
|
|
||||||
for scanner.Scan() {
|
|
||||||
// Skip any whitespace from the authors list
|
|
||||||
line := strings.TrimSpace(scanner.Text())
|
|
||||||
if line == "" || line[0] == '#' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Split the author and insert as a contributor
|
|
||||||
re := regexp.MustCompile("([^<]+) <(.+)>")
|
|
||||||
parts := re.FindStringSubmatch(line)
|
|
||||||
if len(parts) == 3 {
|
|
||||||
contribs = append(contribs, mavenContributor{Name: parts[1], Email: parts[2]})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Render the version and package strings
|
|
||||||
version := params.Version
|
|
||||||
if isUnstableBuild(env) {
|
|
||||||
version += "-SNAPSHOT"
|
|
||||||
}
|
|
||||||
return mavenMetadata{
|
|
||||||
Version: version,
|
|
||||||
Package: "geth-" + version,
|
|
||||||
Develop: isUnstableBuild(env),
|
|
||||||
Contributors: contribs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// XCode frameworks
|
|
||||||
|
|
||||||
func doXCodeFramework(cmdline []string) {
|
|
||||||
var (
|
|
||||||
local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`)
|
|
||||||
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. IOS_SIGNING_KEY)`)
|
|
||||||
signify = flag.String("signify", "", `Environment variable holding the signify signing key (e.g. IOS_SIGNIFY_KEY)`)
|
|
||||||
deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "trunk")`)
|
|
||||||
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
|
|
||||||
)
|
|
||||||
flag.CommandLine.Parse(cmdline)
|
|
||||||
env := build.Env()
|
|
||||||
tc := new(build.GoToolchain)
|
|
||||||
|
|
||||||
// Build gomobile.
|
|
||||||
build.MustRun(tc.Install(GOBIN, "golang.org/x/mobile/cmd/gomobile@latest", "golang.org/x/mobile/cmd/gobind@latest"))
|
|
||||||
|
|
||||||
// Ensure all dependencies are available. This is required to make
|
|
||||||
// gomobile bind work because it expects go.sum to contain all checksums.
|
|
||||||
build.MustRun(tc.Go("mod", "download"))
|
|
||||||
|
|
||||||
// Build the iOS XCode framework
|
|
||||||
bind := gomobileTool("bind", "-ldflags", "-s -w", "--target", "ios", "-v", "github.com/ethereum/go-ethereum/mobile")
|
|
||||||
|
|
||||||
if *local {
|
|
||||||
// If we're building locally, use the build folder and stop afterwards
|
|
||||||
bind.Dir = GOBIN
|
|
||||||
build.MustRun(bind)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the archive.
|
|
||||||
maybeSkipArchive(env)
|
|
||||||
archive := "geth-" + archiveBasename("ios", params.ArchiveVersion(env.Commit))
|
|
||||||
if err := os.MkdirAll(archive, 0755); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
bind.Dir, _ = filepath.Abs(archive)
|
|
||||||
build.MustRun(bind)
|
|
||||||
build.MustRunCommand("tar", "-zcvf", archive+".tar.gz", archive)
|
|
||||||
|
|
||||||
// Sign and upload the framework to Azure
|
|
||||||
if err := archiveUpload(archive+".tar.gz", *upload, *signer, *signify); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
// Prepare and upload a PodSpec to CocoaPods
|
|
||||||
if *deploy != "" {
|
|
||||||
meta := newPodMetadata(env, archive)
|
|
||||||
build.Render("build/pod.podspec", "Geth.podspec", 0755, meta)
|
|
||||||
build.MustRunCommand("pod", *deploy, "push", "Geth.podspec", "--allow-warnings")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type podMetadata struct {
|
|
||||||
Version string
|
|
||||||
Commit string
|
|
||||||
Archive string
|
|
||||||
Contributors []podContributor
|
|
||||||
}
|
|
||||||
|
|
||||||
type podContributor struct {
|
|
||||||
Name string
|
|
||||||
Email string
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPodMetadata(env build.Environment, archive string) podMetadata {
|
|
||||||
// Collect the list of authors from the repo root
|
|
||||||
contribs := []podContributor{}
|
|
||||||
if authors, err := os.Open("AUTHORS"); err == nil {
|
|
||||||
defer authors.Close()
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(authors)
|
|
||||||
for scanner.Scan() {
|
|
||||||
// Skip any whitespace from the authors list
|
|
||||||
line := strings.TrimSpace(scanner.Text())
|
|
||||||
if line == "" || line[0] == '#' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Split the author and insert as a contributor
|
|
||||||
re := regexp.MustCompile("([^<]+) <(.+)>")
|
|
||||||
parts := re.FindStringSubmatch(line)
|
|
||||||
if len(parts) == 3 {
|
|
||||||
contribs = append(contribs, podContributor{Name: parts[1], Email: parts[2]})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
version := params.Version
|
|
||||||
if isUnstableBuild(env) {
|
|
||||||
version += "-unstable." + env.Buildnum
|
|
||||||
}
|
|
||||||
return podMetadata{
|
|
||||||
Archive: archive,
|
|
||||||
Version: version,
|
|
||||||
Commit: env.Commit,
|
|
||||||
Contributors: contribs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Binary distribution cleanups
|
// Binary distribution cleanups
|
||||||
|
|
||||||
func doPurge(cmdline []string) {
|
func doPurge(cmdline []string) {
|
||||||
|
|
|
||||||
16
build/deb/ethereum/completions/bash/geth
Executable file
16
build/deb/ethereum/completions/bash/geth
Executable file
|
|
@ -0,0 +1,16 @@
|
||||||
|
_geth_bash_autocomplete() {
|
||||||
|
if [[ "${COMP_WORDS[0]}" != "source" ]]; then
|
||||||
|
local cur opts base
|
||||||
|
COMPREPLY=()
|
||||||
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||||
|
if [[ "$cur" == "-"* ]]; then
|
||||||
|
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} ${cur} --generate-bash-completion )
|
||||||
|
else
|
||||||
|
opts=$( ${COMP_WORDS[@]:0:$COMP_CWORD} --generate-bash-completion )
|
||||||
|
fi
|
||||||
|
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -o bashdefault -o default -o nospace -F _geth_bash_autocomplete geth
|
||||||
18
build/deb/ethereum/completions/zsh/_geth
Normal file
18
build/deb/ethereum/completions/zsh/_geth
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
_geth_zsh_autocomplete() {
|
||||||
|
local -a opts
|
||||||
|
local cur
|
||||||
|
cur=${words[-1]}
|
||||||
|
if [[ "$cur" == "-"* ]]; then
|
||||||
|
opts=("${(@f)$(${words[@]:0:#words[@]-1} ${cur} --generate-bash-completion)}")
|
||||||
|
else
|
||||||
|
opts=("${(@f)$(${words[@]:0:#words[@]-1} --generate-bash-completion)}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${opts[1]}" != "" ]]; then
|
||||||
|
_describe 'values' opts
|
||||||
|
else
|
||||||
|
_files
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
compdef _geth_zsh_autocomplete geth
|
||||||
|
|
@ -5,7 +5,7 @@ Maintainer: {{.Author}}
|
||||||
Build-Depends: debhelper (>= 8.0.0), {{.GoBootPackage}}
|
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: https://github.com/ethereum/go-ethereum.git
|
||||||
Vcs-Browser: https://github.com/ethereum/go-ethereum
|
Vcs-Browser: https://github.com/ethereum/go-ethereum
|
||||||
|
|
||||||
Package: {{.Name}}
|
Package: {{.Name}}
|
||||||
|
|
|
||||||
|
|
@ -1 +1,5 @@
|
||||||
build/bin/{{.BinaryName}} usr/bin
|
build/bin/{{.BinaryName}} usr/bin
|
||||||
|
{{- if eq .BinaryName "geth" }}
|
||||||
|
build/deb/ethereum/completions/bash/geth etc/bash_completion.d
|
||||||
|
build/deb/ethereum/completions/zsh/_geth usr/share/zsh/vendor-completions
|
||||||
|
{{end -}}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,11 @@ override_dh_auto_build:
|
||||||
# We can't download a fresh Go within Launchpad, so we're shipping and building
|
# We can't download a fresh Go within Launchpad, so we're shipping and building
|
||||||
# one on the fly. However, we can't build it inside the go-ethereum folder as
|
# one on the fly. However, we can't build it inside the go-ethereum folder as
|
||||||
# bootstrapping clashes with go modules, so build in a sibling folder.
|
# bootstrapping clashes with go modules, so build in a sibling folder.
|
||||||
(mv .go ../ && cd ../.go/src && ./make.bash)
|
#
|
||||||
|
# We're also shipping the bootstrapper as of Go 1.20 as it had minimum version
|
||||||
|
# requirements opposed to older versions of Go.
|
||||||
|
(mv .goboot ../ && cd ../.goboot/src && ./make.bash)
|
||||||
|
(mv .go ../ && cd ../.go/src && GOROOT_BOOTSTRAP=`pwd`/../../.goboot ./make.bash)
|
||||||
|
|
||||||
# We can't download external go modules within Launchpad, so we're shipping the
|
# We can't download external go modules within Launchpad, so we're shipping the
|
||||||
# entire dependency source cache with go-ethereum.
|
# entire dependency source cache with go-ethereum.
|
||||||
|
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
|
||||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<groupId>org.ethereum</groupId>
|
|
||||||
<artifactId>geth</artifactId>
|
|
||||||
<version>{{.Version}}</version>
|
|
||||||
<packaging>aar</packaging>
|
|
||||||
|
|
||||||
<name>Android Ethereum Client</name>
|
|
||||||
<description>Android port of the go-ethereum libraries and node</description>
|
|
||||||
<url>https://github.com/ethereum/go-ethereum</url>
|
|
||||||
<inceptionYear>2015</inceptionYear>
|
|
||||||
|
|
||||||
<licenses>
|
|
||||||
<license>
|
|
||||||
<name>GNU Lesser General Public License, Version 3.0</name>
|
|
||||||
<url>https://www.gnu.org/licenses/lgpl-3.0.en.html</url>
|
|
||||||
<distribution>repo</distribution>
|
|
||||||
</license>
|
|
||||||
</licenses>
|
|
||||||
|
|
||||||
<organization>
|
|
||||||
<name>Ethereum</name>
|
|
||||||
<url>https://ethereum.org</url>
|
|
||||||
</organization>
|
|
||||||
|
|
||||||
<developers>
|
|
||||||
<developer>
|
|
||||||
<id>karalabe</id>
|
|
||||||
<name>Péter Szilágyi</name>
|
|
||||||
<email>peterke@gmail.com</email>
|
|
||||||
<url>https://github.com/karalabe</url>
|
|
||||||
<properties>
|
|
||||||
<picUrl>https://www.gravatar.com/avatar/2ecbf0f5b4b79eebf8c193e5d324357f?s=256</picUrl>
|
|
||||||
</properties>
|
|
||||||
</developer>
|
|
||||||
</developers>
|
|
||||||
|
|
||||||
<contributors>{{range .Contributors}}
|
|
||||||
<contributor>
|
|
||||||
<name>{{.Name}}</name>
|
|
||||||
<email>{{.Email}}</email>
|
|
||||||
</contributor>{{end}}
|
|
||||||
</contributors>
|
|
||||||
|
|
||||||
<issueManagement>
|
|
||||||
<system>GitHub Issues</system>
|
|
||||||
<url>https://github.com/ethereum/go-ethereum/issues/</url>
|
|
||||||
</issueManagement>
|
|
||||||
|
|
||||||
<scm>
|
|
||||||
<url>https://github.com/ethereum/go-ethereum</url>
|
|
||||||
</scm>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
|
|
||||||
http://maven.apache.org/xsd/settings-1.0.0.xsd">
|
|
||||||
<servers>
|
|
||||||
<server>
|
|
||||||
<id>ossrh</id>
|
|
||||||
<username>${env.ANDROID_SONATYPE_USERNAME}</username>
|
|
||||||
<password>${env.ANDROID_SONATYPE_PASSWORD}</password>
|
|
||||||
</server>
|
|
||||||
</servers>
|
|
||||||
<profiles>
|
|
||||||
<profile>
|
|
||||||
<id>ossrh</id>
|
|
||||||
<activation>
|
|
||||||
<activeByDefault>true</activeByDefault>
|
|
||||||
</activation>
|
|
||||||
<properties>
|
|
||||||
<gpg.executable>gpg</gpg.executable>
|
|
||||||
<gpg.passphrase></gpg.passphrase>
|
|
||||||
</properties>
|
|
||||||
</profile>
|
|
||||||
</profiles>
|
|
||||||
</settings>
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
||||||
Pod::Spec.new do |spec|
|
|
||||||
spec.name = 'Geth'
|
|
||||||
spec.version = '{{.Version}}'
|
|
||||||
spec.license = { :type => 'GNU Lesser General Public License, Version 3.0' }
|
|
||||||
spec.homepage = 'https://github.com/ethereum/go-ethereum'
|
|
||||||
spec.authors = { {{range .Contributors}}
|
|
||||||
'{{.Name}}' => '{{.Email}}',{{end}}
|
|
||||||
}
|
|
||||||
spec.summary = 'iOS Ethereum Client'
|
|
||||||
spec.source = { :git => 'https://github.com/ethereum/go-ethereum.git', :commit => '{{.Commit}}' }
|
|
||||||
|
|
||||||
spec.platform = :ios
|
|
||||||
spec.ios.deployment_target = '9.0'
|
|
||||||
spec.ios.vendored_frameworks = 'Frameworks/Geth.framework'
|
|
||||||
|
|
||||||
spec.prepare_command = <<-CMD
|
|
||||||
curl https://gethstore.blob.core.windows.net/builds/{{.Archive}}.tar.gz | tar -xvz
|
|
||||||
mkdir Frameworks
|
|
||||||
mv {{.Archive}}/Geth.framework Frameworks
|
|
||||||
rm -rf {{.Archive}}
|
|
||||||
CMD
|
|
||||||
end
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2016 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
|
||||||
|
|
@ -14,10 +14,14 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//go:build !android && !ios
|
//go:build tools
|
||||||
// +build !android,!ios
|
// +build tools
|
||||||
|
|
||||||
package geth
|
package tools
|
||||||
|
|
||||||
// clientIdentifier is a hard coded identifier to report into the network.
|
import (
|
||||||
var clientIdentifier = "GethMobile"
|
// Tool imports for go:generate.
|
||||||
|
_ "github.com/fjl/gencodec"
|
||||||
|
_ "github.com/golang/protobuf/protoc-gen-go"
|
||||||
|
_ "golang.org/x/tools/cmd/stringer"
|
||||||
|
)
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//go:build none
|
||||||
// +build none
|
// +build none
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -39,7 +40,6 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
@ -68,7 +68,9 @@ var (
|
||||||
"common/bitutil/bitutil",
|
"common/bitutil/bitutil",
|
||||||
"common/prque/",
|
"common/prque/",
|
||||||
"consensus/ethash/xor.go",
|
"consensus/ethash/xor.go",
|
||||||
|
"crypto/blake2b/",
|
||||||
"crypto/bn256/",
|
"crypto/bn256/",
|
||||||
|
"crypto/bls12381/",
|
||||||
"crypto/ecies/",
|
"crypto/ecies/",
|
||||||
"graphql/graphiql.go",
|
"graphql/graphiql.go",
|
||||||
"internal/jsre/deps",
|
"internal/jsre/deps",
|
||||||
|
|
@ -241,7 +243,7 @@ func gitAuthors(files []string) []string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func readAuthors() []string {
|
func readAuthors() []string {
|
||||||
content, err := ioutil.ReadFile("AUTHORS")
|
content, err := os.ReadFile("AUTHORS")
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
log.Fatalln("error reading AUTHORS:", err)
|
log.Fatalln("error reading AUTHORS:", err)
|
||||||
}
|
}
|
||||||
|
|
@ -305,7 +307,7 @@ func writeAuthors(files []string) {
|
||||||
content.WriteString("\n")
|
content.WriteString("\n")
|
||||||
}
|
}
|
||||||
fmt.Println("writing AUTHORS")
|
fmt.Println("writing AUTHORS")
|
||||||
if err := ioutil.WriteFile("AUTHORS", content.Bytes(), 0644); err != nil {
|
if err := os.WriteFile("AUTHORS", content.Bytes(), 0644); err != nil {
|
||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -340,7 +342,10 @@ func isGenerated(file string) bool {
|
||||||
}
|
}
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
buf := make([]byte, 2048)
|
buf := make([]byte, 2048)
|
||||||
n, _ := fd.Read(buf)
|
n, err := fd.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
buf = buf[:n]
|
buf = buf[:n]
|
||||||
for _, l := range bytes.Split(buf, []byte("\n")) {
|
for _, l := range bytes.Split(buf, []byte("\n")) {
|
||||||
if bytes.HasPrefix(l, []byte("// Code generated")) {
|
if bytes.HasPrefix(l, []byte("// Code generated")) {
|
||||||
|
|
@ -381,7 +386,7 @@ func writeLicense(info *info) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error stat'ing %s: %v\n", info.file, err)
|
log.Fatalf("error stat'ing %s: %v\n", info.file, err)
|
||||||
}
|
}
|
||||||
content, err := ioutil.ReadFile(info.file)
|
content, err := os.ReadFile(info.file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("error reading %s: %v\n", info.file, err)
|
log.Fatalf("error reading %s: %v\n", info.file, err)
|
||||||
}
|
}
|
||||||
|
|
@ -400,7 +405,7 @@ func writeLicense(info *info) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Println("writing", info.ShortLicense(), info.file)
|
fmt.Println("writing", info.ShortLicense(), info.file)
|
||||||
if err := ioutil.WriteFile(info.file, buf.Bytes(), fi.Mode()); err != nil {
|
if err := os.WriteFile(info.file, buf.Bytes(), fi.Mode()); err != nil {
|
||||||
log.Fatalf("error writing %s: %v", info.file, err)
|
log.Fatalf("error writing %s: %v", info.file, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ syncmode = "full"
|
||||||
# evmtimeout = "5s"
|
# evmtimeout = "5s"
|
||||||
# txfeecap = 5.0
|
# txfeecap = 5.0
|
||||||
# allow-unprotected-txs = false
|
# allow-unprotected-txs = false
|
||||||
|
# enabledeprecatedpersonal = false
|
||||||
# [jsonrpc.http]
|
# [jsonrpc.http]
|
||||||
# enabled = false
|
# enabled = false
|
||||||
# port = 8545
|
# port = 8545
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,18 @@
|
||||||
// Copyright 2019 The go-ethereum Authors
|
// Copyright 2020 The go-ethereum Authors
|
||||||
// This file is part of the go-ethereum library.
|
// This file is part of go-ethereum.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
// go-ethereum is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU Lesser General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
// (at your option) any later version.
|
// (at your option) any later version.
|
||||||
//
|
//
|
||||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
// go-ethereum is distributed in the hope that it will be useful,
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
// GNU Lesser General Public License for more details.
|
// GNU General Public License for more details.
|
||||||
//
|
//
|
||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
|
@ -41,8 +41,10 @@ func parse(data []byte) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
die(err)
|
die(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
messages := apitypes.ValidationMessages{}
|
messages := apitypes.ValidationMessages{}
|
||||||
db.ValidateCallData(nil, data, &messages)
|
db.ValidateCallData(nil, data, &messages)
|
||||||
|
|
||||||
for _, m := range messages.Messages {
|
for _, m := range messages.Messages {
|
||||||
fmt.Printf("%v: %v\n", m.Typ, m.Message)
|
fmt.Printf("%v: %v\n", m.Typ, m.Message)
|
||||||
}
|
}
|
||||||
|
|
@ -56,10 +58,12 @@ func main() {
|
||||||
switch {
|
switch {
|
||||||
case flag.NArg() == 1:
|
case flag.NArg() == 1:
|
||||||
hexdata := flag.Arg(0)
|
hexdata := flag.Arg(0)
|
||||||
|
|
||||||
data, err := hex.DecodeString(strings.TrimPrefix(hexdata, "0x"))
|
data, err := hex.DecodeString(strings.TrimPrefix(hexdata, "0x"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
die(err)
|
die(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(data)
|
parse(data)
|
||||||
default:
|
default:
|
||||||
fmt.Fprintln(os.Stderr, "Error: one argument needed")
|
fmt.Fprintln(os.Stderr, "Error: one argument needed")
|
||||||
|
|
|
||||||
|
|
@ -19,124 +19,94 @@ package main
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
"github.com/urfave/cli/v2"
|
||||||
|
|
||||||
"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/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/crypto"
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Git SHA1 commit hash of the release (set via linker flags)
|
|
||||||
gitCommit = ""
|
|
||||||
gitDate = ""
|
|
||||||
|
|
||||||
app *cli.App
|
|
||||||
|
|
||||||
// Flags needed by abigen
|
// Flags needed by abigen
|
||||||
abiFlag = cli.StringFlag{
|
abiFlag = &cli.StringFlag{
|
||||||
Name: "abi",
|
Name: "abi",
|
||||||
Usage: "Path to the Ethereum contract ABI json to bind, - for STDIN",
|
Usage: "Path to the Ethereum contract ABI json to bind, - for STDIN",
|
||||||
}
|
}
|
||||||
binFlag = cli.StringFlag{
|
binFlag = &cli.StringFlag{
|
||||||
Name: "bin",
|
Name: "bin",
|
||||||
Usage: "Path to the Ethereum contract bytecode (generate deploy method)",
|
Usage: "Path to the Ethereum contract bytecode (generate deploy method)",
|
||||||
}
|
}
|
||||||
typeFlag = cli.StringFlag{
|
typeFlag = &cli.StringFlag{
|
||||||
Name: "type",
|
Name: "type",
|
||||||
Usage: "Struct name for the binding (default = package name)",
|
Usage: "Struct name for the binding (default = package name)",
|
||||||
}
|
}
|
||||||
jsonFlag = cli.StringFlag{
|
jsonFlag = &cli.StringFlag{
|
||||||
Name: "combined-json",
|
Name: "combined-json",
|
||||||
Usage: "Path to the combined-json file generated by compiler",
|
Usage: "Path to the combined-json file generated by compiler, - for STDIN",
|
||||||
}
|
}
|
||||||
solFlag = cli.StringFlag{
|
excFlag = &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",
|
Name: "exc",
|
||||||
Usage: "Comma separated types to exclude from binding",
|
Usage: "Comma separated types to exclude from binding",
|
||||||
}
|
}
|
||||||
pkgFlag = cli.StringFlag{
|
pkgFlag = &cli.StringFlag{
|
||||||
Name: "pkg",
|
Name: "pkg",
|
||||||
Usage: "Package name to generate the binding into",
|
Usage: "Package name to generate the binding into",
|
||||||
}
|
}
|
||||||
outFlag = cli.StringFlag{
|
outFlag = &cli.StringFlag{
|
||||||
Name: "out",
|
Name: "out",
|
||||||
Usage: "Output file for the generated binding (default = stdout)",
|
Usage: "Output file for the generated binding (default = stdout)",
|
||||||
}
|
}
|
||||||
langFlag = cli.StringFlag{
|
langFlag = &cli.StringFlag{
|
||||||
Name: "lang",
|
Name: "lang",
|
||||||
Usage: "Destination language for the bindings (go, java, objc)",
|
Usage: "Destination language for the bindings (go)",
|
||||||
Value: "go",
|
Value: "go",
|
||||||
}
|
}
|
||||||
aliasFlag = cli.StringFlag{
|
aliasFlag = &cli.StringFlag{
|
||||||
Name: "alias",
|
Name: "alias",
|
||||||
Usage: "Comma separated aliases for function and event renaming, e.g. original1=alias1, original2=alias2",
|
Usage: "Comma separated aliases for function and event renaming, e.g. original1=alias1, original2=alias2",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var app = flags.NewApp("Ethereum ABI wrapper code generator")
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
app = flags.NewApp(gitCommit, gitDate, "ethereum checkpoint helper tool")
|
app.Name = "abigen"
|
||||||
app.Flags = []cli.Flag{
|
app.Flags = []cli.Flag{
|
||||||
abiFlag,
|
abiFlag,
|
||||||
binFlag,
|
binFlag,
|
||||||
typeFlag,
|
typeFlag,
|
||||||
jsonFlag,
|
jsonFlag,
|
||||||
solFlag,
|
|
||||||
solcFlag,
|
|
||||||
vyFlag,
|
|
||||||
vyperFlag,
|
|
||||||
excFlag,
|
excFlag,
|
||||||
pkgFlag,
|
pkgFlag,
|
||||||
outFlag,
|
outFlag,
|
||||||
langFlag,
|
langFlag,
|
||||||
aliasFlag,
|
aliasFlag,
|
||||||
}
|
}
|
||||||
app.Action = utils.MigrateFlags(abigen)
|
app.Action = abigen
|
||||||
cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func abigen(c *cli.Context) error {
|
func abigen(c *cli.Context) error {
|
||||||
utils.CheckExclusive(c, abiFlag, jsonFlag, solFlag, vyFlag) // Only one source can be selected.
|
utils.CheckExclusive(c, abiFlag, jsonFlag) // Only one source can be selected.
|
||||||
if c.GlobalString(pkgFlag.Name) == "" {
|
|
||||||
|
if c.String(pkgFlag.Name) == "" {
|
||||||
utils.Fatalf("No destination package specified (--pkg)")
|
utils.Fatalf("No destination package specified (--pkg)")
|
||||||
}
|
}
|
||||||
|
|
||||||
var lang bind.Lang
|
var lang bind.Lang
|
||||||
switch c.GlobalString(langFlag.Name) {
|
|
||||||
|
switch c.String(langFlag.Name) {
|
||||||
case "go":
|
case "go":
|
||||||
lang = bind.LangGo
|
lang = bind.LangGo
|
||||||
case "java":
|
|
||||||
lang = bind.LangJava
|
|
||||||
case "objc":
|
|
||||||
lang = bind.LangObjC
|
|
||||||
utils.Fatalf("Objc binding generation is uncompleted")
|
|
||||||
default:
|
default:
|
||||||
utils.Fatalf("Unsupported destination language \"%s\" (--lang)", c.GlobalString(langFlag.Name))
|
utils.Fatalf("Unsupported destination language \"%s\" (--lang)", c.String(langFlag.Name))
|
||||||
}
|
}
|
||||||
// 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 (
|
||||||
|
|
@ -147,76 +117,78 @@ func abigen(c *cli.Context) error {
|
||||||
libs = make(map[string]string)
|
libs = make(map[string]string)
|
||||||
aliases = make(map[string]string)
|
aliases = make(map[string]string)
|
||||||
)
|
)
|
||||||
if c.GlobalString(abiFlag.Name) != "" {
|
|
||||||
|
// nolint:nestif
|
||||||
|
if c.String(abiFlag.Name) != "" {
|
||||||
// Load up the ABI, optional bytecode and type name from the parameters
|
// Load up the ABI, optional bytecode and type name from the parameters
|
||||||
var (
|
var (
|
||||||
abi []byte
|
abi []byte
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
input := c.GlobalString(abiFlag.Name)
|
|
||||||
|
input := c.String(abiFlag.Name)
|
||||||
if input == "-" {
|
if input == "-" {
|
||||||
abi, err = ioutil.ReadAll(os.Stdin)
|
abi, err = io.ReadAll(os.Stdin)
|
||||||
} else {
|
} else {
|
||||||
abi, err = ioutil.ReadFile(input)
|
abi, err = os.ReadFile(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to read input ABI: %v", err)
|
utils.Fatalf("Failed to read input ABI: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
abis = append(abis, string(abi))
|
abis = append(abis, string(abi))
|
||||||
|
|
||||||
var bin []byte
|
var bin []byte
|
||||||
if binFile := c.GlobalString(binFlag.Name); binFile != "" {
|
|
||||||
if bin, err = ioutil.ReadFile(binFile); err != nil {
|
if binFile := c.String(binFlag.Name); binFile != "" {
|
||||||
|
if bin, err = os.ReadFile(binFile); err != nil {
|
||||||
utils.Fatalf("Failed to read input bytecode: %v", err)
|
utils.Fatalf("Failed to read input bytecode: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.Contains(string(bin), "//") {
|
if strings.Contains(string(bin), "//") {
|
||||||
utils.Fatalf("Contract has additional library references, please use other mode(e.g. --combined-json) to catch library infos")
|
utils.Fatalf("Contract has additional library references, please use other mode(e.g. --combined-json) to catch library infos")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bins = append(bins, string(bin))
|
bins = append(bins, string(bin))
|
||||||
|
|
||||||
kind := c.GlobalString(typeFlag.Name)
|
kind := c.String(typeFlag.Name)
|
||||||
if kind == "" {
|
if kind == "" {
|
||||||
kind = c.GlobalString(pkgFlag.Name)
|
kind = c.String(pkgFlag.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
types = append(types, kind)
|
types = append(types, kind)
|
||||||
} else {
|
} else {
|
||||||
// Generate the list of types to exclude from binding
|
// Generate the list of types to exclude from binding
|
||||||
exclude := make(map[string]bool)
|
var exclude *nameFilter
|
||||||
for _, kind := range strings.Split(c.GlobalString(excFlag.Name), ",") {
|
|
||||||
exclude[strings.ToLower(kind)] = true
|
if c.IsSet(excFlag.Name) {
|
||||||
}
|
|
||||||
var err error
|
var err error
|
||||||
|
if exclude, err = newNameFilter(strings.Split(c.String(excFlag.Name), ",")...); err != nil {
|
||||||
|
utils.Fatalf("Failed to parse excludes: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var contracts map[string]*compiler.Contract
|
var contracts map[string]*compiler.Contract
|
||||||
|
|
||||||
switch {
|
if c.IsSet(jsonFlag.Name) {
|
||||||
case c.GlobalIsSet(solFlag.Name):
|
var (
|
||||||
contracts, err = compiler.CompileSolidity(c.GlobalString(solcFlag.Name), c.GlobalString(solFlag.Name))
|
input = c.String(jsonFlag.Name)
|
||||||
if err != nil {
|
jsonOutput []byte
|
||||||
utils.Fatalf("Failed to build Solidity contract: %v", err)
|
err error
|
||||||
}
|
)
|
||||||
case c.GlobalIsSet(vyFlag.Name):
|
|
||||||
output, err := compiler.CompileVyper(c.GlobalString(vyperFlag.Name), c.GlobalString(vyFlag.Name))
|
if input == "-" {
|
||||||
if err != nil {
|
jsonOutput, err = io.ReadAll(os.Stdin)
|
||||||
utils.Fatalf("Failed to build Vyper contract: %v", err)
|
} else {
|
||||||
}
|
jsonOutput, err = os.ReadFile(input)
|
||||||
contracts = make(map[string]*compiler.Contract)
|
|
||||||
for n, contract := range output {
|
|
||||||
name := n
|
|
||||||
// Sanitize the combined json names to match the
|
|
||||||
// format expected by solidity.
|
|
||||||
if !strings.Contains(n, ":") {
|
|
||||||
// Remove extra path components
|
|
||||||
name = abi.ToCamelCase(strings.TrimSuffix(filepath.Base(name), ".vy"))
|
|
||||||
}
|
|
||||||
contracts[name] = contract
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case c.GlobalIsSet(jsonFlag.Name):
|
|
||||||
jsonOutput, err := ioutil.ReadFile(c.GlobalString(jsonFlag.Name))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to read combined-json from compiler: %v", err)
|
utils.Fatalf("Failed to read combined-json: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
contracts, err = compiler.ParseCombinedJSON(jsonOutput, "", "", "", "")
|
contracts, err = compiler.ParseCombinedJSON(jsonOutput, "", "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to read contract information from json output: %v", err)
|
utils.Fatalf("Failed to read contract information from json output: %v", err)
|
||||||
|
|
@ -224,48 +196,61 @@ func abigen(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
// Gather all non-excluded contract for binding
|
// Gather all non-excluded contract for binding
|
||||||
for name, contract := range contracts {
|
for name, contract := range contracts {
|
||||||
if exclude[strings.ToLower(name)] {
|
// fully qualified name is of the form <solFilePath>:<type>
|
||||||
|
nameParts := strings.Split(name, ":")
|
||||||
|
typeName := nameParts[len(nameParts)-1]
|
||||||
|
|
||||||
|
if exclude != nil && exclude.Matches(name) {
|
||||||
|
fmt.Fprintf(os.Stderr, "excluding: %v\n", name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
utils.Fatalf("Failed to parse ABIs from compiler output: %v", err)
|
utils.Fatalf("Failed to parse ABIs from compiler output: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
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, ":")
|
types = append(types, typeName)
|
||||||
types = append(types, nameParts[len(nameParts)-1])
|
|
||||||
|
|
||||||
libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36]
|
// Derive the library placeholder which is a 34 character prefix of the
|
||||||
libs[libPattern] = nameParts[len(nameParts)-1]
|
// hex encoding of the keccak256 hash of the fully qualified library name.
|
||||||
|
// Note that the fully qualified library name is the path of its source
|
||||||
|
// file and the library name separated by ":".
|
||||||
|
libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36] // the first 2 chars are 0x
|
||||||
|
libs[libPattern] = typeName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Extract all aliases from the flags
|
// Extract all aliases from the flags
|
||||||
if c.GlobalIsSet(aliasFlag.Name) {
|
if c.IsSet(aliasFlag.Name) {
|
||||||
// We support multi-versions for aliasing
|
// We support multi-versions for aliasing
|
||||||
// e.g.
|
// e.g.
|
||||||
// foo=bar,foo2=bar2
|
// foo=bar,foo2=bar2
|
||||||
// foo:bar,foo2:bar2
|
// foo:bar,foo2:bar2
|
||||||
re := regexp.MustCompile(`(?:(\w+)[:=](\w+))`)
|
re := regexp.MustCompile(`(?:(\w+)[:=](\w+))`)
|
||||||
submatches := re.FindAllStringSubmatch(c.GlobalString(aliasFlag.Name), -1)
|
|
||||||
|
submatches := re.FindAllStringSubmatch(c.String(aliasFlag.Name), -1)
|
||||||
for _, match := range submatches {
|
for _, match := range submatches {
|
||||||
aliases[match[1]] = match[2]
|
aliases[match[1]] = match[2]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Generate the contract binding
|
// Generate the contract binding
|
||||||
code, err := bind.Bind(types, abis, bins, sigs, c.GlobalString(pkgFlag.Name), lang, libs, aliases)
|
code, err := bind.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), lang, libs, aliases)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Failed to generate ABI binding: %v", err)
|
utils.Fatalf("Failed to generate ABI binding: %v", err)
|
||||||
}
|
}
|
||||||
// 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 !c.GlobalIsSet(outFlag.Name) {
|
if !c.IsSet(outFlag.Name) {
|
||||||
fmt.Printf("%s\n", code)
|
fmt.Printf("%s\n", code)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := ioutil.WriteFile(c.GlobalString(outFlag.Name), []byte(code), 0600); err != nil {
|
|
||||||
|
if err := os.WriteFile(c.String(outFlag.Name), []byte(code), 0600); err != nil {
|
||||||
utils.Fatalf("Failed to write ABI binding: %v", err)
|
utils.Fatalf("Failed to write ABI binding: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
61
cmd/abigen/namefilter.go
Normal file
61
cmd/abigen/namefilter.go
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type nameFilter struct {
|
||||||
|
fulls map[string]bool // path/to/contract.sol:Type
|
||||||
|
files map[string]bool // path/to/contract.sol:*
|
||||||
|
types map[string]bool // *:Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNameFilter(patterns ...string) (*nameFilter, error) {
|
||||||
|
f := &nameFilter{
|
||||||
|
fulls: make(map[string]bool),
|
||||||
|
files: make(map[string]bool),
|
||||||
|
types: make(map[string]bool),
|
||||||
|
}
|
||||||
|
for _, pattern := range patterns {
|
||||||
|
if err := f.add(pattern); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *nameFilter) add(pattern string) error {
|
||||||
|
ft := strings.Split(pattern, ":")
|
||||||
|
if len(ft) != 2 {
|
||||||
|
// filenames and types must not include ':' symbol
|
||||||
|
return fmt.Errorf("invalid pattern: %s", pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, typ := ft[0], ft[1]
|
||||||
|
if file == "*" {
|
||||||
|
f.types[typ] = true
|
||||||
|
return nil
|
||||||
|
} else if typ == "*" {
|
||||||
|
f.files[file] = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
f.fulls[pattern] = true
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *nameFilter) Matches(name string) bool {
|
||||||
|
ft := strings.Split(name, ":")
|
||||||
|
if len(ft) != 2 {
|
||||||
|
// If contract names are always of the fully-qualified form
|
||||||
|
// <filePath>:<type>, then this case will never happen.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
file, typ := ft[0], ft[1]
|
||||||
|
// full paths > file paths > types
|
||||||
|
return f.fulls[name] || f.files[file] || f.types[typ]
|
||||||
|
}
|
||||||
40
cmd/abigen/namefilter_test.go
Normal file
40
cmd/abigen/namefilter_test.go
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNameFilter(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
_, err := newNameFilter("Foo")
|
||||||
|
require.Error(t, err)
|
||||||
|
_, err = newNameFilter("too/many:colons:Foo")
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
|
f, err := newNameFilter("a/path:A", "*:B", "c/path:*")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, tt := range []struct {
|
||||||
|
name string
|
||||||
|
match bool
|
||||||
|
}{
|
||||||
|
{"a/path:A", true},
|
||||||
|
{"unknown/path:A", false},
|
||||||
|
{"a/path:X", false},
|
||||||
|
{"unknown/path:X", false},
|
||||||
|
{"any/path:B", true},
|
||||||
|
{"c/path:X", true},
|
||||||
|
{"c/path:foo:B", false},
|
||||||
|
} {
|
||||||
|
match := f.Matches(tt.name)
|
||||||
|
if tt.match {
|
||||||
|
assert.True(t, match, "expected match")
|
||||||
|
} else {
|
||||||
|
assert.False(t, match, "expected no match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue