mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 06:36:43 +00:00
Revert "Upstream merge from go-ethereum/v1.11.6 (#901)"
This reverts commit bbfb2016fa.
This commit is contained in:
parent
bbfb2016fa
commit
9c8e572edd
1402 changed files with 181600 additions and 204584 deletions
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
|
|
@ -35,6 +35,6 @@ and help.
|
|||
|
||||
## Configuration, dependencies, and tests
|
||||
|
||||
Please see the [Developers' Guide](https://geth.ethereum.org/docs/developers/geth-developer/dev-guide)
|
||||
Please see the [Developers' Guide](https://geth.ethereum.org/docs/developers/devguide)
|
||||
for more details on configuring your environment, managing project dependencies
|
||||
and testing procedures.
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ linters-settings:
|
|||
|
||||
issues:
|
||||
exclude-rules:
|
||||
- path: crypto/bn256/cloudflare/optate.go
|
||||
- path: crypto/blake2b/
|
||||
linters:
|
||||
- deadcode
|
||||
- path: crypto/bn256/cloudflare
|
||||
|
|
|
|||
274
.mailmap
274
.mailmap
|
|
@ -1,237 +1,123 @@
|
|||
Aaron Buchwald <aaron.buchwald56@gmail.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>
|
||||
|
||||
Aaron Kumavis <kumavis@users.noreply.github.com>
|
||||
Viktor Trón <viktor.tron@gmail.com>
|
||||
|
||||
Abel Nieto <abel.nieto90@gmail.com>
|
||||
Abel Nieto <abel.nieto90@gmail.com> <anietoro@uwaterloo.ca>
|
||||
Joseph Goulden <joegoulden@gmail.com>
|
||||
|
||||
Afri Schoedon <58883403+q9f@users.noreply.github.com>
|
||||
Afri Schoedon <5chdn@users.noreply.github.com> <58883403+q9f@users.noreply.github.com>
|
||||
Nick Savers <nicksavers@gmail.com>
|
||||
|
||||
Alec Perseghin <aperseghin@gmail.com>
|
||||
Maran Hidskes <maran.hidskes@gmail.com>
|
||||
|
||||
Aleksey Smyrnov <i@soar.name>
|
||||
|
||||
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>
|
||||
Taylor Gerring <taylor.gerring@gmail.com>
|
||||
Taylor Gerring <taylor.gerring@gmail.com> <taylor.gerring@ethereum.org>
|
||||
|
||||
Bas van Kervel <bas@ethdev.com>
|
||||
Bas van Kervel <bas@ethdev.com> <basvankervel@ziggo.nl>
|
||||
Bas van Kervel <bas@ethdev.com> <basvankervel@gmail.com>
|
||||
Bas van Kervel <bas@ethdev.com> <bas-vk@users.noreply.github.com>
|
||||
|
||||
Boqin Qin <bobbqqin@bupt.edu.cn>
|
||||
Boqin Qin <bobbqqin@bupt.edu.cn> <Bobbqqin@gmail.com>
|
||||
Sven Ehlert <sven@ethdev.com>
|
||||
|
||||
Casey Detrio <cdetrio@gmail.com>
|
||||
Vitalik Buterin <v@buterin.com>
|
||||
|
||||
Cheng Li <lob4tt@gmail.com>
|
||||
|
||||
Chris Ziogas <ziogaschr@gmail.com>
|
||||
Chris Ziogas <ziogaschr@gmail.com> <ziogas_chr@hotmail.com>
|
||||
Marian Oancea <contact@siteshop.ro>
|
||||
|
||||
Christoph Jentzsch <jentzsch.software@gmail.com>
|
||||
|
||||
Diederik Loerakker <proto@protolambda.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>
|
||||
|
||||
Domino Valdano <dominoplural@gmail.com>
|
||||
Domino Valdano <dominoplural@gmail.com> <jeff@okcupid.com>
|
||||
Roman Mandeleil <roman.mandeleil@gmail.com>
|
||||
|
||||
Edgar Aroutiounian <edgar.factorial@gmail.com>
|
||||
Alec Perseghin <aperseghin@gmail.com>
|
||||
|
||||
Elliot Shepherd <elliot@identitii.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>
|
||||
|
||||
Enrique Fynn <me@enriquefynn.com>
|
||||
Enrique Fynn <me@enriquefynn.com> <enriquefynn@gmail.com>
|
||||
Vincent G <caktux@gmail.com>
|
||||
|
||||
Ernesto del Toro <ernesto.deltoro@gmail.com>
|
||||
Ernesto del Toro <ernesto.deltoro@gmail.com> <ernestodeltoro@users.noreply.github.com>
|
||||
RJ Catalano <catalanor0220@gmail.com>
|
||||
RJ Catalano <catalanor0220@gmail.com> <rj@erisindustries.com>
|
||||
|
||||
Everton Fraga <ev@ethereum.org>
|
||||
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>
|
||||
|
||||
Jens Agerberg <github@agerberg.me>
|
||||
|
||||
Nick Johnson <arachnid@notdot.net>
|
||||
|
||||
Henning Diedrich <hd@eonblast.com>
|
||||
Henning Diedrich <hd@eonblast.com> Drake Burroughs <wildfyre@hotmail.com>
|
||||
|
||||
Felix Lange <fjl@twurst.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>
|
||||
|
||||
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>
|
||||
|
||||
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@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> <wuestholz@users.noreply.github.com>
|
||||
|
||||
Victor Tran <vu.tran54@gmail.com>
|
||||
Armin Braun <me@obrown.io>
|
||||
|
||||
Viktor Trón <viktor.tron@gmail.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>
|
||||
Ernesto del Toro <ernesto.deltoro@gmail.com>
|
||||
Ernesto del Toro <ernesto.deltoro@gmail.com> <ernestodeltoro@users.noreply.github.com>
|
||||
|
|
|
|||
45
.travis.yml
45
.travis.yml
|
|
@ -5,8 +5,11 @@ jobs:
|
|||
allow_failures:
|
||||
- stage: build
|
||||
os: osx
|
||||
go: 1.17.x
|
||||
env:
|
||||
- azure-osx
|
||||
- azure-ios
|
||||
- cocoapods-ios
|
||||
|
||||
include:
|
||||
# This builder only tests code linters on latest version of Go
|
||||
|
|
@ -162,6 +165,8 @@ jobs:
|
|||
go: 1.20.x
|
||||
env:
|
||||
- azure-osx
|
||||
- azure-ios
|
||||
- cocoapods-ios
|
||||
- GO111MODULE=on
|
||||
git:
|
||||
submodules: false # avoid cloning ethereum/tests
|
||||
|
|
@ -169,6 +174,21 @@ jobs:
|
|||
- 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
|
||||
|
||||
# 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
|
||||
- stage: build
|
||||
os: linux
|
||||
|
|
@ -178,7 +198,7 @@ jobs:
|
|||
env:
|
||||
- GO111MODULE=on
|
||||
script:
|
||||
- go run build/ci.go test $TEST_PACKAGES
|
||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
||||
|
||||
- stage: build
|
||||
if: type = pull_request
|
||||
|
|
@ -189,31 +209,16 @@ jobs:
|
|||
env:
|
||||
- GO111MODULE=on
|
||||
script:
|
||||
- go run build/ci.go test $TEST_PACKAGES
|
||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
||||
|
||||
# This builder does the Ubuntu PPA nightly uploads
|
||||
- stage: build
|
||||
if: type = cron || (type = push && tag ~= /^v[0-9]/)
|
||||
os: linux
|
||||
dist: bionic
|
||||
go: 1.18.x
|
||||
go: 1.17.x
|
||||
env:
|
||||
- ubuntu-ppa
|
||||
- GO111MODULE=on
|
||||
git:
|
||||
submodules: false # avoid cloning ethereum/tests
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- devscripts
|
||||
- debhelper
|
||||
- dput
|
||||
- fakeroot
|
||||
- python-bzrlib
|
||||
- python-paramiko
|
||||
script:
|
||||
- echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts
|
||||
- go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"
|
||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
||||
|
||||
# This builder does the Azure archive purges to avoid accumulating junk
|
||||
- stage: build
|
||||
|
|
@ -238,5 +243,5 @@ jobs:
|
|||
env:
|
||||
- GO111MODULE=on
|
||||
script:
|
||||
- go run build/ci.go test -race $TEST_PACKAGES
|
||||
- go run build/ci.go test -race -coverage $TEST_PACKAGES
|
||||
|
||||
|
|
|
|||
260
AUTHORS
260
AUTHORS
|
|
@ -1,46 +1,27 @@
|
|||
# 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>
|
||||
Aaron Buchwald <aaron.buchwald56@gmail.com>
|
||||
Abel Nieto <abel.nieto90@gmail.com>
|
||||
Abel Nieto <anietoro@uwaterloo.ca>
|
||||
Adam Babik <a.babik@designfortress.com>
|
||||
Adam Schmideg <adamschmideg@users.noreply.github.com>
|
||||
Aditya <adityasripal@gmail.com>
|
||||
Aditya Arora <arora.aditya520@gmail.com>
|
||||
Adrià Cidre <adria.cidre@gmail.com>
|
||||
Afanasii Kurakin <afanasy@users.noreply.github.com>
|
||||
Afri Schoedon <5chdn@users.noreply.github.com>
|
||||
Agustin Armellini Fischer <armellini13@gmail.com>
|
||||
Ahyun <urbanart2251@gmail.com>
|
||||
Airead <fgh1987168@gmail.com>
|
||||
Alan Chen <alanchchen@users.noreply.github.com>
|
||||
Alejandro Isaza <alejandro.isaza@gmail.com>
|
||||
Aleksey Smyrnov <i@soar.name>
|
||||
Ales Katona <ales@coinbase.com>
|
||||
Alex Beregszaszi <alex@rtfs.hu>
|
||||
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>
|
||||
Alexander van der Meij <alexandervdm@users.noreply.github.com>
|
||||
Alexander Yastrebov <yastrebov.alex@gmail.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>
|
||||
am2rican5 <am2rican5@gmail.com>
|
||||
AmitBRD <60668103+AmitBRD@users.noreply.github.com>
|
||||
Anatole <62328077+a2br@users.noreply.github.com>
|
||||
Andrea Franz <andrea@gravityblast.com>
|
||||
Andrei Maiboroda <andrei@ethereum.org>
|
||||
Andrey Petrov <andrey.petrov@shazow.net>
|
||||
Andrey Petrov <shazow@gmail.com>
|
||||
ANOTHEL <anothel1@naver.com>
|
||||
Antoine Rondelet <rondelet.antoine@gmail.com>
|
||||
Antoine Toulme <atoulme@users.noreply.github.com>
|
||||
Anton Evangelatov <anton.evangelatov@gmail.com>
|
||||
Antonio Salazar Cardozo <savedfastcool@gmail.com>
|
||||
Arba Sasmoyo <arba.sasmoyo@gmail.com>
|
||||
|
|
@ -48,26 +29,19 @@ Armani Ferrante <armaniferrante@berkeley.edu>
|
|||
Armin Braun <me@obrown.io>
|
||||
Aron Fischer <github@aron.guru>
|
||||
atsushi-ishibashi <atsushi.ishibashi@finatext.com>
|
||||
Austin Roberts <code@ausiv.com>
|
||||
ayeowch <ayeowch@gmail.com>
|
||||
b00ris <b00ris@mail.ru>
|
||||
b1ackd0t <blackd0t@protonmail.com>
|
||||
bailantaotao <Edwin@maicoin.com>
|
||||
baizhenxuan <nkbai@163.com>
|
||||
Balaji Shetty Pachai <32358081+balajipachai@users.noreply.github.com>
|
||||
Balint Gabor <balint.g@gmail.com>
|
||||
baptiste-b-pegasys <85155432+baptiste-b-pegasys@users.noreply.github.com>
|
||||
Bas van Kervel <bas@ethdev.com>
|
||||
Benjamin Brent <benjamin@benjaminbrent.com>
|
||||
benma <mbencun@gmail.com>
|
||||
Benoit Verkindt <benoit.verkindt@gmail.com>
|
||||
Binacs <bin646891055@gmail.com>
|
||||
bloonfield <bloonfield@163.com>
|
||||
Bo <bohende@gmail.com>
|
||||
Bo Ye <boy.e.computer.1982@outlook.com>
|
||||
Bob Glickstein <bobg@users.noreply.github.com>
|
||||
Boqin Qin <bobbqqin@bupt.edu.cn>
|
||||
Brandon Harden <b.harden92@gmail.com>
|
||||
Brent <bmperrea@gmail.com>
|
||||
Brian Schroeder <bts@gmail.com>
|
||||
Bruno Škvorc <bruno@skvorc.me>
|
||||
|
|
@ -75,58 +49,36 @@ C. Brown <hackdom@majoolr.io>
|
|||
Caesar Chad <BLUE.WEB.GEEK@gmail.com>
|
||||
Casey Detrio <cdetrio@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>
|
||||
Chase Wright <mysticryuujin@gmail.com>
|
||||
Chen Quan <terasum@163.com>
|
||||
Cheng Li <lob4tt@gmail.com>
|
||||
chenglin <910372762@qq.com>
|
||||
chenyufeng <yufengcode@gmail.com>
|
||||
Chris Pacia <ctpacia@gmail.com>
|
||||
Chris Ziogas <ziogaschr@gmail.com>
|
||||
Christian Muehlhaeuser <muesli@gmail.com>
|
||||
Christoph Jentzsch <jentzsch.software@gmail.com>
|
||||
chuwt <weitaochu@gmail.com>
|
||||
cong <ackratos@users.noreply.github.com>
|
||||
Connor Stein <connor.stein@mail.mcgill.ca>
|
||||
Corey Lin <514971757@qq.com>
|
||||
courtier <derinilter@gmail.com>
|
||||
cpusoft <cpusoft@live.com>
|
||||
Crispin Flowerday <crispin@bitso.com>
|
||||
croath <croathliu@gmail.com>
|
||||
cui <523516579@qq.com>
|
||||
Dan DeGreef <dan.degreef@gmail.com>
|
||||
Dan Kinsley <dan@joincivil.com>
|
||||
Dan Sosedoff <dan.sosedoff@gmail.com>
|
||||
Daniel A. Nagy <nagy.da@gmail.com>
|
||||
Daniel Perez <daniel@perez.sh>
|
||||
Daniel Sloof <goapsychadelic@gmail.com>
|
||||
Darioush Jalali <darioush.jalali@avalabs.org>
|
||||
Darrel Herbst <dherbst@gmail.com>
|
||||
Dave Appleton <calistralabs@gmail.com>
|
||||
Dave McGregor <dave.s.mcgregor@gmail.com>
|
||||
David Cai <davidcai1993@yahoo.com>
|
||||
David Huie <dahuie@gmail.com>
|
||||
Denver <aeharvlee@gmail.com>
|
||||
Derek Chiang <me@derekchiang.com>
|
||||
Derek Gottfrid <derek@codecubed.com>
|
||||
Di Peng <pendyaaa@gmail.com>
|
||||
Diederik Loerakker <proto@protolambda.com>
|
||||
Diego Siqueira <DiSiqueira@users.noreply.github.com>
|
||||
Diep Pham <mrfavadi@gmail.com>
|
||||
dipingxian2 <39109351+dipingxian2@users.noreply.github.com>
|
||||
divergencetech <94644849+divergencetech@users.noreply.github.com>
|
||||
dm4 <sunrisedm4@gmail.com>
|
||||
Dmitrij Koniajev <dimchansky@gmail.com>
|
||||
Dmitry Shulyak <yashulyak@gmail.com>
|
||||
Dmitry Zenovich <dzenovich@gmail.com>
|
||||
Domino Valdano <dominoplural@gmail.com>
|
||||
Domino Valdano <jeff@okcupid.com>
|
||||
Dragan Milic <dragan@netice9.com>
|
||||
dragonvslinux <35779158+dragononcrypto@users.noreply.github.com>
|
||||
Edgar Aroutiounian <edgar.factorial@gmail.com>
|
||||
Eduard S <eduardsanou@posteo.net>
|
||||
Egon Elbre <egonelbre@gmail.com>
|
||||
Elad <theman@elad.im>
|
||||
Eli <elihanover@yahoo.com>
|
||||
|
|
@ -134,189 +86,131 @@ Elias Naur <elias.naur@gmail.com>
|
|||
Elliot Shepherd <elliot@identitii.com>
|
||||
Emil <mursalimovemeel@gmail.com>
|
||||
emile <emile@users.noreply.github.com>
|
||||
Emmanuel T Odeke <odeke@ualberta.ca>
|
||||
Eng Zer Jun <engzerjun@gmail.com>
|
||||
Enrique Fynn <enriquefynn@gmail.com>
|
||||
Enrique Fynn <me@enriquefynn.com>
|
||||
Enrique Ortiz <hi@enriqueortiz.dev>
|
||||
EOS Classic <info@eos-classic.io>
|
||||
Erichin <erichinbato@gmail.com>
|
||||
Ernesto del Toro <ernesto.deltoro@gmail.com>
|
||||
Ethan Buchman <ethan@coinculture.info>
|
||||
ethersphere <thesw@rm.eth>
|
||||
Eugene Lepeico <eugenelepeico@gmail.com>
|
||||
Eugene Valeyev <evgen.povt@gmail.com>
|
||||
Evangelos Pappas <epappas@evalonlabs.com>
|
||||
Everton Fraga <ev@ethereum.org>
|
||||
Evgeny <awesome.observer@yandex.com>
|
||||
Evgeny Danilenko <6655321@bk.ru>
|
||||
evgk <evgeniy.kamyshev@gmail.com>
|
||||
Evolution404 <35091674+Evolution404@users.noreply.github.com>
|
||||
EXEC <execvy@gmail.com>
|
||||
Fabian Vogelsteller <fabian@frozeman.de>
|
||||
Fabio Barone <fabio.barone.co@gmail.com>
|
||||
Fabio Berger <fabioberger1991@gmail.com>
|
||||
FaceHo <facehoshi@gmail.com>
|
||||
Felipe Strozberg <48066928+FelStroz@users.noreply.github.com>
|
||||
Felix Lange <fjl@twurst.com>
|
||||
Ferenc Szabo <frncmx@gmail.com>
|
||||
ferhat elmas <elmas.ferhat@gmail.com>
|
||||
Ferran Borreguero <ferranbt@protonmail.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 Wang <eternnoir@gmail.com>
|
||||
Franklin <mr_franklin@126.com>
|
||||
Furkan KAMACI <furkankamaci@gmail.com>
|
||||
Fuyang Deng <dengfuyang@outlook.com>
|
||||
GagziW <leon.stanko@rwth-aachen.de>
|
||||
Gary Rong <garyrong0905@gmail.com>
|
||||
Gautam Botrel <gautam.botrel@gmail.com>
|
||||
George Ornbo <george@shapeshed.com>
|
||||
Giuseppe Bertone <bertone.giuseppe@gmail.com>
|
||||
Greg Colvin <greg@colvin.org>
|
||||
Gregg Dourgarian <greggd@tempworks.com>
|
||||
Gregory Markou <16929357+GregTheGreek@users.noreply.github.com>
|
||||
Guifel <toowik@gmail.com>
|
||||
Guilherme Salgado <gsalgado@gmail.com>
|
||||
Guillaume Ballet <gballet@gmail.com>
|
||||
Guillaume Nicolas <guin56@gmail.com>
|
||||
GuiltyMorishita <morilliantblue@gmail.com>
|
||||
Guruprasad Kamath <48196632+gurukamath@users.noreply.github.com>
|
||||
Gus <yo@soygus.com>
|
||||
Gustav Simonsson <gustav.simonsson@gmail.com>
|
||||
Gísli Kristjánsson <gislik@hamstur.is>
|
||||
Ha ĐANG <dvietha@gmail.com>
|
||||
HackyMiner <hackyminer@gmail.com>
|
||||
hadv <dvietha@gmail.com>
|
||||
Hanjiang Yu <delacroix.yu@gmail.com>
|
||||
Hao Bryan Cheng <haobcheng@gmail.com>
|
||||
Hao Duan <duanhao0814@gmail.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>
|
||||
henopied <13500516+henopied@users.noreply.github.com>
|
||||
hero5512 <lvshuaino@gmail.com>
|
||||
holisticode <holistic.computing@gmail.com>
|
||||
Hongbin Mao <hello2mao@gmail.com>
|
||||
Hsien-Tang Kao <htkao@pm.me>
|
||||
hsyodyssey <47173566+hsyodyssey@users.noreply.github.com>
|
||||
Husam Ibrahim <39692071+HusamIbrahim@users.noreply.github.com>
|
||||
Hwanjo Heo <34005989+hwanjo@users.noreply.github.com>
|
||||
hydai <z54981220@gmail.com>
|
||||
Hyung-Kyu Hqueue Choi <hyungkyu.choi@gmail.com>
|
||||
Håvard Anda Estensen <haavard.ae@gmail.com>
|
||||
Ian Macalinao <me@ian.pw>
|
||||
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>
|
||||
Iskander (Alex) Sharipov <quasilyte@gmail.com>
|
||||
Ivan Bogatyy <bogatyi@gmail.com>
|
||||
Ivan Daniluk <ivan.daniluk@gmail.com>
|
||||
Ivo Georgiev <ivo@strem.io>
|
||||
jacksoom <lifengliu1994@gmail.com>
|
||||
Jae Kwon <jkwon.work@gmail.com>
|
||||
James Prestwich <10149425+prestwich@users.noreply.github.com>
|
||||
Jamie Pitts <james.pitts@gmail.com>
|
||||
Janoš Guljaš <janos@resenje.org>
|
||||
Jared Wasinger <j-wasinger@hotmail.com>
|
||||
Janos Guljas <janos@resenje.org>
|
||||
Janoš Guljaš <janos@users.noreply.github.com>
|
||||
Jason Carver <jacarver@linkedin.com>
|
||||
Javier Peletier <jm@epiclabs.io>
|
||||
Javier Peletier <jpeletier@users.noreply.github.com>
|
||||
Javier Sagredo <jasataco@gmail.com>
|
||||
Jay <codeholic.arena@gmail.com>
|
||||
Jay Guo <guojiannan1101@gmail.com>
|
||||
Jaynti Kanani <jdkanani@gmail.com>
|
||||
Jeff Prestes <jeffprestes@gmail.com>
|
||||
Jeff R. Allen <jra@nella.org>
|
||||
Jeff Wentworth <jeff@curvegrid.com>
|
||||
Jeffery Robert Walsh <rlxrlps@gmail.com>
|
||||
Jeffrey Wilcke <jeffrey@ethereum.org>
|
||||
Jens Agerberg <github@agerberg.me>
|
||||
Jeremy McNevin <jeremy.mcnevin@optum.com>
|
||||
Jeremy Schlatter <jeremy.schlatter@gmail.com>
|
||||
Jerzy Lasyk <jerzylasyk@gmail.com>
|
||||
Jesse Tane <jesse.tane@gmail.com>
|
||||
Jia Chenhui <jiachenhui1989@gmail.com>
|
||||
Jim McDonald <Jim@mcdee.net>
|
||||
jk-jeongkyun <45347815+jeongkyun-oh@users.noreply.github.com>
|
||||
jkcomment <jkcomment@gmail.com>
|
||||
JoeGruffins <34998433+JoeGruffins@users.noreply.github.com>
|
||||
Joel Burget <joelburget@gmail.com>
|
||||
John C. Vernaleo <john@netpurgatory.com>
|
||||
John Difool <johndifoolpi@gmail.com>
|
||||
Johns Beharry <johns@peakshift.com>
|
||||
Jonas <felberj@users.noreply.github.com>
|
||||
Jonathan Brown <jbrown@bluedroplet.com>
|
||||
Jonathan Chappelow <chappjc@users.noreply.github.com>
|
||||
Jonathan Gimeno <jgimeno@gmail.com>
|
||||
JoranHonig <JoranHonig@users.noreply.github.com>
|
||||
Jordan Krage <jmank88@gmail.com>
|
||||
Jorropo <jorropo.pgm@gmail.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>
|
||||
JU HYEONG PARK <dkdkajej@gmail.com>
|
||||
Julian Y <jyap808@users.noreply.github.com>
|
||||
Justin Clark-Casey <justincc@justincc.org>
|
||||
Justin Drake <drakefjustin@gmail.com>
|
||||
Justus <jus@gtsbr.org>
|
||||
Kawashima <91420903+sscodereth@users.noreply.github.com>
|
||||
jwasinger <j-wasinger@hotmail.com>
|
||||
ken10100147 <sunhongping@kanjian.com>
|
||||
Kenji Siu <kenji@isuntv.com>
|
||||
Kenso Trabing <kenso.trabing@bloomwebsite.com>
|
||||
Kenso Trabing <ktrabing@acm.org>
|
||||
Kevin <denk.kevin@web.de>
|
||||
kevin.xu <cming.xu@gmail.com>
|
||||
KibGzr <kibgzr@gmail.com>
|
||||
kiel barry <kiel.j.barry@gmail.com>
|
||||
kilic <onurkilic1004@gmail.com>
|
||||
kimmylin <30611210+kimmylin@users.noreply.github.com>
|
||||
Kitten King <53072918+kittenking@users.noreply.github.com>
|
||||
knarfeh <hejun1874@gmail.com>
|
||||
Kobi Gurkan <kobigurk@gmail.com>
|
||||
komika <komika@komika.org>
|
||||
Konrad Feldmeier <konrad@brainbot.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>
|
||||
Kushagra Sharma <ksharm01@gmail.com>
|
||||
Kwuaint <34888408+kwuaint@users.noreply.github.com>
|
||||
Kyuntae Ethan Kim <ethan.kyuntae.kim@gmail.com>
|
||||
Lee Bousfield <ljbousfield@gmail.com>
|
||||
ledgerwatch <akhounov@gmail.com>
|
||||
Lefteris Karapetsas <lefteris@refu.co>
|
||||
Leif Jurvetson <leijurv@gmail.com>
|
||||
Leo Shklovskii <leo@thermopylae.net>
|
||||
LeoLiao <leofantast@gmail.com>
|
||||
Lewis Marshall <lewis@lmars.net>
|
||||
lhendre <lhendre2@gmail.com>
|
||||
Li Dongwei <lidw1988@126.com>
|
||||
Liang Ma <liangma.ul@gmail.com>
|
||||
Liang Ma <liangma@liangbit.com>
|
||||
Liang ZOU <liang.d.zou@gmail.com>
|
||||
libby kent <viskovitzzz@gmail.com>
|
||||
libotony <liboliqi@gmail.com>
|
||||
LieutenantRoger <dijsky_2015@hotmail.com>
|
||||
ligi <ligi@ligi.de>
|
||||
Lio李欧 <lionello@users.noreply.github.com>
|
||||
lmittmann <lmittmann@users.noreply.github.com>
|
||||
Lorenzo Manacorda <lorenzo@kinvolk.io>
|
||||
Louis Holbrook <dev@holbrook.no>
|
||||
Luca Zeug <luclu@users.noreply.github.com>
|
||||
Lucas Hendren <lhendre2@gmail.com>
|
||||
lzhfromustc <43191155+lzhfromustc@users.noreply.github.com>
|
||||
Magicking <s@6120.eu>
|
||||
manlio <manlio.poltronieri@gmail.com>
|
||||
Maran Hidskes <maran.hidskes@gmail.com>
|
||||
Marek Kotewicz <marek.kotewicz@gmail.com>
|
||||
Mariano Cortesi <mcortesi@gmail.com>
|
||||
Marius van der Wijden <m.vanderwijden@live.de>
|
||||
Mark <markya0616@gmail.com>
|
||||
Mark Rushakoff <mark.rushakoff@gmail.com>
|
||||
|
|
@ -324,193 +218,108 @@ mark.lin <mark@maicoin.com>
|
|||
Martin Alex Philip Dawson <u1356770@gmail.com>
|
||||
Martin Holst Swende <martin@swende.se>
|
||||
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>
|
||||
Matt Garnett <14004106+lightclient@users.noreply.github.com>
|
||||
Matt K <1036969+mkrump@users.noreply.github.com>
|
||||
Matthew Di Ferrante <mattdf@users.noreply.github.com>
|
||||
Matthew Halpern <matthalp@gmail.com>
|
||||
Matthew Halpern <matthalp@google.com>
|
||||
Matthew Wampler-Doty <matthew.wampler.doty@gmail.com>
|
||||
Max Sistemich <mafrasi2@googlemail.com>
|
||||
Maxim Zhiburt <zhiburt@gmail.com>
|
||||
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>
|
||||
Michael Forney <mforney@mforney.org>
|
||||
Michael Riabzev <michael@starkware.co>
|
||||
Michael Ruminer <michael.ruminer+github@gmail.com>
|
||||
michael1011 <me@michael1011.at>
|
||||
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>
|
||||
Mohanson <mohanson@outlook.com>
|
||||
mr_franklin <mr_franklin@126.com>
|
||||
Mudit Gupta <guptamudit@ymail.com>
|
||||
Mymskmkt <1847234666@qq.com>
|
||||
Nalin Bhardwaj <nalinbhardwaj@nibnalin.me>
|
||||
Natsu Kagami <natsukagami@gmail.com>
|
||||
Nchinda Nchinda <nchinda2@gmail.com>
|
||||
nebojsa94 <nebojsa94@users.noreply.github.com>
|
||||
necaremus <necaremus@gmail.com>
|
||||
nedifi <103940716+nedifi@users.noreply.github.com>
|
||||
needkane <604476380@qq.com>
|
||||
Nguyen Kien Trung <trung.n.k@gmail.com>
|
||||
Nguyen Sy Thanh Son <thanhson1085@gmail.com>
|
||||
Nic Jansma <nic@nicj.net>
|
||||
Nick Dodson <silentcicero@outlook.com>
|
||||
Nick Johnson <arachnid@notdot.net>
|
||||
Nicolas Feignon <nfeignon@gmail.com>
|
||||
Nicolas Guillaume <gunicolas@sqli.com>
|
||||
Nikita Kozhemyakin <enginegl.ec@gmail.com>
|
||||
Nikola Madjarevic <nikola.madjarevic@gmail.com>
|
||||
Nilesh Trivedi <nilesh@hypertrack.io>
|
||||
Nimrod Gutman <nimrod.gutman@gmail.com>
|
||||
Nishant Das <nishdas93@gmail.com>
|
||||
njupt-moon <1015041018@njupt.edu.cn>
|
||||
nkbai <nkbai@163.com>
|
||||
noam-alchemy <76969113+noam-alchemy@users.noreply.github.com>
|
||||
nobody <ddean2009@163.com>
|
||||
Noman <noman@noman.land>
|
||||
nujabes403 <nujabes403@gmail.com>
|
||||
Nye Liu <nyet@nyet.org>
|
||||
Oleg Kovalov <iamolegkovalov@gmail.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>
|
||||
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 Litvak <litvakpol@012.net.il>
|
||||
Paul-Armand Verhaegen <paularmand.verhaegen@gmail.com>
|
||||
Paulo L F Casaretto <pcasaretto@gmail.com>
|
||||
Paweł Bylica <chfast@gmail.com>
|
||||
Pedro Gomes <otherview@gmail.com>
|
||||
Pedro Pombeiro <PombeirP@users.noreply.github.com>
|
||||
Peter Broadhurst <peter@themumbles.net>
|
||||
peter cresswell <pcresswell@gmail.com>
|
||||
Peter Pratscher <pratscher@gmail.com>
|
||||
Peter Simard <petesimard56@gmail.com>
|
||||
Petr Mikusek <petr@mikusek.info>
|
||||
Philip Schlump <pschlump@gmail.com>
|
||||
Pierre Neter <pierreneter@gmail.com>
|
||||
Pierre R <p.rousset@gmail.com>
|
||||
piersy <pierspowlesland@gmail.com>
|
||||
PilkyuJung <anothel1@naver.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>
|
||||
protolambda <proto@protolambda.com>
|
||||
Péter Szilágyi <peterke@gmail.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>
|
||||
Ralph Caraveo <deckarep@gmail.com>
|
||||
Ralph Caraveo III <deckarep@gmail.com>
|
||||
Ramesh Nair <ram@hiddentao.com>
|
||||
rangzen <public@l-homme.com>
|
||||
reinerRubin <tolstov.georgij@gmail.com>
|
||||
Rene Lubov <41963722+renaynay@users.noreply.github.com>
|
||||
rhaps107 <dod-source@yandex.ru>
|
||||
Ricardo Catalinas Jiménez <r@untroubled.be>
|
||||
Ricardo Domingos <ricardohsd@gmail.com>
|
||||
Richard Hart <richardhart92@gmail.com>
|
||||
Rick <rick.no@groundx.xyz>
|
||||
RJ Catalano <catalanor0220@gmail.com>
|
||||
Rob <robert@rojotek.com>
|
||||
Rob Mulholand <rmulholand@8thlight.com>
|
||||
Robert Zaremba <robert@zaremba.ch>
|
||||
Robert Zaremba <robert.zaremba@scale-it.pl>
|
||||
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>
|
||||
Russ Cox <rsc@golang.org>
|
||||
Ryan Schneider <ryanleeschneider@gmail.com>
|
||||
ryanc414 <ryan@tokencard.io>
|
||||
Rémy Roy <remyroy@remyroy.com>
|
||||
S. Matthew English <s-matthew-english@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>
|
||||
sanskarkhare <sanskarkhare47@gmail.com>
|
||||
Sarlor <kinsleer@outlook.com>
|
||||
Sasuke1964 <neilperry1964@gmail.com>
|
||||
Satpal <28562234+SatpalSandhu61@users.noreply.github.com>
|
||||
Saulius Grigaitis <saulius@necolt.com>
|
||||
Sean <darcys22@gmail.com>
|
||||
Serhat Şevki Dinçer <jfcgauss@gmail.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>
|
||||
Sheldon <11510383@mail.sustc.edu.cn>
|
||||
Sheldon <374662347@qq.com>
|
||||
Shintaro Kaneko <kaneshin0120@gmail.com>
|
||||
shiqinfeng1 <150627601@qq.com>
|
||||
Shuai Qi <qishuai231@gmail.com>
|
||||
Shude Li <islishude@gmail.com>
|
||||
Shunsuke Watanabe <ww.shunsuke@gmail.com>
|
||||
silence <wangsai.silence@qq.com>
|
||||
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>
|
||||
Smilenator <yurivanenko@yandex.ru>
|
||||
soc1c <soc1c@users.noreply.github.com>
|
||||
Sorin Neacsu <sorin.neacsu@gmail.com>
|
||||
Sparty <vignesh.crysis@gmail.com>
|
||||
Stein Dekker <dekker.stein@gmail.com>
|
||||
Steve Gattuso <steve@stevegattuso.me>
|
||||
Steve Ruckdashel <steve.ruckdashel@gmail.com>
|
||||
Steve Waldman <swaldman@mchange.com>
|
||||
Steven E. Harris <seh@panix.com>
|
||||
Steven Roose <stevenroose@gmail.com>
|
||||
stompesi <stompesi@gmail.com>
|
||||
stormpang <jialinpeng@vip.qq.com>
|
||||
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>
|
||||
Tangui Clairet <tangui.clairet@gmail.com>
|
||||
Tatsuya Shimoda <tacoo@users.noreply.github.com>
|
||||
Taylor Gerring <taylor.gerring@gmail.com>
|
||||
TColl <38299499+TColl@users.noreply.github.com>
|
||||
terasum <terasum@163.com>
|
||||
tgyKomgo <52910426+tgyKomgo@users.noreply.github.com>
|
||||
Thad Guidry <thadguidry@gmail.com>
|
||||
Thomas Bocek <tom@tomp2p.net>
|
||||
thomasmodeneis <thomas.modeneis@gmail.com>
|
||||
thumb8432 <thumb8432@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>
|
||||
tsarpaul <Litvakpol@012.net.il>
|
||||
Tyler Chambers <2775339+tylerchambers@users.noreply.github.com>
|
||||
tzapu <alex@tzapu.com>
|
||||
ucwong <ucwong@126.com>
|
||||
uji <49834542+uji@users.noreply.github.com>
|
||||
ult-bobonovski <alex@ultiledger.io>
|
||||
Valentin Trinqué <ValentinTrinque@users.noreply.github.com>
|
||||
Valentin Wüstholz <wuestholz@gmail.com>
|
||||
Vedhavyas Singareddi <vedhavyas.singareddi@gmail.com>
|
||||
Victor Farazdagi <simple.square@gmail.com>
|
||||
|
|
@ -521,71 +330,40 @@ Ville Sundell <github@solarius.fi>
|
|||
vim88 <vim88vim88@gmail.com>
|
||||
Vincent G <caktux@gmail.com>
|
||||
Vincent Serpoul <vincent@serpoul.com>
|
||||
Vinod Damle <vdamle@users.noreply.github.com>
|
||||
Vitalik Buterin <v@buterin.com>
|
||||
Vitaly Bogdanov <vsbogd@gmail.com>
|
||||
Vitaly V <vvelikodny@gmail.com>
|
||||
Vivek Anand <vivekanand1101@users.noreply.github.com>
|
||||
Vlad <gluk256@gmail.com>
|
||||
Vlad Bokov <razum2um@mail.ru>
|
||||
Vlad Gluhovsky <gluk256@gmail.com>
|
||||
Ward Bradt <wardbradt5@gmail.com>
|
||||
Water <44689567+codeoneline@users.noreply.github.com>
|
||||
wbt <wbt@users.noreply.github.com>
|
||||
Vlad Gluhovsky <gluk256@users.noreply.github.com>
|
||||
weimumu <934657014@qq.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>
|
||||
williambannas <wrschwartz@wpi.edu>
|
||||
wuff1996 <33193253+wuff1996@users.noreply.github.com>
|
||||
Wuxiang <wuxiangzhou2010@gmail.com>
|
||||
Xiaobing Jiang <s7v7nislands@gmail.com>
|
||||
xiekeyang <xiekeyang@users.noreply.github.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>
|
||||
Yang Hau <vulxj0j8j8@gmail.com>
|
||||
YaoZengzeng <yaozengzeng@zju.edu.cn>
|
||||
YH-Zhou <yanhong.zhou05@gmail.com>
|
||||
Yihau Chen <a122092487@gmail.com>
|
||||
Yohann Léon <sybiload@gmail.com>
|
||||
Yoichi Hirai <i@yoichihirai.com>
|
||||
Yole <007yuyue@gmail.com>
|
||||
Yondon Fu <yondon.fu@gmail.com>
|
||||
YOSHIDA Masanori <masanori.yoshida@gmail.com>
|
||||
yoza <yoza.is12s@gmail.com>
|
||||
yumiel yoomee1313 <yumiel.ko@groundx.xyz>
|
||||
Yusup <awklsgrep@gmail.com>
|
||||
yutianwu <wzxingbupt@gmail.com>
|
||||
ywzqwwt <39263032+ywzqwwt@users.noreply.github.com>
|
||||
zaccoding <zaccoding725@gmail.com>
|
||||
Zach <zach.ramsay@gmail.com>
|
||||
Zachinquarantine <Zachinquarantine@protonmail.com>
|
||||
zah <zahary@gmail.com>
|
||||
Zahoor Mohamed <zahoor@zahoor.in>
|
||||
Zak Cole <zak@beattiecole.com>
|
||||
zcheng9 <zcheng9@hawk.iit.edu>
|
||||
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>
|
||||
zhiqiangxu <652732310@qq.com>
|
||||
Zhou Zhiyao <ZHOU0250@e.ntu.edu.sg>
|
||||
Ziyuan Zhong <zzy.albert@163.com>
|
||||
Zoe Nolan <github@zoenolan.org>
|
||||
Zou Guangxian <zouguangxian@gmail.com>
|
||||
Zsolt Felföldi <zsfelfoldi@gmail.com>
|
||||
Łukasz Kurowski <crackcomm@users.noreply.github.com>
|
||||
Łukasz Zimnoch <lukaszzimnoch1994@gmail.com>
|
||||
ΞTHΞЯSPHΞЯΞ <{viktor.tron,nagydani,zsfelfoldi}@gmail.com>
|
||||
Максим Чусовлянов <mchusovlianov@gmail.com>
|
||||
大彬 <hz_stb@163.com>
|
||||
沉风 <myself659@users.noreply.github.com>
|
||||
贺鹏飞 <hpf@hackerful.cn>
|
||||
陈佳 <chenjiablog@gmail.com>
|
||||
유용환 <33824408+eric-yoo@users.noreply.github.com>
|
||||
|
|
|
|||
1
Makefile
1
Makefile
|
|
@ -35,7 +35,6 @@ protoc:
|
|||
generate-mocks:
|
||||
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/IDatabase.go -package=filters . Database
|
||||
|
||||
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),
|
||||
also included in our repository in the `COPYING.LESSER` file.
|
||||
|
||||
The go-ethereum binaries (i.e. all code inside of the `cmd` directory) are licensed under the
|
||||
The go-ethereum binaries (i.e. all code inside of the `cmd` directory) is licensed under the
|
||||
[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.
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ func JSON(reader io.Reader) (ABI, error) {
|
|||
if err := dec.Decode(&abi); err != nil {
|
||||
return ABI{}, err
|
||||
}
|
||||
|
||||
return abi, nil
|
||||
}
|
||||
|
||||
|
|
@ -68,15 +67,12 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return arguments, nil
|
||||
}
|
||||
|
||||
method, exist := abi.Methods[name]
|
||||
if !exist {
|
||||
return nil, fmt.Errorf("method '%s' not found", name)
|
||||
}
|
||||
|
||||
arguments, err := method.Inputs.Pack(args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -89,23 +85,18 @@ func (abi ABI) getArguments(name string, data []byte) (Arguments, error) {
|
|||
// since there can't be naming collisions with contracts and events,
|
||||
// we need to decide whether we're calling a method or an event
|
||||
var args Arguments
|
||||
|
||||
if method, ok := abi.Methods[name]; ok {
|
||||
if len(data)%32 != 0 {
|
||||
return nil, fmt.Errorf("abi: improperly formatted output: %q - Bytes: %+v", data, data)
|
||||
return nil, fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(data), data)
|
||||
}
|
||||
|
||||
args = method.Outputs
|
||||
}
|
||||
|
||||
if event, ok := abi.Events[name]; ok {
|
||||
args = event.Inputs
|
||||
}
|
||||
|
||||
if args == nil {
|
||||
return nil, fmt.Errorf("abi: could not locate named method or event: %s", name)
|
||||
return nil, errors.New("abi: could not locate named method or event")
|
||||
}
|
||||
|
||||
return args, nil
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +106,6 @@ func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return args.Unpack(data)
|
||||
}
|
||||
|
||||
|
|
@ -127,12 +117,10 @@ func (abi ABI) UnpackIntoInterface(v interface{}, name string, data []byte) erro
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
unpacked, err := args.Unpack(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return args.Copy(v, unpacked)
|
||||
}
|
||||
|
||||
|
|
@ -142,7 +130,6 @@ func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte)
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return args.UnpackIntoMap(v, data)
|
||||
}
|
||||
|
||||
|
|
@ -166,21 +153,18 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
|
|||
// declared as anonymous.
|
||||
Anonymous bool
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &fields); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
abi.Methods = make(map[string]Method)
|
||||
abi.Events = make(map[string]Event)
|
||||
abi.Errors = make(map[string]Error)
|
||||
|
||||
for _, field := range fields {
|
||||
switch field.Type {
|
||||
case "constructor":
|
||||
abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
|
||||
case "function":
|
||||
name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Methods[s]; return ok })
|
||||
name := overloadedName(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)
|
||||
case "fallback":
|
||||
// New introduced function type in v0.6.0, check more detail
|
||||
|
|
@ -188,7 +172,6 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
|
|||
if abi.HasFallback() {
|
||||
return errors.New("only single fallback is allowed")
|
||||
}
|
||||
|
||||
abi.Fallback = NewMethod("", "", Fallback, field.StateMutability, field.Constant, field.Payable, nil, nil)
|
||||
case "receive":
|
||||
// New introduced function type in v0.6.0, check more detail
|
||||
|
|
@ -196,24 +179,19 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
|
|||
if abi.HasReceive() {
|
||||
return errors.New("only single receive is allowed")
|
||||
}
|
||||
|
||||
if field.StateMutability != "payable" {
|
||||
return errors.New("the statemutability of receive can only be payable")
|
||||
}
|
||||
|
||||
abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
|
||||
case "event":
|
||||
name := ResolveNameConflict(field.Name, func(s string) bool { _, ok := abi.Events[s]; return ok })
|
||||
name := overloadedName(field.Name, func(s string) bool { _, ok := abi.Events[s]; return ok })
|
||||
abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
|
||||
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)
|
||||
default:
|
||||
return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -223,13 +201,11 @@ func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
|
|||
if len(sigdata) < 4 {
|
||||
return nil, fmt.Errorf("data too short (%d bytes) for abi method lookup", len(sigdata))
|
||||
}
|
||||
|
||||
for _, method := range abi.Methods {
|
||||
if bytes.Equal(method.ID, sigdata[:4]) {
|
||||
return &method, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("no method with id: %#x", sigdata[:4])
|
||||
}
|
||||
|
||||
|
|
@ -241,7 +217,6 @@ func (abi *ABI) EventByID(topic common.Hash) (*Event, error) {
|
|||
return &event, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("no event with id: %#x", topic.Hex())
|
||||
}
|
||||
|
||||
|
|
@ -266,21 +241,30 @@ func UnpackRevert(data []byte) (string, error) {
|
|||
if len(data) < 4 {
|
||||
return "", errors.New("invalid data for unpacking")
|
||||
}
|
||||
|
||||
if !bytes.Equal(data[:4], revertSelector) {
|
||||
return "", errors.New("invalid data for unpacking")
|
||||
}
|
||||
|
||||
typ, err := NewType("string", "", nil)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
typ, _ := NewType("string", "", nil)
|
||||
unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
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,7 +134,6 @@ func TestReader(t *testing.T) {
|
|||
if !exist {
|
||||
t.Errorf("Missing expected method %v", name)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(gotM, expM) {
|
||||
t.Errorf("\nGot abi method: \n%v\ndoes not match expected method\n%v", gotM, expM)
|
||||
}
|
||||
|
|
@ -145,7 +144,6 @@ func TestReader(t *testing.T) {
|
|||
if !exist {
|
||||
t.Errorf("Found extra method %v", name)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(gotM, expM) {
|
||||
t.Errorf("\nGot abi method: \n%v\ndoes not match expected method\n%v", gotM, expM)
|
||||
}
|
||||
|
|
@ -154,14 +152,11 @@ func TestReader(t *testing.T) {
|
|||
|
||||
func TestInvalidABI(t *testing.T) {
|
||||
json := `[{ "type" : "function", "name" : "", "constant" : fals }]`
|
||||
|
||||
_, err := JSON(strings.NewReader(json))
|
||||
if err == nil {
|
||||
t.Fatal("invalid json should produce error")
|
||||
}
|
||||
|
||||
json2 := `[{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "typ" : "uint256" } ] }]`
|
||||
|
||||
_, err = JSON(strings.NewReader(json2))
|
||||
if err == nil {
|
||||
t.Fatal("invalid json should produce error")
|
||||
|
|
@ -170,7 +165,6 @@ func TestInvalidABI(t *testing.T) {
|
|||
|
||||
// TestConstructor tests a constructor function.
|
||||
// The test is based on the following contract:
|
||||
//
|
||||
// contract TestConstructor {
|
||||
// constructor(uint256 a, uint256 b) public{}
|
||||
// }
|
||||
|
|
@ -182,7 +176,6 @@ func TestConstructor(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(abi.Constructor, method) {
|
||||
t.Error("Missing expected constructor")
|
||||
}
|
||||
|
|
@ -191,7 +184,6 @@ func TestConstructor(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
unpacked, err := abi.Constructor.Inputs.Unpack(packed)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
@ -200,7 +192,6 @@ func TestConstructor(t *testing.T) {
|
|||
if !reflect.DeepEqual(unpacked[0], big.NewInt(1)) {
|
||||
t.Error("Unable to pack/unpack from constructor")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(unpacked[1], big.NewInt(2)) {
|
||||
t.Error("Unable to pack/unpack from constructor")
|
||||
}
|
||||
|
|
@ -234,7 +225,6 @@ func TestTestNumbers(t *testing.T) {
|
|||
|
||||
i := new(int)
|
||||
*i = 1000
|
||||
|
||||
if _, err := abi.Pack("send", i); err == nil {
|
||||
t.Errorf("expected send( ptr ) to throw, requires *big.Int instead of *int")
|
||||
}
|
||||
|
|
@ -247,7 +237,6 @@ func TestTestNumbers(t *testing.T) {
|
|||
func TestMethodSignature(t *testing.T) {
|
||||
m := NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil)
|
||||
exp := "foo(string,string)"
|
||||
|
||||
if m.Sig != exp {
|
||||
t.Error("signature mismatch", exp, "!=", m.Sig)
|
||||
}
|
||||
|
|
@ -259,7 +248,6 @@ func TestMethodSignature(t *testing.T) {
|
|||
|
||||
m = NewMethod("foo", "foo", Function, "", false, false, []Argument{{"bar", Uint256, false}}, nil)
|
||||
exp = "foo(uint256)"
|
||||
|
||||
if m.Sig != exp {
|
||||
t.Error("signature mismatch", exp, "!=", m.Sig)
|
||||
}
|
||||
|
|
@ -279,7 +267,6 @@ func TestMethodSignature(t *testing.T) {
|
|||
})
|
||||
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)"
|
||||
|
||||
if m.Sig != exp {
|
||||
t.Error("signature mismatch", exp, "!=", m.Sig)
|
||||
}
|
||||
|
|
@ -287,12 +274,10 @@ func TestMethodSignature(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"}]`
|
||||
|
||||
abi, err := JSON(strings.NewReader(json))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
check := func(name string, expect string, method bool) {
|
||||
if method {
|
||||
if abi.Methods[name].Sig != expect {
|
||||
|
|
@ -312,12 +297,10 @@ func TestOverloadedMethodSignature(t *testing.T) {
|
|||
|
||||
func TestCustomErrors(t *testing.T) {
|
||||
json := `[{ "inputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ],"name": "MyError", "type": "error"} ]`
|
||||
|
||||
abi, err := JSON(strings.NewReader(json))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
check := func(name string, expect string) {
|
||||
if abi.Errors[name].Sig != expect {
|
||||
t.Fatalf("The signature of overloaded method mismatch, want %s, have %s", expect, abi.Methods[name].Sig)
|
||||
|
|
@ -341,7 +324,6 @@ func TestMultiPack(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(packed, sig) {
|
||||
t.Errorf("expected %x got %x", sig, packed)
|
||||
}
|
||||
|
|
@ -354,7 +336,6 @@ func ExampleJSON() {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
out, err := abi.Pack("isBar", common.HexToAddress("01"))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
@ -379,7 +360,6 @@ func TestInputVariableInputLength(t *testing.T) {
|
|||
|
||||
// test one string
|
||||
strin := "hello world"
|
||||
|
||||
strpack, err := abi.Pack("strOne", strin)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
@ -412,7 +392,6 @@ func TestInputVariableInputLength(t *testing.T) {
|
|||
// test two strings
|
||||
str1 := "hello"
|
||||
str2 := "world"
|
||||
|
||||
str2pack, err := abi.Pack("strTwo", str1, str2)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
@ -442,7 +421,6 @@ func TestInputVariableInputLength(t *testing.T) {
|
|||
|
||||
// test two strings, first > 32, second < 32
|
||||
str1 = strings.Repeat("a", 33)
|
||||
|
||||
str2pack, err = abi.Pack("strTwo", str1, str2)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
@ -468,7 +446,6 @@ func TestInputVariableInputLength(t *testing.T) {
|
|||
// test two strings, first > 32, second >32
|
||||
str1 = strings.Repeat("a", 33)
|
||||
str2 = strings.Repeat("a", 33)
|
||||
|
||||
str2pack, err = abi.Pack("strTwo", str1, str2)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
@ -506,7 +483,6 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
|||
// test string, fixed array uint256[2]
|
||||
strin := "hello world"
|
||||
arrin := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
|
||||
|
||||
fixedArrStrPack, err := abi.Pack("fixedArrStr", strin, arrin)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
@ -520,7 +496,6 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
|||
strvalue := common.RightPadBytes([]byte(strin), 32)
|
||||
arrinvalue1 := common.LeftPadBytes(arrin[0].Bytes(), 32)
|
||||
arrinvalue2 := common.LeftPadBytes(arrin[1].Bytes(), 32)
|
||||
|
||||
exp := append(offset, arrinvalue1...)
|
||||
exp = append(exp, arrinvalue2...)
|
||||
exp = append(exp, append(length, strvalue...)...)
|
||||
|
|
@ -534,7 +509,6 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
|||
// test byte array, fixed array uint256[2]
|
||||
bytesin := []byte(strin)
|
||||
arrin = [2]*big.Int{big.NewInt(1), big.NewInt(2)}
|
||||
|
||||
fixedArrBytesPack, err := abi.Pack("fixedArrBytes", bytesin, arrin)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
@ -548,7 +522,6 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
|||
strvalue = common.RightPadBytes([]byte(strin), 32)
|
||||
arrinvalue1 = common.LeftPadBytes(arrin[0].Bytes(), 32)
|
||||
arrinvalue2 = common.LeftPadBytes(arrin[1].Bytes(), 32)
|
||||
|
||||
exp = append(offset, arrinvalue1...)
|
||||
exp = append(exp, arrinvalue2...)
|
||||
exp = append(exp, append(length, strvalue...)...)
|
||||
|
|
@ -563,7 +536,6 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
|||
strin = "hello world"
|
||||
fixedarrin := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
|
||||
dynarrin := []*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}
|
||||
|
||||
mixedArrStrPack, err := abi.Pack("mixedArrStr", strin, fixedarrin, dynarrin)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
@ -584,7 +556,6 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
|||
dynarrinvalue1 := common.LeftPadBytes(dynarrin[0].Bytes(), 32)
|
||||
dynarrinvalue2 := common.LeftPadBytes(dynarrin[1].Bytes(), 32)
|
||||
dynarrinvalue3 := common.LeftPadBytes(dynarrin[2].Bytes(), 32)
|
||||
|
||||
exp = append(stroffset, fixedarrinvalue1...)
|
||||
exp = append(exp, fixedarrinvalue2...)
|
||||
exp = append(exp, dynarroffset...)
|
||||
|
|
@ -604,7 +575,6 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
|||
strin = "hello world"
|
||||
fixedarrin1 := [2]*big.Int{big.NewInt(1), big.NewInt(2)}
|
||||
fixedarrin2 := [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}
|
||||
|
||||
doubleFixedArrStrPack, err := abi.Pack("doubleFixedArrStr", strin, fixedarrin1, fixedarrin2)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
@ -621,7 +591,6 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
|||
fixedarrin2value1 := common.LeftPadBytes(fixedarrin2[0].Bytes(), 32)
|
||||
fixedarrin2value2 := common.LeftPadBytes(fixedarrin2[1].Bytes(), 32)
|
||||
fixedarrin2value3 := common.LeftPadBytes(fixedarrin2[2].Bytes(), 32)
|
||||
|
||||
exp = append(stroffset, fixedarrin1value1...)
|
||||
exp = append(exp, fixedarrin1value2...)
|
||||
exp = append(exp, fixedarrin2value1...)
|
||||
|
|
@ -640,7 +609,6 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
|||
fixedarrin1 = [2]*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)}
|
||||
|
||||
multipleMixedArrStrPack, err := abi.Pack("multipleMixedArrStr", strin, fixedarrin1, dynarrin, fixedarrin2)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
@ -662,7 +630,6 @@ func TestInputFixedArrayAndVariableInputLength(t *testing.T) {
|
|||
fixedarrin2value1 = common.LeftPadBytes(fixedarrin2[0].Bytes(), 32)
|
||||
fixedarrin2value2 = common.LeftPadBytes(fixedarrin2[1].Bytes(), 32)
|
||||
fixedarrin2value3 = common.LeftPadBytes(fixedarrin2[2].Bytes(), 32)
|
||||
|
||||
exp = append(stroffset, fixedarrin1value1...)
|
||||
exp = append(exp, fixedarrin1value2...)
|
||||
exp = append(exp, dynarroffset...)
|
||||
|
|
@ -735,25 +702,20 @@ func TestBareEvents(t *testing.T) {
|
|||
t.Errorf("could not found event %s", name)
|
||||
continue
|
||||
}
|
||||
|
||||
if got.Anonymous != exp.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) {
|
||||
t.Errorf("invalid number of args, want %d, got %d", len(exp.Args), len(got.Inputs))
|
||||
continue
|
||||
}
|
||||
|
||||
for i, arg := range exp.Args {
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
@ -762,7 +724,6 @@ func TestBareEvents(t *testing.T) {
|
|||
}
|
||||
|
||||
// TestUnpackEvent is based on this contract:
|
||||
//
|
||||
// contract T {
|
||||
// event received(address sender, uint amount, bytes memo);
|
||||
// event receivedAddr(address sender);
|
||||
|
|
@ -771,25 +732,20 @@ func TestBareEvents(t *testing.T) {
|
|||
// receivedAddr(msg.sender);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// 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]}
|
||||
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"}]`
|
||||
|
||||
abi, err := JSON(strings.NewReader(abiJSON))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
const hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
|
||||
|
||||
data, err := hex.DecodeString(hexdata)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(data)%32 == 0 {
|
||||
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
||||
}
|
||||
|
|
@ -799,7 +755,6 @@ func TestUnpackEvent(t *testing.T) {
|
|||
Amount *big.Int
|
||||
Memo []byte
|
||||
}
|
||||
|
||||
var ev ReceivedEvent
|
||||
|
||||
err = abi.UnpackIntoInterface(&ev, "received", data)
|
||||
|
|
@ -810,9 +765,7 @@ func TestUnpackEvent(t *testing.T) {
|
|||
type ReceivedAddrEvent struct {
|
||||
Sender common.Address
|
||||
}
|
||||
|
||||
var receivedAddrEv ReceivedAddrEvent
|
||||
|
||||
err = abi.UnpackIntoInterface(&receivedAddrEv, "receivedAddr", data)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
@ -821,19 +774,16 @@ func TestUnpackEvent(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"}]`
|
||||
|
||||
abi, err := JSON(strings.NewReader(abiJSON))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
const hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
|
||||
|
||||
data, err := hex.DecodeString(hexdata)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(data)%32 == 0 {
|
||||
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
||||
}
|
||||
|
|
@ -844,23 +794,18 @@ func TestUnpackEventIntoMap(t *testing.T) {
|
|||
"amount": big.NewInt(1),
|
||||
"memo": []byte{88},
|
||||
}
|
||||
|
||||
if err := abi.UnpackIntoMap(receivedMap, "received", data); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(receivedMap) != 3 {
|
||||
t.Error("unpacked `received` map expected to have length 3")
|
||||
}
|
||||
|
||||
if receivedMap["sender"] != expectedReceivedMap["sender"] {
|
||||
t.Error("unpacked `received` map does not match expected map")
|
||||
}
|
||||
|
||||
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
|
||||
t.Error("unpacked `received` map does not match expected map")
|
||||
}
|
||||
|
||||
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
|
||||
t.Error("unpacked `received` map does not match expected map")
|
||||
}
|
||||
|
|
@ -869,11 +814,9 @@ func TestUnpackEventIntoMap(t *testing.T) {
|
|||
if err = abi.UnpackIntoMap(receivedAddrMap, "receivedAddr", data); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(receivedAddrMap) != 1 {
|
||||
t.Error("unpacked `receivedAddr` map expected to have length 1")
|
||||
}
|
||||
|
||||
if receivedAddrMap["sender"] != expectedReceivedMap["sender"] {
|
||||
t.Error("unpacked `receivedAddr` map does not match expected map")
|
||||
}
|
||||
|
|
@ -881,19 +824,15 @@ func TestUnpackEventIntoMap(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"}]`
|
||||
|
||||
abi, err := JSON(strings.NewReader(abiJSON))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
const hexdata = `00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000015800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000158000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000001580000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000015800000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000158`
|
||||
|
||||
data, err := hex.DecodeString(hexdata)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(data)%32 != 0 {
|
||||
t.Errorf("len(data) is %d, want a multiple of 32", len(data))
|
||||
}
|
||||
|
|
@ -903,7 +842,6 @@ func TestUnpackMethodIntoMap(t *testing.T) {
|
|||
if err = abi.UnpackIntoMap(receiveMap, "receive", data); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(receiveMap) > 0 {
|
||||
t.Error("unpacked `receive` map expected to have length 0")
|
||||
}
|
||||
|
|
@ -913,11 +851,9 @@ func TestUnpackMethodIntoMap(t *testing.T) {
|
|||
if err = abi.UnpackIntoMap(sendMap, "send", data); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(sendMap) != 1 {
|
||||
t.Error("unpacked `send` map expected to have length 1")
|
||||
}
|
||||
|
||||
if sendMap["amount"].(*big.Int).Cmp(big.NewInt(1)) != 0 {
|
||||
t.Error("unpacked `send` map expected `amount` value of 1")
|
||||
}
|
||||
|
|
@ -927,11 +863,9 @@ func TestUnpackMethodIntoMap(t *testing.T) {
|
|||
if err = abi.UnpackIntoMap(getMap, "get", data); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(getMap) != 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}
|
||||
if !bytes.Equal(getMap["hash"].([]byte), expectedBytes) {
|
||||
t.Errorf("unpacked `get` map expected `hash` value of %v", expectedBytes)
|
||||
|
|
@ -941,23 +875,18 @@ func TestUnpackMethodIntoMap(t *testing.T) {
|
|||
func TestUnpackIntoMapNamingConflict(t *testing.T) {
|
||||
// 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"}]`
|
||||
|
||||
abi, err := JSON(strings.NewReader(abiJSON))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var hexdata = `00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
|
||||
|
||||
data, err := hex.DecodeString(hexdata)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(data)%32 == 0 {
|
||||
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
||||
}
|
||||
|
||||
getMap := map[string]interface{}{}
|
||||
if err = abi.UnpackIntoMap(getMap, "get", data); err == nil {
|
||||
t.Error("naming conflict between two methods; error expected")
|
||||
|
|
@ -965,23 +894,18 @@ func TestUnpackIntoMapNamingConflict(t *testing.T) {
|
|||
|
||||
// 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"}]`
|
||||
|
||||
abi, err = JSON(strings.NewReader(abiJSON))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hexdata = `000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158`
|
||||
|
||||
data, err = hex.DecodeString(hexdata)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(data)%32 == 0 {
|
||||
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
||||
}
|
||||
|
||||
receivedMap := map[string]interface{}{}
|
||||
if err = abi.UnpackIntoMap(receivedMap, "received", data); err != nil {
|
||||
t.Error("naming conflict between two events; no error expected")
|
||||
|
|
@ -989,54 +913,43 @@ func TestUnpackIntoMapNamingConflict(t *testing.T) {
|
|||
|
||||
// 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"}]`
|
||||
|
||||
abi, err = JSON(strings.NewReader(abiJSON))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(data)%32 == 0 {
|
||||
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
||||
}
|
||||
|
||||
if err = abi.UnpackIntoMap(receivedMap, "received", data); err == nil {
|
||||
t.Error("naming conflict between an event and a method; error expected")
|
||||
}
|
||||
|
||||
// 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"}]`
|
||||
|
||||
abi, err = JSON(strings.NewReader(abiJSON))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(data)%32 == 0 {
|
||||
t.Errorf("len(data) is %d, want a non-multiple of 32", len(data))
|
||||
}
|
||||
|
||||
expectedReceivedMap := map[string]interface{}{
|
||||
"sender": common.HexToAddress("0x376c47978271565f56DEB45495afa69E59c16Ab2"),
|
||||
"amount": big.NewInt(1),
|
||||
"memo": []byte{88},
|
||||
}
|
||||
|
||||
if err = abi.UnpackIntoMap(receivedMap, "Received", data); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if len(receivedMap) != 3 {
|
||||
t.Error("unpacked `received` map expected to have length 3")
|
||||
}
|
||||
|
||||
if receivedMap["sender"] != expectedReceivedMap["sender"] {
|
||||
t.Error("unpacked `received` map does not match expected map")
|
||||
}
|
||||
|
||||
if receivedMap["amount"].(*big.Int).Cmp(expectedReceivedMap["amount"].(*big.Int)) != 0 {
|
||||
t.Error("unpacked `received` map does not match expected map")
|
||||
}
|
||||
|
||||
if !bytes.Equal(receivedMap["memo"].([]byte), expectedReceivedMap["memo"].([]byte)) {
|
||||
t.Error("unpacked `received` map does not match expected map")
|
||||
}
|
||||
|
|
@ -1047,15 +960,12 @@ func TestABI_MethodById(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for name, m := range abi.Methods {
|
||||
a := fmt.Sprintf("%v", m)
|
||||
|
||||
m2, err := abi.MethodById(m.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to look up ABI method: %v", err)
|
||||
}
|
||||
|
||||
b := fmt.Sprintf("%v", m2)
|
||||
if a != b {
|
||||
t.Errorf("Method %v (id %x) not 'findable' by id in ABI", name, m.ID)
|
||||
|
|
@ -1069,11 +979,9 @@ func TestABI_MethodById(t *testing.T) {
|
|||
if _, err := abi.MethodById([]byte{0x00}); err == nil {
|
||||
t.Errorf("Expected error, too short to decode data")
|
||||
}
|
||||
|
||||
if _, err := abi.MethodById([]byte{}); err == nil {
|
||||
t.Errorf("Expected error, too short to decode data")
|
||||
}
|
||||
|
||||
if _, err := abi.MethodById(nil); err == nil {
|
||||
t.Errorf("Expected error, nil is short to decode data")
|
||||
}
|
||||
|
|
@ -1128,20 +1036,19 @@ func TestABI_EventById(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Failed to look up ABI method: %v, test #%d", err, testnum)
|
||||
}
|
||||
|
||||
if event == nil {
|
||||
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)
|
||||
}
|
||||
|
||||
unknowntopicID := crypto.Keccak256Hash([]byte("unknownEvent"))
|
||||
|
||||
unknownEvent, err := abi.EventByID(unknowntopicID)
|
||||
if err == nil {
|
||||
t.Errorf("EventByID should return an error if a topic is not found, test #%d", testnum)
|
||||
}
|
||||
|
||||
if unknownEvent != nil {
|
||||
t.Errorf("We should not find any event for topic %s, test #%d", unknowntopicID.Hex(), testnum)
|
||||
}
|
||||
|
|
@ -1152,24 +1059,19 @@ func TestABI_EventById(t *testing.T) {
|
|||
// conflict and that the second transfer method will be renamed transfer1.
|
||||
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"}]`
|
||||
|
||||
contractAbi, err := JSON(strings.NewReader(abiJSON))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, ok := contractAbi.Methods["transfer"]; !ok {
|
||||
t.Fatalf("Could not find original method")
|
||||
}
|
||||
|
||||
if _, ok := contractAbi.Methods["transfer0"]; !ok {
|
||||
t.Fatalf("Could not find duplicate method")
|
||||
}
|
||||
|
||||
if _, ok := contractAbi.Methods["transfer1"]; !ok {
|
||||
t.Fatalf("Could not find duplicate method")
|
||||
}
|
||||
|
||||
if _, ok := contractAbi.Methods["transfer2"]; ok {
|
||||
t.Fatalf("Should not have found extra method")
|
||||
}
|
||||
|
|
@ -1178,7 +1080,6 @@ func TestDoubleDuplicateMethodNames(t *testing.T) {
|
|||
// TestDoubleDuplicateEventNames checks that if send0 already exists, there won't be a name
|
||||
// conflict and that the second send event will be renamed send1.
|
||||
// The test runs the abi of the following contract.
|
||||
//
|
||||
// contract DuplicateEvent {
|
||||
// event send(uint256 a);
|
||||
// event send0();
|
||||
|
|
@ -1186,24 +1087,19 @@ func TestDoubleDuplicateMethodNames(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"}]`
|
||||
|
||||
contractAbi, err := JSON(strings.NewReader(abiJSON))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, ok := contractAbi.Events["send"]; !ok {
|
||||
t.Fatalf("Could not find original event")
|
||||
}
|
||||
|
||||
if _, ok := contractAbi.Events["send0"]; !ok {
|
||||
t.Fatalf("Could not find duplicate event")
|
||||
}
|
||||
|
||||
if _, ok := contractAbi.Events["send1"]; !ok {
|
||||
t.Fatalf("Could not find duplicate event")
|
||||
}
|
||||
|
||||
if _, ok := contractAbi.Events["send2"]; ok {
|
||||
t.Fatalf("Should not have found extra event")
|
||||
}
|
||||
|
|
@ -1212,13 +1108,11 @@ func TestDoubleDuplicateEventNames(t *testing.T) {
|
|||
// TestUnnamedEventParam checks that an event with unnamed parameters is
|
||||
// correctly handled.
|
||||
// The test runs the abi of the following contract.
|
||||
//
|
||||
// contract TestEvent {
|
||||
// event send(uint256, uint256);
|
||||
// }
|
||||
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"}]`
|
||||
|
||||
contractAbi, err := JSON(strings.NewReader(abiJSON))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -1228,11 +1122,9 @@ func TestUnnamedEventParam(t *testing.T) {
|
|||
if !ok {
|
||||
t.Fatalf("Could not find event")
|
||||
}
|
||||
|
||||
if event.Inputs[0].Name != "arg0" {
|
||||
t.Fatalf("Could not find input")
|
||||
}
|
||||
|
||||
if event.Inputs[1].Name != "arg1" {
|
||||
t.Fatalf("Could not find input")
|
||||
}
|
||||
|
|
@ -1250,7 +1142,6 @@ func TestUnpackRevert(t *testing.T) {
|
|||
{"08c379a1", "", errors.New("invalid data for unpacking")},
|
||||
{"08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d72657665727420726561736f6e00000000000000000000000000000000000000", "revert reason", nil},
|
||||
}
|
||||
|
||||
for index, c := range cases {
|
||||
t.Run(fmt.Sprintf("case %d", index), func(t *testing.T) {
|
||||
got, err := UnpackRevert(common.Hex2Bytes(c.input))
|
||||
|
|
@ -1258,14 +1149,11 @@ func TestUnpackRevert(t *testing.T) {
|
|||
if err == nil {
|
||||
t.Fatalf("Expected non-nil error")
|
||||
}
|
||||
|
||||
if err.Error() != c.expectErr.Error() {
|
||||
t.Fatalf("Expected error mismatch, want %v, got %v", c.expectErr, err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if c.expect != got {
|
||||
t.Fatalf("Output mismatch, want %v, got %v", c.expect, got)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package abi
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
|
@ -45,7 +44,6 @@ type ArgumentMarshaling struct {
|
|||
// UnmarshalJSON implements json.Unmarshaler interface.
|
||||
func (argument *Argument) UnmarshalJSON(data []byte) error {
|
||||
var arg ArgumentMarshaling
|
||||
|
||||
err := json.Unmarshal(data, &arg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("argument json err: %v", err)
|
||||
|
|
@ -55,7 +53,6 @@ func (argument *Argument) UnmarshalJSON(data []byte) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
argument.Name = arg.Name
|
||||
argument.Indexed = arg.Indexed
|
||||
|
||||
|
|
@ -65,13 +62,11 @@ func (argument *Argument) UnmarshalJSON(data []byte) error {
|
|||
// NonIndexed returns the arguments with indexed arguments filtered out.
|
||||
func (arguments Arguments) NonIndexed() Arguments {
|
||||
var ret []Argument
|
||||
|
||||
for _, arg := range arguments {
|
||||
if !arg.Indexed {
|
||||
ret = append(ret, arg)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
|
|
@ -83,13 +78,11 @@ func (arguments Arguments) isTuple() bool {
|
|||
// Unpack performs the operation hexdata -> Go format.
|
||||
func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) {
|
||||
if len(data) == 0 {
|
||||
if len(arguments.NonIndexed()) != 0 {
|
||||
return nil, errors.New("abi: attempting to unmarshall an empty string while arguments are expected")
|
||||
if len(arguments) != 0 {
|
||||
return nil, fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
|
||||
}
|
||||
|
||||
return make([]interface{}, 0), nil
|
||||
}
|
||||
|
||||
return arguments.UnpackValues(data)
|
||||
}
|
||||
|
||||
|
|
@ -97,26 +90,21 @@ func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) {
|
|||
func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error {
|
||||
// Make sure map is not nil
|
||||
if v == nil {
|
||||
return errors.New("abi: cannot unpack into a nil map")
|
||||
return fmt.Errorf("abi: cannot unpack into a nil map")
|
||||
}
|
||||
|
||||
if len(data) == 0 {
|
||||
if len(arguments.NonIndexed()) != 0 {
|
||||
return errors.New("abi: attempting to unmarshall an empty string while arguments are expected")
|
||||
if len(arguments) != 0 {
|
||||
return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected")
|
||||
}
|
||||
|
||||
return nil // Nothing to unmarshal, return
|
||||
}
|
||||
|
||||
marshalledValues, err := arguments.UnpackValues(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, arg := range arguments.NonIndexed() {
|
||||
v[arg.Name] = marshalledValues[i]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -126,19 +114,15 @@ func (arguments Arguments) Copy(v interface{}, values []interface{}) error {
|
|||
if reflect.Ptr != reflect.ValueOf(v).Kind() {
|
||||
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
|
||||
}
|
||||
|
||||
if len(values) == 0 {
|
||||
if len(arguments.NonIndexed()) != 0 {
|
||||
return errors.New("abi: attempting to copy no values while arguments are expected")
|
||||
if len(arguments) != 0 {
|
||||
return fmt.Errorf("abi: attempting to copy no values while %d arguments are expected", len(arguments))
|
||||
}
|
||||
|
||||
return nil // Nothing to copy, return
|
||||
}
|
||||
|
||||
if arguments.isTuple() {
|
||||
return arguments.copyTuple(v, values)
|
||||
}
|
||||
|
||||
return arguments.copyAtomic(v, values[0])
|
||||
}
|
||||
|
||||
|
|
@ -150,7 +134,6 @@ func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{
|
|||
if dst.Kind() == reflect.Struct {
|
||||
return set(dst.Field(0), src)
|
||||
}
|
||||
|
||||
return set(dst, src)
|
||||
}
|
||||
|
||||
|
|
@ -165,20 +148,16 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
|
|||
for i, arg := range nonIndexedArgs {
|
||||
argNames[i] = arg.Name
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
abi2struct, err := mapArgNamesToStructFields(argNames, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i, arg := range nonIndexedArgs {
|
||||
field := value.FieldByName(abi2struct[arg.Name])
|
||||
if !field.IsValid() {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
|
@ -187,7 +166,6 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
|
|||
if value.Len() < len(marshalledValues) {
|
||||
return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
|
||||
}
|
||||
|
||||
for i := range nonIndexedArgs {
|
||||
if err := set(value.Index(i), reflect.ValueOf(marshalledValues[i])); err != nil {
|
||||
return err
|
||||
|
|
@ -196,7 +174,6 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
|
|||
default:
|
||||
return fmt.Errorf("abi:[2] cannot unmarshal tuple in to %v", value.Type())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -206,14 +183,9 @@ func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface
|
|||
func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
|
||||
nonIndexedArgs := arguments.NonIndexed()
|
||||
retval := make([]interface{}, 0, len(nonIndexedArgs))
|
||||
|
||||
virtualArgs := 0
|
||||
for index, arg := range nonIndexedArgs {
|
||||
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) {
|
||||
// If we have a static array, like [3]uint256, these are coded as
|
||||
// just like uint256,uint256,uint256.
|
||||
|
|
@ -231,10 +203,11 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
|
|||
// coded as just like uint256,bool,uint256
|
||||
virtualArgs += getTypeSize(arg.Type)/32 - 1
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
retval = append(retval, marshalledValue)
|
||||
}
|
||||
|
||||
return retval, nil
|
||||
}
|
||||
|
||||
|
|
@ -260,9 +233,7 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
|
|||
for _, abiArg := range abiArgs {
|
||||
inputOffset += getTypeSize(abiArg.Type)
|
||||
}
|
||||
|
||||
var ret []byte
|
||||
|
||||
for i, a := range args {
|
||||
input := abiArgs[i]
|
||||
// pack the input
|
||||
|
|
@ -297,6 +268,5 @@ func ToCamelCase(input string) string {
|
|||
parts[i] = strings.ToUpper(s[:1]) + s[1:]
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(parts, "")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,9 +64,7 @@ func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
|
|||
// Deprecated: Use NewKeyStoreTransactorWithChainID instead.
|
||||
func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account) (*TransactOpts, error) {
|
||||
log.Warn("WARNING: NewKeyStoreTransactor has been deprecated in favour of NewTransactorWithChainID")
|
||||
|
||||
signer := types.HomesteadSigner{}
|
||||
|
||||
return &TransactOpts{
|
||||
From: account.Address,
|
||||
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
|
||||
|
|
@ -89,10 +87,8 @@ func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account
|
|||
// Deprecated: Use NewKeyedTransactorWithChainID instead.
|
||||
func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
|
||||
log.Warn("WARNING: NewKeyedTransactor has been deprecated in favour of NewKeyedTransactorWithChainID")
|
||||
|
||||
keyAddr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
signer := types.HomesteadSigner{}
|
||||
|
||||
return &TransactOpts{
|
||||
From: keyAddr,
|
||||
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
|
||||
|
|
@ -116,12 +112,10 @@ func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.I
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, err := keystore.DecryptKey(json, passphrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewKeyedTransactorWithChainID(key.PrivateKey, chainID)
|
||||
}
|
||||
|
||||
|
|
@ -131,9 +125,7 @@ func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accou
|
|||
if chainID == nil {
|
||||
return nil, ErrNoChainID
|
||||
}
|
||||
|
||||
signer := types.LatestSignerForChainID(chainID)
|
||||
|
||||
return &TransactOpts{
|
||||
From: account.Address,
|
||||
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
|
||||
|
|
@ -154,13 +146,10 @@ func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accou
|
|||
// from a single private key.
|
||||
func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*TransactOpts, error) {
|
||||
keyAddr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
|
||||
if chainID == nil {
|
||||
return nil, ErrNoChainID
|
||||
}
|
||||
|
||||
signer := types.LatestSignerForChainID(chainID)
|
||||
|
||||
return &TransactOpts{
|
||||
From: keyAddr,
|
||||
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ func (fb *filterBackend) GetBorBlockReceipt(ctx context.Context, hash common.Has
|
|||
if number == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
receipt := rawdb.ReadRawBorReceipt(fb.db, hash, *number)
|
||||
if receipt == nil {
|
||||
return nil, nil
|
||||
|
|
|
|||
|
|
@ -66,10 +66,8 @@ type SimulatedBackend struct {
|
|||
mu sync.Mutex
|
||||
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
|
||||
pendingReceipts types.Receipts // Currently receipts for the pending block
|
||||
|
||||
events *filters.EventSystem // for filtering log events live
|
||||
filterSystem *filters.FilterSystem // for filtering database logs
|
||||
events *filters.EventSystem // Event system for filtering log events live
|
||||
|
||||
config *params.ChainConfig
|
||||
}
|
||||
|
|
@ -78,28 +76,17 @@ type SimulatedBackend struct {
|
|||
// and uses a simulated blockchain for testing purposes.
|
||||
// A simulated backend always uses chainID 1337.
|
||||
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
||||
genesis := core.Genesis{
|
||||
Config: params.AllEthashProtocolChanges,
|
||||
GasLimit: gasLimit,
|
||||
Alloc: alloc,
|
||||
}
|
||||
blockchain, _ := core.NewBlockChain(database, nil, &genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
|
||||
genesis.MustCommit(database)
|
||||
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil, nil)
|
||||
|
||||
backend := &SimulatedBackend{
|
||||
database: database,
|
||||
blockchain: blockchain,
|
||||
config: genesis.Config,
|
||||
events: filters.NewEventSystem(&filterBackend{database, blockchain}, false),
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
backend.rollback(blockchain.CurrentBlock())
|
||||
return backend
|
||||
}
|
||||
|
||||
|
|
@ -118,21 +105,16 @@ func (b *SimulatedBackend) Close() error {
|
|||
|
||||
// Commit imports all the pending transactions as a single block and starts a
|
||||
// fresh new state.
|
||||
func (b *SimulatedBackend) Commit() common.Hash {
|
||||
func (b *SimulatedBackend) Commit() {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
blockHash := b.pendingBlock.Hash()
|
||||
|
||||
// Using the last inserted block here makes it possible to build on a side
|
||||
// chain after a fork.
|
||||
b.rollback(b.pendingBlock)
|
||||
|
||||
return blockHash
|
||||
}
|
||||
|
||||
// Rollback aborts all pending transactions, reverting to the last committed state.
|
||||
|
|
@ -140,10 +122,7 @@ func (b *SimulatedBackend) Rollback() {
|
|||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
header := b.blockchain.CurrentBlock()
|
||||
block := b.blockchain.GetBlock(header.Hash(), header.Number.Uint64())
|
||||
|
||||
b.rollback(block)
|
||||
b.rollback(b.blockchain.CurrentBlock())
|
||||
}
|
||||
|
||||
func (b *SimulatedBackend) rollback(parent *types.Block) {
|
||||
|
|
@ -172,28 +151,23 @@ func (b *SimulatedBackend) Fork(ctx context.Context, parent common.Hash) error {
|
|||
if len(b.pendingBlock.Transactions()) != 0 {
|
||||
return errors.New("pending block dirty")
|
||||
}
|
||||
|
||||
block, err := b.blockByHash(ctx, parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.rollback(block)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// stateByBlockNumber retrieves a state by a given blocknumber.
|
||||
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()
|
||||
}
|
||||
|
||||
block, err := b.blockByNumber(ctx, blockNumber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b.blockchain.StateAt(block.Root())
|
||||
}
|
||||
|
||||
|
|
@ -247,7 +221,6 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres
|
|||
}
|
||||
|
||||
val := stateDB.GetState(contract, key)
|
||||
|
||||
return val[:], nil
|
||||
}
|
||||
|
||||
|
|
@ -260,7 +233,6 @@ func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common
|
|||
if receipt == nil {
|
||||
return nil, ethereum.NotFound
|
||||
}
|
||||
|
||||
return receipt, nil
|
||||
}
|
||||
|
||||
|
|
@ -276,12 +248,10 @@ func (b *SimulatedBackend) TransactionByHash(ctx context.Context, txHash common.
|
|||
if tx != nil {
|
||||
return tx, true, nil
|
||||
}
|
||||
|
||||
tx, _, _, _ = rawdb.ReadTransaction(b.database, txHash)
|
||||
if tx != nil {
|
||||
return tx, false, nil
|
||||
}
|
||||
|
||||
return nil, false, ethereum.NotFound
|
||||
}
|
||||
|
||||
|
|
@ -320,7 +290,7 @@ func (b *SimulatedBackend) BlockByNumber(ctx context.Context, number *big.Int) (
|
|||
// (associated with its hash) if found without Lock.
|
||||
func (b *SimulatedBackend) blockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
|
||||
if number == nil || number.Cmp(b.pendingBlock.Number()) == 0 {
|
||||
return b.blockByHash(ctx, b.blockchain.CurrentBlock().Hash())
|
||||
return b.blockchain.CurrentBlock(), nil
|
||||
}
|
||||
|
||||
block := b.blockchain.GetBlockByNumber(uint64(number.Int64()))
|
||||
|
|
@ -416,11 +386,9 @@ func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Ad
|
|||
func newRevertError(result *core.ExecutionResult) *revertError {
|
||||
reason, errUnpack := abi.UnpackRevert(result.Revert())
|
||||
err := errors.New("execution reverted")
|
||||
|
||||
if errUnpack == nil {
|
||||
err = fmt.Errorf("execution reverted: %v", reason)
|
||||
}
|
||||
|
||||
return &revertError{
|
||||
error: err,
|
||||
reason: hexutil.Encode(result.Revert()),
|
||||
|
|
@ -450,15 +418,13 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
|
|||
b.mu.Lock()
|
||||
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
|
||||
}
|
||||
|
||||
stateDB, err := b.blockchain.State()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), stateDB)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -467,7 +433,6 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
|
|||
if len(res.Revert()) > 0 {
|
||||
return nil, newRevertError(res)
|
||||
}
|
||||
|
||||
return res.Return(), res.Err
|
||||
}
|
||||
|
||||
|
|
@ -477,7 +442,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
|
|||
defer b.mu.Unlock()
|
||||
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
|
||||
|
||||
res, err := b.callContract(ctx, call, b.pendingBlock.Header(), b.pendingState)
|
||||
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -485,7 +450,6 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
|
|||
if len(res.Revert()) > 0 {
|
||||
return nil, newRevertError(res)
|
||||
}
|
||||
|
||||
return res.Return(), res.Err
|
||||
}
|
||||
|
||||
|
|
@ -507,7 +471,6 @@ func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error
|
|||
if b.pendingBlock.Header().BaseFee != nil {
|
||||
return b.pendingBlock.Header().BaseFee, nil
|
||||
}
|
||||
|
||||
return big.NewInt(1), nil
|
||||
}
|
||||
|
||||
|
|
@ -529,7 +492,6 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
|||
hi uint64
|
||||
cap uint64
|
||||
)
|
||||
|
||||
if call.Gas >= params.TxGas {
|
||||
hi = call.Gas
|
||||
} else {
|
||||
|
|
@ -537,7 +499,6 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
|||
}
|
||||
// Normalize the max fee per gas the call is willing to spend.
|
||||
var feeCap *big.Int
|
||||
|
||||
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
|
||||
return 0, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
||||
} else if call.GasPrice != nil {
|
||||
|
|
@ -550,30 +511,24 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
|||
// Recap the highest gas allowance with account's balance.
|
||||
if feeCap.BitLen() != 0 {
|
||||
balance := b.pendingState.GetBalance(call.From) // from can't be nil
|
||||
|
||||
available := new(big.Int).Set(balance)
|
||||
if call.Value != nil {
|
||||
if call.Value.Cmp(available) >= 0 {
|
||||
return 0, core.ErrInsufficientFundsForTransfer
|
||||
return 0, errors.New("insufficient funds for transfer")
|
||||
}
|
||||
|
||||
available.Sub(available, call.Value)
|
||||
}
|
||||
|
||||
allowance := new(big.Int).Div(available, feeCap)
|
||||
if allowance.IsUint64() && hi > allowance.Uint64() {
|
||||
transfer := call.Value
|
||||
if transfer == nil {
|
||||
transfer = new(big.Int)
|
||||
}
|
||||
|
||||
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
|
||||
"sent", transfer, "feecap", feeCap, "fundable", allowance)
|
||||
|
||||
hi = allowance.Uint64()
|
||||
}
|
||||
}
|
||||
|
||||
cap = hi
|
||||
|
||||
// Create a helper to check if a gas allowance results in an executable transaction
|
||||
|
|
@ -581,17 +536,15 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
|||
call.Gas = gas
|
||||
|
||||
snapshot := b.pendingState.Snapshot()
|
||||
res, err := b.callContract(ctx, call, b.pendingBlock.Header(), b.pendingState)
|
||||
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
|
||||
b.pendingState.RevertToSnapshot(snapshot)
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, core.ErrIntrinsicGas) {
|
||||
return true, nil, nil // Special case, raise gas limit
|
||||
}
|
||||
|
||||
return true, nil, err // Bail out
|
||||
}
|
||||
|
||||
return res.Failed(), res, nil
|
||||
}
|
||||
// Execute the binary search and hone in on an executable gas limit
|
||||
|
|
@ -605,7 +558,6 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
|||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if failed {
|
||||
lo = mid
|
||||
} else {
|
||||
|
|
@ -618,38 +570,33 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
|
|||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if failed {
|
||||
if result != nil && result.Err != vm.ErrOutOfGas {
|
||||
if len(result.Revert()) > 0 {
|
||||
return 0, newRevertError(result)
|
||||
}
|
||||
|
||||
return 0, result.Err
|
||||
}
|
||||
// Otherwise, the specified gas cap is too low
|
||||
return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
|
||||
}
|
||||
}
|
||||
|
||||
return hi, nil
|
||||
}
|
||||
|
||||
// callContract implements common code between normal and pending contract calls.
|
||||
// state is modified during execution, make sure to copy it if necessary.
|
||||
func (b *SimulatedBackend) callContract(_ context.Context, call ethereum.CallMsg, header *types.Header, stateDB *state.StateDB) (*core.ExecutionResult, error) {
|
||||
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, stateDB *state.StateDB) (*core.ExecutionResult, error) {
|
||||
// Gas prices post 1559 need to be initialized
|
||||
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
|
||||
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
|
||||
}
|
||||
|
||||
head := b.blockchain.CurrentHeader()
|
||||
if !b.blockchain.Config().IsLondon(head.Number) {
|
||||
// If there's no basefee, then it must be a non-1559 execution
|
||||
if call.GasPrice == nil {
|
||||
call.GasPrice = new(big.Int)
|
||||
}
|
||||
|
||||
call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
|
||||
} else {
|
||||
// A basefee is provided, necessitating 1559-type execution
|
||||
|
|
@ -657,11 +604,10 @@ func (b *SimulatedBackend) callContract(_ context.Context, call ethereum.CallMsg
|
|||
// User specified the legacy gas field, convert to 1559 gas typing
|
||||
call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
|
||||
} else {
|
||||
// User specified 1559 gas fields (or none), use those
|
||||
// User specified 1559 gas feilds (or none), use those
|
||||
if call.GasFeeCap == nil {
|
||||
call.GasFeeCap = new(big.Int)
|
||||
}
|
||||
|
||||
if call.GasTipCap == nil {
|
||||
call.GasTipCap = new(big.Int)
|
||||
}
|
||||
|
|
@ -676,38 +622,23 @@ func (b *SimulatedBackend) callContract(_ context.Context, call ethereum.CallMsg
|
|||
if call.Gas == 0 {
|
||||
call.Gas = 50000000
|
||||
}
|
||||
|
||||
if call.Value == nil {
|
||||
call.Value = new(big.Int)
|
||||
}
|
||||
|
||||
// Set infinite balance to the fake caller account.
|
||||
from := stateDB.GetOrNewStateObject(call.From)
|
||||
from.SetBalance(math.MaxBig256)
|
||||
|
||||
// Execute the call.
|
||||
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,
|
||||
}
|
||||
msg := callMsg{call}
|
||||
|
||||
txContext := core.NewEVMTxContext(msg)
|
||||
evmContext := core.NewEVMBlockContext(block.Header(), b.blockchain, nil)
|
||||
// Create a new environment which holds all relevant information
|
||||
// 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})
|
||||
gasPool := new(core.GasPool).AddGas(math.MaxUint64)
|
||||
|
||||
// nolint : contextcheck
|
||||
return core.ApplyMessage(vmEnv, msg, gasPool, context.Background())
|
||||
return core.NewStateTransition(vmEnv, msg, gasPool).TransitionDb(context.Background())
|
||||
}
|
||||
|
||||
// SendTransaction updates the pending block to include the given transaction.
|
||||
|
|
@ -722,31 +653,25 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
|
|||
}
|
||||
// Check transaction validity
|
||||
signer := types.MakeSigner(b.blockchain.Config(), block.Number())
|
||||
|
||||
sender, err := types.Sender(signer, tx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid transaction: %v", err)
|
||||
}
|
||||
|
||||
nonce := b.pendingState.GetNonce(sender)
|
||||
if tx.Nonce() != nonce {
|
||||
return fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce)
|
||||
}
|
||||
// Include tx in chain
|
||||
// nolint : contextcheck
|
||||
blocks, receipts := core.GenerateChain(b.config, block, 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) {
|
||||
for _, tx := range b.pendingBlock.Transactions() {
|
||||
block.AddTxWithChain(b.blockchain, tx)
|
||||
}
|
||||
|
||||
block.AddTxWithChain(b.blockchain, tx)
|
||||
})
|
||||
stateDB, _ := b.blockchain.State()
|
||||
|
||||
b.pendingBlock = blocks[0]
|
||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
|
||||
b.pendingReceipts = receipts[0]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -758,32 +683,29 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.Filter
|
|||
var filter *filters.Filter
|
||||
if query.BlockHash != nil {
|
||||
// Block filter requested, construct a single-shot filter
|
||||
filter = b.filterSystem.NewBlockFilter(*query.BlockHash, query.Addresses, query.Topics)
|
||||
filter = filters.NewBlockFilter(&filterBackend{b.database, b.blockchain}, *query.BlockHash, query.Addresses, query.Topics)
|
||||
} else {
|
||||
// Initialize unset filter boundaries to run from genesis to chain head
|
||||
from := int64(0)
|
||||
if query.FromBlock != nil {
|
||||
from = query.FromBlock.Int64()
|
||||
}
|
||||
|
||||
to := int64(-1)
|
||||
if query.ToBlock != nil {
|
||||
to = query.ToBlock.Int64()
|
||||
}
|
||||
// Construct the range filter
|
||||
filter = b.filterSystem.NewRangeFilter(from, to, query.Addresses, query.Topics)
|
||||
filter = filters.NewRangeFilter(&filterBackend{b.database, b.blockchain}, from, to, query.Addresses, query.Topics)
|
||||
}
|
||||
// Run the filter and return all the logs
|
||||
logs, err := filter.Logs(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := make([]types.Log, len(logs))
|
||||
for i, nLog := range logs {
|
||||
res[i] = *nLog
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
|
|
@ -800,7 +722,6 @@ 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
|
||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
for {
|
||||
select {
|
||||
case logs := <-sink:
|
||||
|
|
@ -830,7 +751,6 @@ func (b *SimulatedBackend) SubscribeNewHead(ctx context.Context, ch chan<- *type
|
|||
|
||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
for {
|
||||
select {
|
||||
case head := <-sink:
|
||||
|
|
@ -859,13 +779,8 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
|
|||
if len(b.pendingBlock.Transactions()) != 0 {
|
||||
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, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||
block.OffsetTime(int64(adjustment.Seconds()))
|
||||
})
|
||||
stateDB, _ := b.blockchain.State()
|
||||
|
|
@ -881,65 +796,65 @@ func (b *SimulatedBackend) Blockchain() *core.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
|
||||
// taking bloom-bits acceleration structures into account.
|
||||
type filterBackend struct {
|
||||
db ethdb.Database
|
||||
bc *core.BlockChain
|
||||
backend *SimulatedBackend
|
||||
}
|
||||
|
||||
func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db }
|
||||
|
||||
func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") }
|
||||
|
||||
func (fb *filterBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
|
||||
// nolint : exhaustive
|
||||
switch number {
|
||||
case rpc.PendingBlockNumber:
|
||||
if block := fb.backend.pendingBlock; block != nil {
|
||||
return block.Header(), nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
case rpc.LatestBlockNumber:
|
||||
func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumber) (*types.Header, error) {
|
||||
if block == 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) {
|
||||
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) {
|
||||
number := rawdb.ReadHeaderNumber(fb.db, hash)
|
||||
if number == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil
|
||||
}
|
||||
|
||||
func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
|
||||
logs := rawdb.ReadLogs(fb.db, hash, number, fb.bc.Config())
|
||||
func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
|
||||
number := rawdb.ReadHeaderNumber(fb.db, hash)
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -969,14 +884,6 @@ func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.Matche
|
|||
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 {
|
||||
return event.NewSubscription(func(quit <-chan struct{}) error {
|
||||
<-quit
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ func TestSimulatedBackend(t *testing.T) {
|
|||
defer goleak.VerifyNone(t, leak.IgnoreList()...)
|
||||
|
||||
var gasLimit uint64 = 8000029
|
||||
|
||||
key, _ := crypto.GenerateKey() // nolint: gosec
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
genAlloc := make(core.GenesisAlloc)
|
||||
|
|
@ -60,7 +59,6 @@ func TestSimulatedBackend(t *testing.T) {
|
|||
if isPending {
|
||||
t.Fatal("transaction should not be pending")
|
||||
}
|
||||
|
||||
if err != ethereum.NotFound {
|
||||
t.Fatalf("err should be `ethereum.NotFound` but received %v", err)
|
||||
}
|
||||
|
|
@ -70,7 +68,6 @@ func TestSimulatedBackend(t *testing.T) {
|
|||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||
|
||||
code := `6060604052600a8060106000396000f360606040526008565b00`
|
||||
|
||||
var gas uint64 = 3000000
|
||||
tx := types.NewContractCreation(0, big.NewInt(0), gas, gasPrice, common.FromHex(code))
|
||||
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, key)
|
||||
|
|
@ -82,22 +79,18 @@ func TestSimulatedBackend(t *testing.T) {
|
|||
|
||||
txHash = tx.Hash()
|
||||
_, isPending, err = sim.TransactionByHash(context.Background(), txHash)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("error getting transaction with hash: %v", txHash.String())
|
||||
}
|
||||
|
||||
if !isPending {
|
||||
t.Fatal("transaction should have pending status")
|
||||
}
|
||||
|
||||
sim.Commit()
|
||||
|
||||
_, isPending, err = sim.TransactionByHash(context.Background(), txHash)
|
||||
if err != nil {
|
||||
t.Fatalf("error getting transaction with hash: %v", txHash.String())
|
||||
}
|
||||
|
||||
if isPending {
|
||||
t.Fatal("transaction should not have pending status")
|
||||
}
|
||||
|
|
@ -106,7 +99,6 @@ func TestSimulatedBackend(t *testing.T) {
|
|||
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
|
||||
// the following is based on this contract:
|
||||
//
|
||||
// contract T {
|
||||
// event received(address sender, uint amount, bytes memo);
|
||||
// event receivedAddr(address sender);
|
||||
|
|
@ -135,7 +127,6 @@ func simTestBackend(testAddr common.Address) *SimulatedBackend {
|
|||
func TestNewSimulatedBackend(t *testing.T) {
|
||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||
expectedBal := big.NewInt(10000000000000000)
|
||||
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
|
||||
|
|
@ -148,7 +139,6 @@ func TestNewSimulatedBackend(t *testing.T) {
|
|||
}
|
||||
|
||||
stateDB, _ := sim.blockchain.State()
|
||||
|
||||
bal := stateDB.GetBalance(testAddr)
|
||||
if bal.Cmp(expectedBal) != 0 {
|
||||
t.Errorf("expected balance for test address not received. expected: %v actual: %v", expectedBal, bal)
|
||||
|
|
@ -162,11 +152,9 @@ func TestAdjustTime(t *testing.T) {
|
|||
defer sim.Close()
|
||||
|
||||
prevTime := sim.pendingBlock.Time()
|
||||
|
||||
if err := sim.AdjustTime(time.Second); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
newTime := sim.pendingBlock.Time()
|
||||
|
||||
if newTime-prevTime != uint64(time.Second.Seconds()) {
|
||||
|
|
@ -183,41 +171,33 @@ func TestNewAdjustTimeFail(t *testing.T) {
|
|||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||
|
||||
tx := types.NewTransaction(0, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||
|
||||
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||
if err != nil {
|
||||
t.Errorf("could not sign tx: %v", err)
|
||||
}
|
||||
|
||||
sim.SendTransaction(context.Background(), signedTx)
|
||||
// AdjustTime should fail on non-empty block
|
||||
if err := sim.AdjustTime(time.Second); err == nil {
|
||||
t.Error("Expected adjust time to error on non-empty block")
|
||||
}
|
||||
|
||||
sim.Commit()
|
||||
|
||||
prevTime := sim.pendingBlock.Time()
|
||||
|
||||
if err := sim.AdjustTime(time.Minute); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
newTime := sim.pendingBlock.Time()
|
||||
if newTime-prevTime != uint64(time.Minute.Seconds()) {
|
||||
t.Errorf("adjusted time not equal to a minute. prev: %v, new: %v", prevTime, newTime)
|
||||
}
|
||||
// Put a transaction after adjusting time
|
||||
tx2 := types.NewTransaction(1, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||
|
||||
signedTx2, err := types.SignTx(tx2, types.HomesteadSigner{}, testKey)
|
||||
if err != nil {
|
||||
t.Errorf("could not sign tx: %v", err)
|
||||
}
|
||||
|
||||
sim.SendTransaction(context.Background(), signedTx2)
|
||||
sim.Commit()
|
||||
|
||||
newTime = sim.pendingBlock.Time()
|
||||
if newTime-prevTime >= uint64(time.Minute.Seconds()) {
|
||||
t.Errorf("time adjusted, but shouldn't be: prev: %v, new: %v", prevTime, newTime)
|
||||
|
|
@ -227,10 +207,8 @@ func TestNewAdjustTimeFail(t *testing.T) {
|
|||
func TestBalanceAt(t *testing.T) {
|
||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||
expectedBal := big.NewInt(10000000000000000)
|
||||
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
|
||||
bgCtx := context.Background()
|
||||
|
||||
bal, err := sim.BalanceAt(bgCtx, testAddr, nil)
|
||||
|
|
@ -248,14 +226,12 @@ func TestBlockByHash(t *testing.T) {
|
|||
core.GenesisAlloc{}, 10000000,
|
||||
)
|
||||
defer sim.Close()
|
||||
|
||||
bgCtx := context.Background()
|
||||
|
||||
block, err := sim.BlockByNumber(bgCtx, nil)
|
||||
if err != nil {
|
||||
t.Errorf("could not get recent block: %v", err)
|
||||
}
|
||||
|
||||
blockByHash, err := sim.BlockByHash(bgCtx, block.Hash())
|
||||
if err != nil {
|
||||
t.Errorf("could not get recent block: %v", err)
|
||||
|
|
@ -271,14 +247,12 @@ func TestBlockByNumber(t *testing.T) {
|
|||
core.GenesisAlloc{}, 10000000,
|
||||
)
|
||||
defer sim.Close()
|
||||
|
||||
bgCtx := context.Background()
|
||||
|
||||
block, err := sim.BlockByNumber(bgCtx, nil)
|
||||
if err != nil {
|
||||
t.Errorf("could not get recent block: %v", err)
|
||||
}
|
||||
|
||||
if block.NumberU64() != 0 {
|
||||
t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64())
|
||||
}
|
||||
|
|
@ -290,7 +264,6 @@ func TestBlockByNumber(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("could not get recent block: %v", err)
|
||||
}
|
||||
|
||||
if block.NumberU64() != 1 {
|
||||
t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64())
|
||||
}
|
||||
|
|
@ -299,7 +272,6 @@ func TestBlockByNumber(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("could not get block by number: %v", err)
|
||||
}
|
||||
|
||||
if blockByNumber.Hash() != block.Hash() {
|
||||
t.Errorf("did not get the same block with height of 1 as before")
|
||||
}
|
||||
|
|
@ -310,7 +282,6 @@ func TestNonceAt(t *testing.T) {
|
|||
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
|
||||
bgCtx := context.Background()
|
||||
|
||||
nonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(0))
|
||||
|
|
@ -327,7 +298,6 @@ func TestNonceAt(t *testing.T) {
|
|||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||
|
||||
tx := types.NewTransaction(nonce, testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||
|
||||
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||
if err != nil {
|
||||
t.Errorf("could not sign tx: %v", err)
|
||||
|
|
@ -338,7 +308,6 @@ func TestNonceAt(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("could not add tx to pending block: %v", err)
|
||||
}
|
||||
|
||||
sim.Commit()
|
||||
|
||||
newNonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(1))
|
||||
|
|
@ -356,7 +325,6 @@ func TestNonceAt(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("could not get nonce for test addr: %v", err)
|
||||
}
|
||||
|
||||
if newNonce != nonce+uint64(1) {
|
||||
t.Fatalf("received incorrect nonce. expected 1, got %v", nonce)
|
||||
}
|
||||
|
|
@ -367,7 +335,6 @@ func TestSendTransaction(t *testing.T) {
|
|||
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
|
||||
bgCtx := context.Background()
|
||||
|
||||
// create a signed transaction to send
|
||||
|
|
@ -375,7 +342,6 @@ func TestSendTransaction(t *testing.T) {
|
|||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||
|
||||
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||
|
||||
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||
if err != nil {
|
||||
t.Errorf("could not sign tx: %v", err)
|
||||
|
|
@ -386,7 +352,6 @@ func TestSendTransaction(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("could not add tx to pending block: %v", err)
|
||||
}
|
||||
|
||||
sim.Commit()
|
||||
|
||||
block, err := sim.BlockByNumber(bgCtx, big.NewInt(1))
|
||||
|
|
@ -408,7 +373,6 @@ func TestTransactionByHash(t *testing.T) {
|
|||
}, 10000000,
|
||||
)
|
||||
defer sim.Close()
|
||||
|
||||
bgCtx := context.Background()
|
||||
|
||||
// create a signed transaction to send
|
||||
|
|
@ -416,7 +380,6 @@ func TestTransactionByHash(t *testing.T) {
|
|||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||
|
||||
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||
|
||||
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||
if err != nil {
|
||||
t.Errorf("could not sign tx: %v", err)
|
||||
|
|
@ -433,11 +396,9 @@ func TestTransactionByHash(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err)
|
||||
}
|
||||
|
||||
if !pending {
|
||||
t.Errorf("expected transaction to be in pending state")
|
||||
}
|
||||
|
||||
if receivedTx.Hash() != signedTx.Hash() {
|
||||
t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash())
|
||||
}
|
||||
|
|
@ -449,11 +410,9 @@ func TestTransactionByHash(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err)
|
||||
}
|
||||
|
||||
if pending {
|
||||
t.Errorf("expected transaction to not be in pending state")
|
||||
}
|
||||
|
||||
if receivedTx.Hash() != signedTx.Hash() {
|
||||
t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash())
|
||||
}
|
||||
|
|
@ -468,10 +427,8 @@ func TestEstimateGas(t *testing.T) {
|
|||
function OOG() public { for (uint i = 0; ; i++) {}}
|
||||
function Assert() public { assert(false);}
|
||||
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 contractBin = "0x60806040523480156100115760006000fd5b50610017565b61016e806100266000396000f3fe60806040523480156100115760006000fd5b506004361061005c5760003560e01c806350f6fe3414610062578063aa8b1d301461006c578063b9b046f914610076578063d8b9839114610080578063e09fface1461008a5761005c565b60006000fd5b61006a610094565b005b6100746100ad565b005b61007e6100b5565b005b6100886100c2565b005b610092610135565b005b6000600090505b5b808060010191505061009b565b505b565b60006000fd5b565b600015156100bf57fe5b5b565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252600d8152602001807f72657665727420726561736f6e0000000000000000000000000000000000000081526020015060200191505060405180910390fd5b565b5b56fea2646970667358221220345bbcbb1a5ecf22b53a78eaebf95f8ee0eceff6d10d4b9643495084d2ec934a64736f6c63430006040033"
|
||||
|
||||
key, _ := crypto.GenerateKey()
|
||||
|
|
@ -555,18 +512,15 @@ func TestEstimateGas(t *testing.T) {
|
|||
Data: common.Hex2Bytes("e09fface"),
|
||||
}, 21275, nil, nil},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
got, err := sim.EstimateGas(context.Background(), c.message)
|
||||
if c.expectError != nil {
|
||||
if err == nil {
|
||||
t.Fatalf("Expect error, got nil")
|
||||
}
|
||||
|
||||
if c.expectError.Error() != err.Error() {
|
||||
t.Fatalf("Expect error, want %v, got %v", c.expectError, err)
|
||||
}
|
||||
|
||||
if c.expectData != nil {
|
||||
if err, ok := err.(*revertError); !ok {
|
||||
t.Fatalf("Expect revert error, got %T", err)
|
||||
|
|
@ -574,10 +528,8 @@ func TestEstimateGas(t *testing.T) {
|
|||
t.Fatalf("Error data mismatch, want %v, got %v", c.expectData, err.ErrorData())
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if got != c.expect {
|
||||
t.Fatalf("Gas estimation mismatch, want %d, got %d", c.expect, got)
|
||||
}
|
||||
|
|
@ -592,7 +544,6 @@ func TestEstimateGasWithPrice(t *testing.T) {
|
|||
defer sim.Close()
|
||||
|
||||
recipient := common.HexToAddress("deadbeef")
|
||||
|
||||
var cases = []struct {
|
||||
name string
|
||||
message ethereum.CallMsg
|
||||
|
|
@ -655,25 +606,20 @@ func TestEstimateGasWithPrice(t *testing.T) {
|
|||
Data: nil,
|
||||
}, params.TxGas, errors.New("gas required exceeds allowance (20999)")}, // 20999=(2.2ether-0.1ether-1wei)/(1e14)
|
||||
}
|
||||
|
||||
for i, c := range cases {
|
||||
got, err := sim.EstimateGas(context.Background(), c.message)
|
||||
if c.expectError != nil {
|
||||
if err == nil {
|
||||
t.Fatalf("test %d: expect error, got nil", i)
|
||||
}
|
||||
|
||||
if c.expectError.Error() != err.Error() {
|
||||
t.Fatalf("test %d: expect error, want %v, got %v", i, c.expectError, err)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if c.expectError == nil && err != nil {
|
||||
t.Fatalf("test %d: didn't expect error, got %v", i, err)
|
||||
}
|
||||
|
||||
if got != c.expect {
|
||||
t.Fatalf("test %d: gas estimation mismatch, want %d, got %d", i, c.expect, got)
|
||||
}
|
||||
|
|
@ -685,14 +631,12 @@ func TestHeaderByHash(t *testing.T) {
|
|||
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
|
||||
bgCtx := context.Background()
|
||||
|
||||
header, err := sim.HeaderByNumber(bgCtx, nil)
|
||||
if err != nil {
|
||||
t.Errorf("could not get recent block: %v", err)
|
||||
}
|
||||
|
||||
headerByHash, err := sim.HeaderByHash(bgCtx, header.Hash())
|
||||
if err != nil {
|
||||
t.Errorf("could not get recent block: %v", err)
|
||||
|
|
@ -708,17 +652,16 @@ func TestHeaderByNumber(t *testing.T) {
|
|||
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
|
||||
bgCtx := context.Background()
|
||||
|
||||
latestBlockHeader, err := sim.HeaderByNumber(bgCtx, nil)
|
||||
if err != nil {
|
||||
t.Errorf("could not get header for tip of chain: %v", err)
|
||||
}
|
||||
|
||||
if latestBlockHeader == nil {
|
||||
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())
|
||||
}
|
||||
|
||||
|
|
@ -737,7 +680,6 @@ func TestHeaderByNumber(t *testing.T) {
|
|||
if blockHeader.Hash() != latestBlockHeader.Hash() {
|
||||
t.Errorf("block header and latest block header are not the same")
|
||||
}
|
||||
|
||||
if blockHeader.Number.Int64() != int64(1) {
|
||||
t.Errorf("did not get blockheader for block 1. instead got block %v", blockHeader.Number.Int64())
|
||||
}
|
||||
|
|
@ -757,9 +699,7 @@ func TestTransactionCount(t *testing.T) {
|
|||
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
|
||||
bgCtx := context.Background()
|
||||
|
||||
currentBlock, err := sim.BlockByNumber(bgCtx, nil)
|
||||
if err != nil || currentBlock == nil {
|
||||
t.Error("could not get current block")
|
||||
|
|
@ -778,7 +718,6 @@ func TestTransactionCount(t *testing.T) {
|
|||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||
|
||||
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||
|
||||
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||
if err != nil {
|
||||
t.Errorf("could not sign tx: %v", err)
|
||||
|
|
@ -812,14 +751,12 @@ func TestTransactionInBlock(t *testing.T) {
|
|||
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
|
||||
bgCtx := context.Background()
|
||||
|
||||
transaction, err := sim.TransactionInBlock(bgCtx, sim.pendingBlock.Hash(), uint(0))
|
||||
if err == nil && err != errTransactionDoesNotExist {
|
||||
t.Errorf("expected a transaction does not exist error to be received but received %v", err)
|
||||
}
|
||||
|
||||
if transaction != nil {
|
||||
t.Errorf("expected transaction to be nil but received %v", transaction)
|
||||
}
|
||||
|
|
@ -838,7 +775,6 @@ func TestTransactionInBlock(t *testing.T) {
|
|||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||
|
||||
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||
|
||||
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||
if err != nil {
|
||||
t.Errorf("could not sign tx: %v", err)
|
||||
|
|
@ -861,7 +797,6 @@ func TestTransactionInBlock(t *testing.T) {
|
|||
if err == nil && err != errTransactionDoesNotExist {
|
||||
t.Errorf("expected a transaction does not exist error to be received but received %v", err)
|
||||
}
|
||||
|
||||
if transaction != nil {
|
||||
t.Errorf("expected transaction to be nil but received %v", transaction)
|
||||
}
|
||||
|
|
@ -881,7 +816,6 @@ func TestPendingNonceAt(t *testing.T) {
|
|||
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
|
||||
bgCtx := context.Background()
|
||||
|
||||
// expect pending nonce to be 0 since account has not been used
|
||||
|
|
@ -899,7 +833,6 @@ func TestPendingNonceAt(t *testing.T) {
|
|||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||
|
||||
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||
|
||||
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||
if err != nil {
|
||||
t.Errorf("could not sign tx: %v", err)
|
||||
|
|
@ -923,12 +856,10 @@ func TestPendingNonceAt(t *testing.T) {
|
|||
|
||||
// make a new transaction with a nonce of 1
|
||||
tx = types.NewTransaction(uint64(1), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||
|
||||
signedTx, err = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||
if err != nil {
|
||||
t.Errorf("could not sign tx: %v", err)
|
||||
}
|
||||
|
||||
err = sim.SendTransaction(bgCtx, signedTx)
|
||||
if err != nil {
|
||||
t.Errorf("could not send tx: %v", err)
|
||||
|
|
@ -950,7 +881,6 @@ func TestTransactionReceipt(t *testing.T) {
|
|||
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
|
||||
bgCtx := context.Background()
|
||||
|
||||
// create a signed transaction to send
|
||||
|
|
@ -958,7 +888,6 @@ func TestTransactionReceipt(t *testing.T) {
|
|||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||
|
||||
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, gasPrice, nil)
|
||||
|
||||
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||
if err != nil {
|
||||
t.Errorf("could not sign tx: %v", err)
|
||||
|
|
@ -969,7 +898,6 @@ func TestTransactionReceipt(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("could not add tx to pending block: %v", err)
|
||||
}
|
||||
|
||||
sim.Commit()
|
||||
|
||||
receipt, err := sim.TransactionReceipt(bgCtx, signedTx.Hash())
|
||||
|
|
@ -988,14 +916,11 @@ func TestSuggestGasPrice(t *testing.T) {
|
|||
10000000,
|
||||
)
|
||||
defer sim.Close()
|
||||
|
||||
bgCtx := context.Background()
|
||||
|
||||
gasPrice, err := sim.SuggestGasPrice(bgCtx)
|
||||
if err != nil {
|
||||
t.Errorf("could not get gas price: %v", err)
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
|
@ -1003,17 +928,13 @@ func TestSuggestGasPrice(t *testing.T) {
|
|||
|
||||
func TestPendingCodeAt(t *testing.T) {
|
||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
|
||||
bgCtx := context.Background()
|
||||
|
||||
code, err := sim.CodeAt(bgCtx, testAddr, nil)
|
||||
if err != nil {
|
||||
t.Errorf("could not get code at test addr: %v", err)
|
||||
}
|
||||
|
||||
if len(code) != 0 {
|
||||
t.Errorf("got code for account that does not have contract code")
|
||||
}
|
||||
|
|
@ -1022,9 +943,7 @@ func TestPendingCodeAt(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("could not get code at test addr: %v", err)
|
||||
}
|
||||
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
|
||||
|
||||
contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
|
||||
if err != nil {
|
||||
t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
|
||||
|
|
@ -1034,7 +953,6 @@ func TestPendingCodeAt(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("could not get code at test addr: %v", err)
|
||||
}
|
||||
|
||||
if len(code) == 0 {
|
||||
t.Errorf("did not get code for account that has contract code")
|
||||
}
|
||||
|
|
@ -1046,17 +964,13 @@ func TestPendingCodeAt(t *testing.T) {
|
|||
|
||||
func TestCodeAt(t *testing.T) {
|
||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
|
||||
bgCtx := context.Background()
|
||||
|
||||
code, err := sim.CodeAt(bgCtx, testAddr, nil)
|
||||
if err != nil {
|
||||
t.Errorf("could not get code at test addr: %v", err)
|
||||
}
|
||||
|
||||
if len(code) != 0 {
|
||||
t.Errorf("got code for account that does not have contract code")
|
||||
}
|
||||
|
|
@ -1065,21 +979,17 @@ func TestCodeAt(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("could not get code at test addr: %v", err)
|
||||
}
|
||||
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
|
||||
|
||||
contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
|
||||
if err != nil {
|
||||
t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
|
||||
}
|
||||
|
||||
sim.Commit()
|
||||
|
||||
code, err = sim.CodeAt(bgCtx, contractAddr, nil)
|
||||
if err != nil {
|
||||
t.Errorf("could not get code at test addr: %v", err)
|
||||
}
|
||||
|
||||
if len(code) == 0 {
|
||||
t.Errorf("did not get code for account that has contract code")
|
||||
}
|
||||
|
|
@ -1090,23 +1000,18 @@ func TestCodeAt(t *testing.T) {
|
|||
}
|
||||
|
||||
// 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]}
|
||||
func TestPendingAndCallContract(t *testing.T) {
|
||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
|
||||
bgCtx := context.Background()
|
||||
|
||||
parsed, err := abi.JSON(strings.NewReader(abiJSON))
|
||||
if err != nil {
|
||||
t.Errorf("could not get code at test addr: %v", err)
|
||||
}
|
||||
|
||||
contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
|
||||
|
||||
addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(abiBin), sim)
|
||||
if err != nil {
|
||||
t.Errorf("could not deploy contract: %v", err)
|
||||
|
|
@ -1126,7 +1031,6 @@ func TestPendingAndCallContract(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("could not call receive method on contract: %v", err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
t.Errorf("result of contract call was empty: %v", res)
|
||||
}
|
||||
|
|
@ -1147,7 +1051,6 @@ func TestPendingAndCallContract(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("could not call receive method on contract: %v", err)
|
||||
}
|
||||
|
||||
if len(res) == 0 {
|
||||
t.Errorf("result of contract call was empty: %v", res)
|
||||
}
|
||||
|
|
@ -1184,10 +1087,8 @@ contract Reverter {
|
|||
}*/
|
||||
func TestCallContractRevert(t *testing.T) {
|
||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
|
||||
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"}]`
|
||||
|
|
@ -1197,9 +1098,7 @@ func TestCallContractRevert(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("could not get code at test addr: %v", err)
|
||||
}
|
||||
|
||||
contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
|
||||
|
||||
addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(reverterBin), sim)
|
||||
if err != nil {
|
||||
t.Errorf("could not deploy contract: %v", err)
|
||||
|
|
@ -1238,17 +1137,14 @@ func TestCallContractRevert(t *testing.T) {
|
|||
if err == nil {
|
||||
t.Errorf("call to %v was not reverted", key)
|
||||
}
|
||||
|
||||
if res != nil {
|
||||
t.Errorf("result from %v was not nil: %v", key, res)
|
||||
}
|
||||
|
||||
if val != nil {
|
||||
rerr, ok := err.(*revertError)
|
||||
if !ok {
|
||||
t.Errorf("expect revert error")
|
||||
}
|
||||
|
||||
if rerr.Error() != "execution reverted: "+val.(string) {
|
||||
t.Errorf("error was malformed: got %v want %v", rerr.Error(), val)
|
||||
}
|
||||
|
|
@ -1259,21 +1155,17 @@ func TestCallContractRevert(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
input, err := parsed.Pack("noRevert")
|
||||
if err != nil {
|
||||
t.Errorf("could not pack noRevert function on contract: %v", err)
|
||||
}
|
||||
|
||||
res, err := cl(input)
|
||||
if err != nil {
|
||||
t.Error("call to noRevert was reverted")
|
||||
}
|
||||
|
||||
if res == nil {
|
||||
t.Errorf("result from noRevert was nil")
|
||||
}
|
||||
|
||||
sim.Commit()
|
||||
}
|
||||
}
|
||||
|
|
@ -1290,7 +1182,6 @@ func TestCallContractRevert(t *testing.T) {
|
|||
// having a chain length of just n+1 means that a reorg occurred.
|
||||
func TestFork(t *testing.T) {
|
||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
// 1.
|
||||
|
|
@ -1301,7 +1192,7 @@ func TestFork(t *testing.T) {
|
|||
sim.Commit()
|
||||
}
|
||||
// 3.
|
||||
if sim.blockchain.CurrentBlock().Number.Uint64() != uint64(n) {
|
||||
if sim.blockchain.CurrentBlock().NumberU64() != uint64(n) {
|
||||
t.Error("wrong chain length")
|
||||
}
|
||||
// 4.
|
||||
|
|
@ -1311,7 +1202,7 @@ func TestFork(t *testing.T) {
|
|||
sim.Commit()
|
||||
}
|
||||
// 6.
|
||||
if sim.blockchain.CurrentBlock().Number.Uint64() != uint64(n+1) {
|
||||
if sim.blockchain.CurrentBlock().NumberU64() != uint64(n+1) {
|
||||
t.Error("wrong chain length")
|
||||
}
|
||||
}
|
||||
|
|
@ -1344,18 +1235,15 @@ const callableBin = "6080604052348015600f57600080fd5b5060998061001e6000396000f3f
|
|||
// 10. Check that the event was reborn.
|
||||
func TestForkLogsReborn(t *testing.T) {
|
||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
// 1.
|
||||
parsed, _ := abi.JSON(strings.NewReader(callableAbi))
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
|
||||
|
||||
_, _, contract, err := bind.DeployContract(auth, parsed, common.FromHex(callableBin), sim)
|
||||
if err != nil {
|
||||
t.Errorf("deploying contract: %v", err)
|
||||
}
|
||||
|
||||
sim.Commit()
|
||||
// 2.
|
||||
logs, sub, err := contract.WatchLogs(nil, "Called")
|
||||
|
|
@ -1370,14 +1258,12 @@ func TestForkLogsReborn(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("transacting: %v", err)
|
||||
}
|
||||
|
||||
sim.Commit()
|
||||
// 5.
|
||||
log := <-logs
|
||||
if log.TxHash != tx.Hash() {
|
||||
t.Error("wrong event tx hash")
|
||||
}
|
||||
|
||||
if log.Removed {
|
||||
t.Error("Event should be included")
|
||||
}
|
||||
|
|
@ -1393,7 +1279,6 @@ func TestForkLogsReborn(t *testing.T) {
|
|||
if log.TxHash != tx.Hash() {
|
||||
t.Error("wrong event tx hash")
|
||||
}
|
||||
|
||||
if !log.Removed {
|
||||
t.Error("Event should be removed")
|
||||
}
|
||||
|
|
@ -1401,14 +1286,12 @@ func TestForkLogsReborn(t *testing.T) {
|
|||
if err := sim.SendTransaction(context.Background(), tx); err != nil {
|
||||
t.Errorf("sending transaction: %v", err)
|
||||
}
|
||||
|
||||
sim.Commit()
|
||||
// 10.
|
||||
log = <-logs
|
||||
if log.TxHash != tx.Hash() {
|
||||
t.Error("wrong event tx hash")
|
||||
}
|
||||
|
||||
if log.Removed {
|
||||
t.Error("Event should be included")
|
||||
}
|
||||
|
|
@ -1425,7 +1308,6 @@ func TestForkLogsReborn(t *testing.T) {
|
|||
// 6. Check that the TX is now included in block 2.
|
||||
func TestForkResendTx(t *testing.T) {
|
||||
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
|
||||
|
||||
sim := simTestBackend(testAddr)
|
||||
defer sim.Close()
|
||||
// 1.
|
||||
|
|
@ -1449,11 +1331,9 @@ func TestForkResendTx(t *testing.T) {
|
|||
}
|
||||
// 5.
|
||||
sim.Commit()
|
||||
|
||||
if err := sim.SendTransaction(context.Background(), tx); err != nil {
|
||||
t.Errorf("sending transaction: %v", err)
|
||||
}
|
||||
|
||||
sim.Commit()
|
||||
// 6.
|
||||
receipt, _ = sim.TransactionReceipt(context.Background(), tx.Hash())
|
||||
|
|
@ -1461,69 +1341,3 @@ func TestForkResendTx(t *testing.T) {
|
|||
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,13 +32,6 @@ import (
|
|||
"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
|
||||
// sign the transaction before submission.
|
||||
type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, error)
|
||||
|
|
@ -97,17 +90,14 @@ type MetaData struct {
|
|||
func (m *MetaData) GetAbi() (*abi.ABI, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.ab != nil {
|
||||
return m.ab, nil
|
||||
}
|
||||
|
||||
if parsed, err := abi.JSON(strings.NewReader(m.ABI)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
m.ab = &parsed
|
||||
}
|
||||
|
||||
return m.ab, nil
|
||||
}
|
||||
|
||||
|
|
@ -144,14 +134,11 @@ func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend Co
|
|||
if err != nil {
|
||||
return common.Address{}, nil, nil, err
|
||||
}
|
||||
|
||||
tx, err := c.transact(opts, nil, append(bytecode, input...))
|
||||
if err != nil {
|
||||
return common.Address{}, nil, nil, err
|
||||
}
|
||||
|
||||
c.address = crypto.CreateAddress(opts.From, tx.Nonce())
|
||||
|
||||
return c.address, tx, c, nil
|
||||
}
|
||||
|
||||
|
|
@ -164,7 +151,6 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
|
|||
if opts == nil {
|
||||
opts = new(CallOpts)
|
||||
}
|
||||
|
||||
if results == nil {
|
||||
results = new([]interface{})
|
||||
}
|
||||
|
|
@ -173,26 +159,19 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
msg = ethereum.CallMsg{From: opts.From, To: &c.address, Data: input}
|
||||
ctx = ensureContext(opts.Context)
|
||||
code []byte
|
||||
output []byte
|
||||
)
|
||||
|
||||
if opts.Pending {
|
||||
pb, ok := c.caller.(PendingContractCaller)
|
||||
if !ok {
|
||||
return ErrNoPendingState
|
||||
}
|
||||
|
||||
output, err = pb.PendingCallContract(ctx, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(output) == 0 {
|
||||
if err == nil && len(output) == 0 {
|
||||
// Make sure we have a contract to operate on, and bail out otherwise.
|
||||
if code, err = pb.PendingCodeAt(ctx, c.address); err != nil {
|
||||
return err
|
||||
|
|
@ -205,7 +184,6 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(output) == 0 {
|
||||
// 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 {
|
||||
|
|
@ -219,12 +197,9 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri
|
|||
if len(*results) == 0 {
|
||||
res, err := c.abi.Unpack(method, output)
|
||||
*results = res
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
res := *results
|
||||
|
||||
return c.abi.UnpackIntoInterface(res[0], method, output)
|
||||
}
|
||||
|
||||
|
|
@ -269,7 +244,6 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gasTipCap = tip
|
||||
}
|
||||
// Estimate FeeCap
|
||||
|
|
@ -277,19 +251,16 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add
|
|||
if gasFeeCap == nil {
|
||||
gasFeeCap = new(big.Int).Add(
|
||||
gasTipCap,
|
||||
new(big.Int).Mul(head.BaseFee, big.NewInt(basefeeWiggleMultiplier)),
|
||||
new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
|
||||
)
|
||||
}
|
||||
|
||||
if gasFeeCap.Cmp(gasTipCap) < 0 {
|
||||
return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", gasFeeCap, gasTipCap)
|
||||
}
|
||||
// Estimate GasLimit
|
||||
gasLimit := opts.GasLimit
|
||||
|
||||
if opts.GasLimit == 0 {
|
||||
var err error
|
||||
|
||||
gasLimit, err = c.estimateGasLimit(opts, contract, input, nil, gasTipCap, gasFeeCap, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -300,7 +271,6 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
baseTx := &types.DynamicFeeTx{
|
||||
To: contract,
|
||||
Nonce: nonce,
|
||||
|
|
@ -310,7 +280,6 @@ func (c *BoundContract) createDynamicTx(opts *TransactOpts, contract *common.Add
|
|||
Value: value,
|
||||
Data: input,
|
||||
}
|
||||
|
||||
return types.NewTx(baseTx), nil
|
||||
}
|
||||
|
||||
|
|
@ -330,15 +299,12 @@ func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Addr
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gasPrice = price
|
||||
}
|
||||
// Estimate GasLimit
|
||||
gasLimit := opts.GasLimit
|
||||
|
||||
if opts.GasLimit == 0 {
|
||||
var err error
|
||||
|
||||
gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, nil, nil, value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -349,7 +315,6 @@ func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Addr
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
baseTx := &types.LegacyTx{
|
||||
To: contract,
|
||||
Nonce: nonce,
|
||||
|
|
@ -358,7 +323,6 @@ func (c *BoundContract) createLegacyTx(opts *TransactOpts, contract *common.Addr
|
|||
Value: value,
|
||||
Data: input,
|
||||
}
|
||||
|
||||
return types.NewTx(baseTx), nil
|
||||
}
|
||||
|
||||
|
|
@ -371,7 +335,6 @@ func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Ad
|
|||
return 0, ErrNoCode
|
||||
}
|
||||
}
|
||||
|
||||
msg := ethereum.CallMsg{
|
||||
From: opts.From,
|
||||
To: contract,
|
||||
|
|
@ -381,7 +344,6 @@ func (c *BoundContract) estimateGasLimit(opts *TransactOpts, contract *common.Ad
|
|||
Value: value,
|
||||
Data: input,
|
||||
}
|
||||
|
||||
return c.transactor.EstimateGas(ensureContext(opts.Context), msg)
|
||||
}
|
||||
|
||||
|
|
@ -404,11 +366,8 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
|||
rawTx *types.Transaction
|
||||
err error
|
||||
)
|
||||
|
||||
if opts.GasPrice != nil {
|
||||
rawTx, err = c.createLegacyTx(opts, contract, input)
|
||||
} else if opts.GasFeeCap != nil && opts.GasTipCap != nil {
|
||||
rawTx, err = c.createDynamicTx(opts, contract, input, nil)
|
||||
} else {
|
||||
// Only query for basefee if gasPrice not specified
|
||||
if head, errHead := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil); errHead != nil {
|
||||
|
|
@ -420,7 +379,6 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
|||
rawTx, err = c.createLegacyTx(opts, contract, input)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -428,20 +386,16 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
|||
if opts.Signer == nil {
|
||||
return nil, errors.New("no signer to authorize the transaction with")
|
||||
}
|
||||
|
||||
signedTx, err := opts.Signer(opts.From, rawTx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if opts.NoSend {
|
||||
return signedTx, nil
|
||||
}
|
||||
|
||||
if err := c.transactor.SendTransaction(ensureContext(opts.Context), signedTx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return signedTx, nil
|
||||
}
|
||||
|
||||
|
|
@ -477,7 +431,6 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
|
|||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
sub, err := event.NewSubscription(func(quit <-chan struct{}) error {
|
||||
for _, log := range buff {
|
||||
select {
|
||||
|
|
@ -486,14 +439,12 @@ func (c *BoundContract) FilterLogs(opts *FilterOpts, name string, query ...[]int
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}), nil
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return logs, sub, nil
|
||||
}
|
||||
|
||||
|
|
@ -521,68 +472,48 @@ func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]inter
|
|||
if opts.Start != nil {
|
||||
config.FromBlock = new(big.Int).SetUint64(*opts.Start)
|
||||
}
|
||||
|
||||
sub, err := c.filterer.SubscribeFilterLogs(ensureContext(opts.Context), config, logs)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return logs, sub, nil
|
||||
}
|
||||
|
||||
// UnpackLog unpacks a retrieved log into the provided output structure.
|
||||
func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error {
|
||||
// Anonymous events are not supported.
|
||||
if len(log.Topics) == 0 {
|
||||
return errNoEventSignature
|
||||
}
|
||||
|
||||
if log.Topics[0] != c.abi.Events[event].ID {
|
||||
return errEventSignatureMismatch
|
||||
return fmt.Errorf("event signature mismatch")
|
||||
}
|
||||
|
||||
if len(log.Data) > 0 {
|
||||
if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var indexed abi.Arguments
|
||||
|
||||
for _, arg := range c.abi.Events[event].Inputs {
|
||||
if arg.Indexed {
|
||||
indexed = append(indexed, arg)
|
||||
}
|
||||
}
|
||||
|
||||
return abi.ParseTopics(out, indexed, log.Topics[1:])
|
||||
}
|
||||
|
||||
// UnpackLogIntoMap unpacks a retrieved log into the provided map.
|
||||
func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log types.Log) error {
|
||||
// Anonymous events are not supported.
|
||||
if len(log.Topics) == 0 {
|
||||
return errNoEventSignature
|
||||
}
|
||||
|
||||
if log.Topics[0] != c.abi.Events[event].ID {
|
||||
return errEventSignatureMismatch
|
||||
return fmt.Errorf("event signature mismatch")
|
||||
}
|
||||
|
||||
if len(log.Data) > 0 {
|
||||
if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var indexed abi.Arguments
|
||||
|
||||
for _, arg := range c.abi.Events[event].Inputs {
|
||||
if arg.Indexed {
|
||||
indexed = append(indexed, arg)
|
||||
}
|
||||
}
|
||||
|
||||
return abi.ParseTopicsIntoMap(out, indexed, log.Topics[1:])
|
||||
}
|
||||
|
||||
|
|
@ -592,6 +523,5 @@ func ensureContext(ctx context.Context) context.Context {
|
|||
if ctx == nil {
|
||||
return context.Background()
|
||||
}
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package bind_test
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
|
@ -78,50 +77,32 @@ func (mt *mockTransactor) SendTransaction(ctx context.Context, tx *types.Transac
|
|||
type mockCaller struct {
|
||||
codeAtBlockNumber *big.Int
|
||||
callContractBlockNumber *big.Int
|
||||
callContractBytes []byte
|
||||
callContractErr error
|
||||
codeAtBytes []byte
|
||||
codeAtErr error
|
||||
pendingCodeAtCalled bool
|
||||
pendingCallContractCalled bool
|
||||
}
|
||||
|
||||
func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||
mc.codeAtBlockNumber = blockNumber
|
||||
return mc.codeAtBytes, mc.codeAtErr
|
||||
return []byte{1, 2, 3}, nil
|
||||
}
|
||||
|
||||
func (mc *mockCaller) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
||||
mc.callContractBlockNumber = blockNumber
|
||||
return mc.callContractBytes, mc.callContractErr
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
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) {
|
||||
func (mc *mockCaller) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
|
||||
mc.pendingCodeAtCalled = true
|
||||
return mc.pendingCodeAtBytes, mc.pendingCodeAtErr
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (mc *mockPendingCaller) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
|
||||
func (mc *mockCaller) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
|
||||
mc.pendingCallContractCalled = true
|
||||
return mc.pendingCallContractBytes, mc.pendingCallContractErr
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func TestPassingBlockNumber(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
mc := &mockPendingCaller{
|
||||
mockCaller: &mockCaller{
|
||||
codeAtBytes: []byte{1, 2, 3},
|
||||
},
|
||||
}
|
||||
mc := &mockCaller{}
|
||||
|
||||
bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
|
||||
Methods: map[string]abi.Method{
|
||||
|
|
@ -188,33 +169,11 @@ func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
|
|||
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) {
|
||||
sliceBytes, err := rlp.EncodeToBytes([]string{"name1", "name2", "name3", "name4"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hash := crypto.Keccak256Hash(sliceBytes)
|
||||
topics := []common.Hash{
|
||||
crypto.Keccak256Hash([]byte("received(string[],address,uint256,bytes)")),
|
||||
|
|
@ -240,7 +199,6 @@ func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hash := crypto.Keccak256Hash(arrBytes)
|
||||
topics := []common.Hash{
|
||||
crypto.Keccak256Hash([]byte("received(address[2],address,uint256,bytes)")),
|
||||
|
|
@ -267,9 +225,7 @@ func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
|
|||
hash := crypto.Keccak256Hash([]byte("mockFunction(address,uint)"))
|
||||
functionSelector := hash[:4]
|
||||
functionTyBytes := append(addrBytes, functionSelector...)
|
||||
|
||||
var functionTy [24]byte
|
||||
|
||||
copy(functionTy[:], functionTyBytes[0:24])
|
||||
topics := []common.Hash{
|
||||
crypto.Keccak256Hash([]byte("received(function,address,uint256,bytes)")),
|
||||
|
|
@ -365,7 +321,6 @@ func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]in
|
|||
if 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 {
|
||||
if !reflect.DeepEqual(elem, received[name]) {
|
||||
t.Errorf("field %v does not match expected, want %v, got %v", name, elem, received[name])
|
||||
|
|
@ -386,150 +341,3 @@ func newMockLog(topics []common.Hash, txHash common.Hash) types.Log {
|
|||
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,6 +22,7 @@ package bind
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"regexp"
|
||||
|
|
@ -38,45 +39,10 @@ type Lang int
|
|||
|
||||
const (
|
||||
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
|
||||
// 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
|
||||
|
|
@ -92,7 +58,6 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
|||
// isLib is the map used to flag each encountered library as such
|
||||
isLib = make(map[string]struct{})
|
||||
)
|
||||
|
||||
for i := 0; i < len(types); i++ {
|
||||
// Parse the actual ABI to generate the binding for
|
||||
evmABI, err := abi.JSON(strings.NewReader(abis[i]))
|
||||
|
|
@ -104,7 +69,6 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
|||
if unicode.IsSpace(r) {
|
||||
return -1
|
||||
}
|
||||
|
||||
return r
|
||||
}, abis[i])
|
||||
|
||||
|
|
@ -135,41 +99,32 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
|||
// Normalize the method for capital cases and non-anonymous inputs/outputs
|
||||
normalized := original
|
||||
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
|
||||
|
||||
// Ensure there is no duplicated identifier
|
||||
var identifiers = callIdentifiers
|
||||
if !original.IsConstant() {
|
||||
identifiers = transactIdentifiers
|
||||
}
|
||||
|
||||
if identifiers[normalizedName] {
|
||||
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
|
||||
}
|
||||
|
||||
identifiers[normalizedName] = true
|
||||
|
||||
normalized.Name = normalizedName
|
||||
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
|
||||
copy(normalized.Inputs, original.Inputs)
|
||||
|
||||
for j, input := range normalized.Inputs {
|
||||
if input.Name == "" || isKeyWord(input.Name) {
|
||||
if input.Name == "" {
|
||||
normalized.Inputs[j].Name = fmt.Sprintf("arg%d", j)
|
||||
}
|
||||
|
||||
if hasStruct(input.Type) {
|
||||
bindStructType[lang](input.Type, structs)
|
||||
}
|
||||
}
|
||||
|
||||
normalized.Outputs = make([]abi.Argument, len(original.Outputs))
|
||||
copy(normalized.Outputs, original.Outputs)
|
||||
|
||||
for j, output := range normalized.Outputs {
|
||||
if output.Name != "" {
|
||||
normalized.Outputs[j].Name = capitalise(output.Name)
|
||||
}
|
||||
|
||||
if hasStruct(output.Type) {
|
||||
bindStructType[lang](output.Type, structs)
|
||||
}
|
||||
|
|
@ -181,7 +136,6 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
|||
transacts[original.Name] = &tmplMethod{Original: original, Normalized: normalized, Structured: structured(original.Outputs)}
|
||||
}
|
||||
}
|
||||
|
||||
for _, original := range evmABI.Events {
|
||||
// Skip anonymous events as they don't support explicit filtering
|
||||
if original.Anonymous {
|
||||
|
|
@ -195,29 +149,15 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
|||
if eventIdentifiers[normalizedName] {
|
||||
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
|
||||
}
|
||||
|
||||
eventIdentifiers[normalizedName] = true
|
||||
normalized.Name = normalizedName
|
||||
|
||||
used := make(map[string]bool)
|
||||
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
|
||||
copy(normalized.Inputs, original.Inputs)
|
||||
|
||||
for j, input := range normalized.Inputs {
|
||||
if input.Name == "" || isKeyWord(input.Name) {
|
||||
if input.Name == "" {
|
||||
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) {
|
||||
bindStructType[lang](input.Type, structs)
|
||||
}
|
||||
|
|
@ -229,14 +169,17 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
|||
if evmABI.HasFallback() {
|
||||
fallback = &tmplMethod{Original: evmABI.Fallback}
|
||||
}
|
||||
|
||||
if evmABI.HasReceive() {
|
||||
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{
|
||||
Type: capitalise(types[i]),
|
||||
InputABI: strings.ReplaceAll(strippedABI, "\"", "\\\""),
|
||||
InputABI: strings.Replace(strippedABI, "\"", "\\\"", -1),
|
||||
InputBin: strings.TrimPrefix(strings.TrimSpace(bytecodes[i]), "0x"),
|
||||
Constructor: evmABI.Constructor,
|
||||
Calls: calls,
|
||||
|
|
@ -257,7 +200,6 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
|||
if err != nil {
|
||||
log.Error("Could not search for pattern", "pattern", pattern, "contract", contracts[types[i]], "err", err)
|
||||
}
|
||||
|
||||
if matched {
|
||||
contracts[types[i]].Libraries[pattern] = name
|
||||
// keep track that this type is a library
|
||||
|
|
@ -288,7 +230,6 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
|||
"capitalise": capitalise,
|
||||
"decapitalise": decapitalise,
|
||||
}
|
||||
|
||||
tmpl := template.Must(template.New("").Funcs(funcs).Parse(tmplSource[lang]))
|
||||
if err := tmpl.Execute(buffer, data); err != nil {
|
||||
return "", err
|
||||
|
|
@ -299,7 +240,6 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
|||
if err != nil {
|
||||
return "", fmt.Errorf("%v\n%s", err, buffer)
|
||||
}
|
||||
|
||||
return string(code), nil
|
||||
}
|
||||
// For all others just return as is for now
|
||||
|
|
@ -310,6 +250,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
|
|||
// programming language types.
|
||||
var bindType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
|
||||
LangGo: bindTypeGo,
|
||||
LangJava: bindTypeJava,
|
||||
}
|
||||
|
||||
// bindBasicTypeGo converts basic solidity types(except array, slice and tuple) to Go ones.
|
||||
|
|
@ -323,7 +264,6 @@ func bindBasicTypeGo(kind abi.Type) string {
|
|||
case "8", "16", "32", "64":
|
||||
return fmt.Sprintf("%sint%s", parts[1], parts[2])
|
||||
}
|
||||
|
||||
return "*big.Int"
|
||||
case abi.FixedBytesTy:
|
||||
return fmt.Sprintf("[%d]byte", kind.Size)
|
||||
|
|
@ -353,10 +293,86 @@ 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
|
||||
// supported programming language topic types.
|
||||
var bindTopicType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
|
||||
LangGo: bindTopicTypeGo,
|
||||
LangJava: bindTopicTypeJava,
|
||||
}
|
||||
|
||||
// bindTopicTypeGo converts a Solidity topic type to a Go one. It is almost the same
|
||||
|
|
@ -373,7 +389,23 @@ func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
|||
if bound == "string" || bound == "[]byte" {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -381,6 +413,7 @@ func bindTopicTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
|||
// programming language struct definition.
|
||||
var bindStructType = map[Lang]func(kind abi.Type, structs map[string]*tmplStruct) string{
|
||||
LangGo: bindStructTypeGo,
|
||||
LangJava: bindStructTypeJava,
|
||||
}
|
||||
|
||||
// bindStructTypeGo converts a Solidity tuple type to a Go one and records the mapping
|
||||
|
|
@ -399,32 +432,19 @@ func bindStructTypeGo(kind abi.Type, structs map[string]*tmplStruct) string {
|
|||
if s, exist := structs[id]; exist {
|
||||
return s.Name
|
||||
}
|
||||
|
||||
var (
|
||||
names = make(map[string]bool)
|
||||
fields []*tmplField
|
||||
)
|
||||
|
||||
var fields []*tmplField
|
||||
for i, elem := range kind.TupleElems {
|
||||
name := capitalise(kind.TupleRawNames[i])
|
||||
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})
|
||||
field := bindStructTypeGo(*elem, structs)
|
||||
fields = append(fields, &tmplField{Type: field, Name: capitalise(kind.TupleRawNames[i]), SolKind: *elem})
|
||||
}
|
||||
|
||||
name := kind.TupleRawName
|
||||
if name == "" {
|
||||
name = fmt.Sprintf("Struct%d", len(structs))
|
||||
}
|
||||
|
||||
name = capitalise(name)
|
||||
|
||||
structs[id] = &tmplStruct{
|
||||
Name: name,
|
||||
Fields: fields,
|
||||
}
|
||||
|
||||
return name
|
||||
case abi.ArrayTy:
|
||||
return fmt.Sprintf("[%d]", kind.Size) + bindStructTypeGo(*kind.Elem, structs)
|
||||
|
|
@ -435,10 +455,74 @@ 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
|
||||
// named versions that may be used inside method names.
|
||||
var namedType = map[Lang]func(string, abi.Type) string{
|
||||
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
|
||||
|
|
@ -447,7 +531,6 @@ func alias(aliases map[string]string, n string) string {
|
|||
if alias, exist := aliases[n]; exist {
|
||||
return alias
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
|
|
@ -455,6 +538,7 @@ func alias(aliases map[string]string, n string) string {
|
|||
// conform to target language naming conventions.
|
||||
var methodNormalizer = map[Lang]func(string) string{
|
||||
LangGo: abi.ToCamelCase,
|
||||
LangJava: decapitalise,
|
||||
}
|
||||
|
||||
// capitalise makes a camel-case string which starts with an upper case character.
|
||||
|
|
@ -467,7 +551,6 @@ func decapitalise(input string) string {
|
|||
}
|
||||
|
||||
goForm := abi.ToCamelCase(input)
|
||||
|
||||
return strings.ToLower(goForm[:1]) + goForm[1:]
|
||||
}
|
||||
|
||||
|
|
@ -477,9 +560,7 @@ func structured(args abi.Arguments) bool {
|
|||
if len(args) < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
exists := make(map[string]bool)
|
||||
|
||||
for _, out := range args {
|
||||
// If the name is anonymous, we can't organize into a struct
|
||||
if out.Name == "" {
|
||||
|
|
@ -491,10 +572,8 @@ func structured(args abi.Arguments) bool {
|
|||
if field == "" || exists[field] {
|
||||
return false
|
||||
}
|
||||
|
||||
exists[field] = true
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -76,6 +76,7 @@ type tmplStruct struct {
|
|||
// programming languages the package can generate to.
|
||||
var tmplSource = map[Lang]string{
|
||||
LangGo: tmplSourceGo,
|
||||
LangJava: tmplSourceJava,
|
||||
}
|
||||
|
||||
// tmplSourceGo is the Go source template that the generated Go contract binding
|
||||
|
|
@ -109,7 +110,6 @@ var (
|
|||
_ = common.Big1
|
||||
_ = types.BloomLookup
|
||||
_ = event.NewSubscription
|
||||
_ = abi.ConvertType
|
||||
)
|
||||
|
||||
{{$structs := .Structs}}
|
||||
|
|
@ -161,7 +161,7 @@ var (
|
|||
}
|
||||
{{range $pattern, $name := .Libraries}}
|
||||
{{decapitalise $name}}Addr, _, _, _ := Deploy{{capitalise $name}}(auth, backend)
|
||||
{{$contract.Type}}Bin = strings.ReplaceAll({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:])
|
||||
{{$contract.Type}}Bin = strings.Replace({{$contract.Type}}Bin, "__${{$pattern}}$__", {{decapitalise $name}}Addr.String()[2:], -1)
|
||||
{{end}}
|
||||
address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex({{.Type}}Bin), backend {{range .Constructor.Inputs}}, {{.Name}}{{end}})
|
||||
if err != nil {
|
||||
|
|
@ -268,11 +268,11 @@ var (
|
|||
|
||||
// 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) {
|
||||
parsed, err := {{.Type}}MetaData.GetAbi()
|
||||
parsed, err := abi.JSON(strings.NewReader({{.Type}}ABI))
|
||||
if err != nil {
|
||||
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
|
||||
|
|
@ -569,3 +569,140 @@ var (
|
|||
{{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,7 +34,6 @@ func WaitMined(ctx context.Context, b DeployBackend, tx *types.Transaction) (*ty
|
|||
defer queryTicker.Stop()
|
||||
|
||||
logger := log.New("hash", tx.Hash())
|
||||
|
||||
for {
|
||||
receipt, err := b.TransactionReceipt(ctx, tx.Hash())
|
||||
if err == nil {
|
||||
|
|
@ -62,12 +61,10 @@ func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (
|
|||
if tx.To() != nil {
|
||||
return common.Address{}, errors.New("tx is not contract creation")
|
||||
}
|
||||
|
||||
receipt, err := WaitMined(ctx, b, tx)
|
||||
if err != nil {
|
||||
return common.Address{}, err
|
||||
}
|
||||
|
||||
if receipt.ContractAddress == (common.Address{}) {
|
||||
return common.Address{}, errors.New("zero address")
|
||||
}
|
||||
|
|
@ -78,6 +75,5 @@ func WaitDeployed(ctx context.Context, b DeployBackend, tx *types.Transaction) (
|
|||
if err == nil && len(code) == 0 {
|
||||
err = ErrNoCodeAfterDeploy
|
||||
}
|
||||
|
||||
return receipt.ContractAddress, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,10 +76,8 @@ func TestWaitDeployed(t *testing.T) {
|
|||
mined = make(chan struct{})
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
go func() {
|
||||
address, err = bind.WaitDeployed(ctx, backend, tx)
|
||||
|
||||
close(mined)
|
||||
}()
|
||||
|
||||
|
|
@ -92,7 +90,6 @@ func TestWaitDeployed(t *testing.T) {
|
|||
if err != test.wantErr {
|
||||
t.Errorf("test %q: error mismatch: want %q, got %q", name, test.wantErr, err)
|
||||
}
|
||||
|
||||
if address != test.wantAddress {
|
||||
t.Errorf("test %q: unexpected contract address %s", name, address.Hex())
|
||||
}
|
||||
|
|
@ -118,12 +115,10 @@ func TestWaitDeployedCornerCases(t *testing.T) {
|
|||
code := "6060604052600a8060106000396000f360606040526008565b00"
|
||||
tx := types.NewTransaction(0, common.HexToAddress("0x01"), big.NewInt(0), 3000000, gasPrice, common.FromHex(code))
|
||||
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
backend.SendTransaction(ctx, tx)
|
||||
backend.Commit()
|
||||
|
||||
notContentCreation := errors.New("tx is not contract creation")
|
||||
if _, err := bind.WaitDeployed(ctx, backend, tx); err.Error() != notContentCreation.Error() {
|
||||
t.Errorf("error mismatch: want %q, got %q, ", notContentCreation, err)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2016 The go-ethereum Authors
|
||||
// Copyright 2021 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
|
||||
|
|
@ -30,13 +30,11 @@ type Error struct {
|
|||
Name string
|
||||
Inputs Arguments
|
||||
str string
|
||||
|
||||
// Sig contains the string signature according to the ABI spec.
|
||||
// e.g. error 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"
|
||||
Sig string
|
||||
|
||||
// ID returns the canonical representation of the error'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.
|
||||
ID common.Hash
|
||||
}
|
||||
|
|
@ -46,7 +44,6 @@ func NewError(name string, inputs Arguments) Error {
|
|||
// and precompute string and sig representation.
|
||||
names := make([]string, len(inputs))
|
||||
types := make([]string, len(inputs))
|
||||
|
||||
for i, input := range inputs {
|
||||
if input.Name == "" {
|
||||
inputs[i] = Argument{
|
||||
|
|
@ -87,10 +84,8 @@ func (e *Error) Unpack(data []byte) (interface{}, error) {
|
|||
if len(data) < 4 {
|
||||
return "", errors.New("invalid data for unpacking")
|
||||
}
|
||||
|
||||
if !bytes.Equal(data[:4], e.ID[:4]) {
|
||||
return "", errors.New("invalid data for unpacking")
|
||||
}
|
||||
|
||||
return e.Inputs.Unpack(data[4:])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,14 +24,6 @@ import (
|
|||
|
||||
var (
|
||||
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
|
||||
|
|
@ -40,7 +32,6 @@ func formatSliceString(kind reflect.Kind, sliceSize int) string {
|
|||
if sliceSize == -1 {
|
||||
return fmt.Sprintf("[]%v", kind)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("[%d]%v", sliceSize, kind)
|
||||
}
|
||||
|
||||
|
|
@ -64,7 +55,6 @@ func sliceTypeCheck(t Type, val reflect.Value) error {
|
|||
if val.Type().Elem().Kind() != t.Elem.GetType().Kind() {
|
||||
return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), val.Type())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -83,6 +73,7 @@ func typeCheck(t Type, value reflect.Value) error {
|
|||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// typeErr returns a formatted type casting error.
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ import (
|
|||
// don't get the signature canonical representation as the first LOG topic.
|
||||
type Event struct {
|
||||
// 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 event overloading.
|
||||
// the raw name and a suffix will be added in the case of a event overload.
|
||||
//
|
||||
// e.g.
|
||||
// These are two events that have the same name:
|
||||
|
|
@ -38,18 +38,15 @@ type Event struct {
|
|||
// The event name of the first one will be resolved as foo while the second one
|
||||
// will be resolved as foo0.
|
||||
Name string
|
||||
|
||||
// RawName is the raw event name parsed from ABI.
|
||||
RawName string
|
||||
Anonymous bool
|
||||
Inputs Arguments
|
||||
str string
|
||||
|
||||
// Sig contains the string signature according to the ABI spec.
|
||||
// e.g. event foo(uint32 a, int b) = "foo(uint32,int256)"
|
||||
// Please note that "int" is substitute for its canonical representation "int256"
|
||||
Sig string
|
||||
|
||||
// ID returns the canonical representation of the event's signature used by the
|
||||
// abi definition to identify event names and types.
|
||||
ID common.Hash
|
||||
|
|
@ -64,7 +61,6 @@ func NewEvent(name, rawName string, anonymous bool, inputs Arguments) Event {
|
|||
// and precompute string and sig representation.
|
||||
names := make([]string, len(inputs))
|
||||
types := make([]string, len(inputs))
|
||||
|
||||
for i, input := range inputs {
|
||||
if input.Name == "" {
|
||||
inputs[i] = Argument{
|
||||
|
|
|
|||
|
|
@ -149,14 +149,11 @@ func TestEventMultiValueWithArrayUnpack(t *testing.T) {
|
|||
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))
|
||||
require.NoError(t, err)
|
||||
|
||||
var b bytes.Buffer
|
||||
|
||||
var i uint8 = 1
|
||||
for ; i <= 3; i++ {
|
||||
b.Write(packNum(reflect.ValueOf(i)))
|
||||
}
|
||||
|
||||
unpacked, err := abi.Unpack("test", b.Bytes())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, [2]uint8{1, 2}, unpacked[0])
|
||||
|
|
@ -164,6 +161,7 @@ func TestEventMultiValueWithArrayUnpack(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestEventTupleUnpack(t *testing.T) {
|
||||
|
||||
type EventTransfer struct {
|
||||
Value *big.Int
|
||||
}
|
||||
|
|
@ -212,7 +210,6 @@ func TestEventTupleUnpack(t *testing.T) {
|
|||
bigintExpected2 := big.NewInt(2218516807680)
|
||||
bigintExpected3 := big.NewInt(1000001)
|
||||
addr := common.HexToAddress("0x00Ce0d46d924CC8437c806721496599FC3FFA268")
|
||||
|
||||
var testCases = []struct {
|
||||
data string
|
||||
dest interface{}
|
||||
|
|
@ -347,33 +344,24 @@ func TestEventTupleUnpack(t *testing.T) {
|
|||
func unpackTestEventData(dest interface{}, hexData string, jsonEvent []byte, assert *assert.Assertions) error {
|
||||
data, err := hex.DecodeString(hexData)
|
||||
assert.NoError(err, "Hex data should be a correct hex-string")
|
||||
|
||||
var e Event
|
||||
|
||||
assert.NoError(json.Unmarshal(jsonEvent, &e), "Should be able to unmarshal event ABI")
|
||||
a := ABI{Events: map[string]Event{"e": e}}
|
||||
|
||||
return a.UnpackIntoInterface(dest, "e", data)
|
||||
}
|
||||
|
||||
// TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder.
|
||||
func TestEventUnpackIndexed(t *testing.T) {
|
||||
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
|
||||
|
||||
type testStruct struct {
|
||||
Value1 uint8 // indexed
|
||||
Value2 uint8
|
||||
}
|
||||
|
||||
abi, err := JSON(strings.NewReader(definition))
|
||||
require.NoError(t, err)
|
||||
|
||||
var b bytes.Buffer
|
||||
|
||||
b.Write(packNum(reflect.ValueOf(uint8(8))))
|
||||
|
||||
var rst testStruct
|
||||
|
||||
require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes()))
|
||||
require.Equal(t, uint8(0), rst.Value1)
|
||||
require.Equal(t, uint8(8), rst.Value2)
|
||||
|
|
@ -382,17 +370,13 @@ func TestEventUnpackIndexed(t *testing.T) {
|
|||
// TestEventIndexedWithArrayUnpack verifies that decoder will not overflow when static array is indexed input.
|
||||
func TestEventIndexedWithArrayUnpack(t *testing.T) {
|
||||
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"string"}]}]`
|
||||
|
||||
type testStruct struct {
|
||||
Value1 [2]uint8 // indexed
|
||||
Value2 string
|
||||
}
|
||||
|
||||
abi, err := JSON(strings.NewReader(definition))
|
||||
require.NoError(t, err)
|
||||
|
||||
var b bytes.Buffer
|
||||
|
||||
stringOut := "abc"
|
||||
// number of fields that will be encoded * 32
|
||||
b.Write(packNum(reflect.ValueOf(32)))
|
||||
|
|
@ -400,7 +384,6 @@ func TestEventIndexedWithArrayUnpack(t *testing.T) {
|
|||
b.Write(common.RightPadBytes([]byte(stringOut), 32))
|
||||
|
||||
var rst testStruct
|
||||
|
||||
require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes()))
|
||||
require.Equal(t, [2]uint8{0, 0}, rst.Value1)
|
||||
require.Equal(t, stringOut, rst.Value2)
|
||||
|
|
|
|||
|
|
@ -97,12 +97,10 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
|
|||
inputNames = make([]string, len(inputs))
|
||||
outputNames = make([]string, len(outputs))
|
||||
)
|
||||
|
||||
for i, input := range inputs {
|
||||
inputNames[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
|
||||
types[i] = input.Type.String()
|
||||
}
|
||||
|
||||
for i, output := range outputs {
|
||||
outputNames[i] = output.Type.String()
|
||||
if len(output.Name) > 0 {
|
||||
|
|
@ -115,7 +113,6 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
|
|||
sig string
|
||||
id []byte
|
||||
)
|
||||
|
||||
if funType == Function {
|
||||
sig = fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ","))
|
||||
id = crypto.Keccak256([]byte(sig))[:4]
|
||||
|
|
@ -126,11 +123,9 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
|
|||
if state == "nonpayable" {
|
||||
state = ""
|
||||
}
|
||||
|
||||
if state != "" {
|
||||
state = state + " "
|
||||
}
|
||||
|
||||
identity := fmt.Sprintf("function %v", rawName)
|
||||
if funType == Fallback {
|
||||
identity = "fallback"
|
||||
|
|
@ -139,7 +134,6 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
|
|||
} else if funType == Constructor {
|
||||
identity = "constructor"
|
||||
}
|
||||
|
||||
str := fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputNames, ", "), state, strings.Join(outputNames, ", "))
|
||||
|
||||
return Method{
|
||||
|
|
|
|||
|
|
@ -91,7 +91,6 @@ func TestMethodString(t *testing.T) {
|
|||
} else {
|
||||
got = abi.Methods[test.method].String()
|
||||
}
|
||||
|
||||
if got != test.expectation {
|
||||
t.Errorf("expected string to be %s, got %s", test.expectation, got)
|
||||
}
|
||||
|
|
@ -132,7 +131,6 @@ func TestMethodSig(t *testing.T) {
|
|||
expect: "complexTuple((uint256,uint256)[5][])",
|
||||
},
|
||||
}
|
||||
|
||||
abi, err := JSON(strings.NewReader(methoddata))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
|||
|
|
@ -51,23 +51,19 @@ func packElement(t Type, reflectValue reflect.Value) ([]byte, error) {
|
|||
if reflectValue.Bool() {
|
||||
return math.PaddedBigBytes(common.Big1, 32), nil
|
||||
}
|
||||
|
||||
return math.PaddedBigBytes(common.Big0, 32), nil
|
||||
case BytesTy:
|
||||
if reflectValue.Kind() == reflect.Array {
|
||||
reflectValue = mustArrayToByteSlice(reflectValue)
|
||||
}
|
||||
|
||||
if reflectValue.Type() != reflect.TypeOf([]byte{}) {
|
||||
return []byte{}, errors.New("Bytes type is neither slice nor array")
|
||||
}
|
||||
|
||||
return packBytesSlice(reflectValue.Bytes(), reflectValue.Len()), nil
|
||||
case FixedBytesTy, FunctionTy:
|
||||
if reflectValue.Kind() == reflect.Array {
|
||||
reflectValue = mustArrayToByteSlice(reflectValue)
|
||||
}
|
||||
|
||||
return common.RightPadBytes(reflectValue.Bytes(), 32), nil
|
||||
default:
|
||||
return []byte{}, fmt.Errorf("Could not pack element, unknown type: %v", t.T)
|
||||
|
|
|
|||
|
|
@ -38,21 +38,17 @@ func TestPack(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("invalid hex %s: %v", test.packed, err)
|
||||
}
|
||||
|
||||
inDef := fmt.Sprintf(`[{ "name" : "method", "type": "function", "inputs": %s}]`, test.def)
|
||||
inAbi, err := JSON(strings.NewReader(inDef))
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("invalid ABI definition %s, %v", inDef, err)
|
||||
}
|
||||
|
||||
var packed []byte
|
||||
packed, err = inAbi.Pack("method", test.unpacked)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("test %d (%v) failed: %v", i, test.def, err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(packed[4:], encb) {
|
||||
t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, encb, packed[4:])
|
||||
}
|
||||
|
|
@ -80,7 +76,6 @@ func TestMethodPack(t *testing.T) {
|
|||
}
|
||||
|
||||
var addrA, addrB = common.Address{1}, common.Address{2}
|
||||
|
||||
sig = abi.Methods["sliceAddress"].ID
|
||||
sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
|
||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||
|
|
@ -91,13 +86,11 @@ func TestMethodPack(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(packed, sig) {
|
||||
t.Errorf("expected %x got %x", sig, packed)
|
||||
}
|
||||
|
||||
var addrC, addrD = common.Address{3}, common.Address{4}
|
||||
|
||||
sig = abi.Methods["sliceMultiAddress"].ID
|
||||
sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...)
|
||||
sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...)
|
||||
|
|
@ -112,7 +105,6 @@ func TestMethodPack(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(packed, sig) {
|
||||
t.Errorf("expected %x got %x", sig, packed)
|
||||
}
|
||||
|
|
@ -140,12 +132,10 @@ func TestMethodPack(t *testing.T) {
|
|||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||
sig = append(sig, common.LeftPadBytes(addrC[:], 32)...)
|
||||
sig = append(sig, common.LeftPadBytes(addrD[:], 32)...)
|
||||
|
||||
packed, err = abi.Pack("nestedArray", a, []common.Address{addrC, addrD})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(packed, sig) {
|
||||
t.Errorf("expected %x got %x", sig, packed)
|
||||
}
|
||||
|
|
@ -158,12 +148,10 @@ 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)...)
|
||||
|
||||
packed, err = abi.Pack("nestedArray2", [2][]uint8{{1}, {1}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(packed, sig) {
|
||||
t.Errorf("expected %x got %x", sig, packed)
|
||||
}
|
||||
|
|
@ -179,12 +167,10 @@ func TestMethodPack(t *testing.T) {
|
|||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
|
||||
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
|
||||
|
||||
packed, err = abi.Pack("nestedSlice", [][]uint8{{1, 2}, {1, 2}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(packed, sig) {
|
||||
t.Errorf("expected %x got %x", sig, packed)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,18 +25,15 @@ import (
|
|||
)
|
||||
|
||||
// ConvertType converts an interface of a runtime type into a interface of the
|
||||
// given type, e.g. turn this code:
|
||||
//
|
||||
// given type
|
||||
// e.g. turn
|
||||
// var fields []reflect.StructField
|
||||
//
|
||||
// fields = append(fields, reflect.StructField{
|
||||
// Name: "X",
|
||||
// Type: reflect.TypeOf(new(big.Int)),
|
||||
// Tag: reflect.StructTag("json:\"" + "x" + "\""),
|
||||
// }
|
||||
//
|
||||
// into:
|
||||
//
|
||||
// into
|
||||
// type TupleT struct { X *big.Int }
|
||||
func ConvertType(in interface{}, proto interface{}) interface{} {
|
||||
protoType := reflect.TypeOf(proto)
|
||||
|
|
@ -47,7 +44,6 @@ func ConvertType(in interface{}, proto interface{}) interface{} {
|
|||
if err := set(reflect.ValueOf(proto), reflect.ValueOf(in)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return proto
|
||||
}
|
||||
|
||||
|
|
@ -57,7 +53,6 @@ func indirect(v reflect.Value) reflect.Value {
|
|||
if v.Kind() == reflect.Ptr && v.Elem().Type() != reflect.TypeOf(big.Int{}) {
|
||||
return indirect(v.Elem())
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +71,6 @@ func reflectIntType(unsigned bool, size int) reflect.Type {
|
|||
return reflect.TypeOf(uint64(0))
|
||||
}
|
||||
}
|
||||
|
||||
switch size {
|
||||
case 8:
|
||||
return reflect.TypeOf(int8(0))
|
||||
|
|
@ -87,7 +81,6 @@ func reflectIntType(unsigned bool, size int) reflect.Type {
|
|||
case 64:
|
||||
return reflect.TypeOf(int64(0))
|
||||
}
|
||||
|
||||
return reflect.TypeOf(&big.Int{})
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +89,6 @@ func reflectIntType(unsigned bool, size int) reflect.Type {
|
|||
func mustArrayToByteSlice(value reflect.Value) reflect.Value {
|
||||
slice := reflect.MakeSlice(reflect.TypeOf([]byte{}), value.Len(), value.Len())
|
||||
reflect.Copy(slice, value)
|
||||
|
||||
return slice
|
||||
}
|
||||
|
||||
|
|
@ -106,9 +98,8 @@ func mustArrayToByteSlice(value reflect.Value) reflect.Value {
|
|||
// strict ruleset as bare `reflect` does.
|
||||
func set(dst, src reflect.Value) error {
|
||||
dstType, srcType := dst.Type(), src.Type()
|
||||
|
||||
switch {
|
||||
case dstType.Kind() == reflect.Interface && dst.Elem().IsValid() && (dst.Elem().Type().Kind() == reflect.Ptr || dst.Elem().CanSet()):
|
||||
case dstType.Kind() == reflect.Interface && dst.Elem().IsValid():
|
||||
return set(dst.Elem(), src)
|
||||
case dstType.Kind() == reflect.Ptr && dstType.Elem() != reflect.TypeOf(big.Int{}):
|
||||
return set(dst.Elem(), src)
|
||||
|
|
@ -123,7 +114,6 @@ func set(dst, src reflect.Value) error {
|
|||
default:
|
||||
return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -137,12 +127,10 @@ func setSlice(dst, src reflect.Value) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if dst.CanSet() {
|
||||
dst.Set(slice)
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("Cannot set slice, destination not settable")
|
||||
}
|
||||
|
||||
|
|
@ -150,25 +138,20 @@ func setArray(dst, src reflect.Value) error {
|
|||
if src.Kind() == reflect.Ptr {
|
||||
return set(dst, indirect(src))
|
||||
}
|
||||
|
||||
array := reflect.New(dst.Type()).Elem()
|
||||
|
||||
min := src.Len()
|
||||
if src.Len() > dst.Len() {
|
||||
min = dst.Len()
|
||||
}
|
||||
|
||||
for i := 0; i < min; i++ {
|
||||
if err := set(array.Index(i), src.Index(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if dst.CanSet() {
|
||||
dst.Set(array)
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("Cannot set array, destination not settable")
|
||||
}
|
||||
|
||||
|
|
@ -176,27 +159,22 @@ func setStruct(dst, src reflect.Value) error {
|
|||
for i := 0; i < src.NumField(); i++ {
|
||||
srcField := src.Field(i)
|
||||
dstField := dst.Field(i)
|
||||
|
||||
if !dstField.IsValid() || !srcField.IsValid() {
|
||||
return fmt.Errorf("Could not find src field: %v value: %v in destination", srcField.Type().Name(), srcField)
|
||||
}
|
||||
|
||||
if err := set(dstField, srcField); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// 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.
|
||||
// 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.
|
||||
func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[string]string, error) {
|
||||
typ := value.Type()
|
||||
|
|
@ -223,7 +201,6 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
|
|||
}
|
||||
// check which argument field matches with the abi tag.
|
||||
found := false
|
||||
|
||||
for _, arg := range argNames {
|
||||
if arg == tagName {
|
||||
if abi2struct[arg] != "" {
|
||||
|
|
@ -243,6 +220,7 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
|
|||
|
||||
// second round ~~~
|
||||
for _, argName := range argNames {
|
||||
|
||||
structFieldName := ToCamelCase(argName)
|
||||
|
||||
if structFieldName == "" {
|
||||
|
|
@ -259,7 +237,6 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
|
|||
value.FieldByName(structFieldName).IsValid() {
|
||||
return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", argName)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -279,6 +256,5 @@ func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[stri
|
|||
struct2abi[structFieldName] = argName
|
||||
}
|
||||
}
|
||||
|
||||
return abi2struct, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ type reflectTest struct {
|
|||
|
||||
var reflectTests = []reflectTest{
|
||||
{
|
||||
name: "OneToOneCorrespondence",
|
||||
name: "OneToOneCorrespondance",
|
||||
args: []string{"fieldA"},
|
||||
struc: struct {
|
||||
FieldA int `abi:"fieldA"`
|
||||
|
|
@ -181,7 +181,6 @@ func TestReflectNameToStruct(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
for fname := range test.want {
|
||||
if m[fname] != test.want[fname] {
|
||||
t.Fatalf("Incorrect value for field %s: expected %v, got %v", fname, test.want[fname], m[fname])
|
||||
|
|
@ -218,7 +217,6 @@ func TestConvertType(t *testing.T) {
|
|||
if out.X.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Errorf("ConvertType failed, got %v want %v", out.X, big.NewInt(1))
|
||||
}
|
||||
|
||||
if out.Y.Cmp(big.NewInt(2)) != 0 {
|
||||
t.Errorf("ConvertType failed, got %v want %v", out.Y, big.NewInt(2))
|
||||
}
|
||||
|
|
@ -228,20 +226,16 @@ func TestConvertType(t *testing.T) {
|
|||
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(1).Set(reflect.ValueOf(big.NewInt(4)))
|
||||
|
||||
out2 := *ConvertType(val2.Interface(), new([]T)).(*[]T)
|
||||
if out2[0].X.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1))
|
||||
}
|
||||
|
||||
if out2[0].Y.Cmp(big.NewInt(2)) != 0 {
|
||||
t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2))
|
||||
}
|
||||
|
||||
if out2[1].X.Cmp(big.NewInt(3)) != 0 {
|
||||
t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1))
|
||||
}
|
||||
|
||||
if out2[1].Y.Cmp(big.NewInt(4)) != 0 {
|
||||
t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2))
|
||||
}
|
||||
|
|
@ -251,20 +245,16 @@ func TestConvertType(t *testing.T) {
|
|||
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(1).Set(reflect.ValueOf(big.NewInt(4)))
|
||||
|
||||
out3 := *ConvertType(val3.Interface(), new([2]T)).(*[2]T)
|
||||
if out3[0].X.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1))
|
||||
}
|
||||
|
||||
if out3[0].Y.Cmp(big.NewInt(2)) != 0 {
|
||||
t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2))
|
||||
}
|
||||
|
||||
if out3[1].X.Cmp(big.NewInt(3)) != 0 {
|
||||
t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1))
|
||||
}
|
||||
|
||||
if out3[1].Y.Cmp(big.NewInt(4)) != 0 {
|
||||
t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,3 @@
|
|||
// 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 (
|
||||
|
|
@ -42,23 +26,18 @@ func parseToken(unescapedSelector string, isIdent bool) (string, string, error)
|
|||
if len(unescapedSelector) == 0 {
|
||||
return "", "", fmt.Errorf("empty token")
|
||||
}
|
||||
|
||||
firstChar := unescapedSelector[0]
|
||||
position := 1
|
||||
|
||||
if !(isAlpha(firstChar) || (isIdent && isIdentifierSymbol(firstChar))) {
|
||||
return "", "", fmt.Errorf("invalid token start: %c", firstChar)
|
||||
}
|
||||
|
||||
for position < len(unescapedSelector) {
|
||||
char := unescapedSelector[position]
|
||||
if !(isAlpha(char) || isDigit(char) || (isIdent && isIdentifierSymbol(char))) {
|
||||
break
|
||||
}
|
||||
|
||||
position++
|
||||
}
|
||||
|
||||
return unescapedSelector[:position], unescapedSelector[position:], nil
|
||||
}
|
||||
|
||||
|
|
@ -75,20 +54,16 @@ func parseElementaryType(unescapedSelector string) (string, string, error) {
|
|||
for len(rest) > 0 && rest[0] == '[' {
|
||||
parsedType = parsedType + string(rest[0])
|
||||
rest = rest[1:]
|
||||
|
||||
for len(rest) > 0 && isDigit(rest[0]) {
|
||||
parsedType = parsedType + string(rest[0])
|
||||
rest = rest[1:]
|
||||
}
|
||||
|
||||
if len(rest) == 0 || rest[0] != ']' {
|
||||
return "", "", fmt.Errorf("failed to parse array: expected ']', got %c", unescapedSelector[0])
|
||||
}
|
||||
|
||||
parsedType = parsedType + string(rest[0])
|
||||
rest = rest[1:]
|
||||
}
|
||||
|
||||
return parsedType, rest, nil
|
||||
}
|
||||
|
||||
|
|
@ -96,31 +71,21 @@ func parseCompositeType(unescapedSelector string) ([]interface{}, string, error)
|
|||
if len(unescapedSelector) == 0 || unescapedSelector[0] != '(' {
|
||||
return nil, "", fmt.Errorf("expected '(', got %c", unescapedSelector[0])
|
||||
}
|
||||
|
||||
parsedType, rest, err := parseType(unescapedSelector[1:])
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to parse type: %v", err)
|
||||
}
|
||||
|
||||
result := []interface{}{parsedType}
|
||||
|
||||
for len(rest) > 0 && rest[0] != ')' {
|
||||
parsedType, rest, err = parseType(rest[1:])
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to parse type: %v", err)
|
||||
}
|
||||
|
||||
result = append(result, parsedType)
|
||||
}
|
||||
|
||||
if len(rest) == 0 || rest[0] != ')' {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -128,7 +93,6 @@ func parseType(unescapedSelector string) (interface{}, string, error) {
|
|||
if len(unescapedSelector) == 0 {
|
||||
return nil, "", fmt.Errorf("empty type")
|
||||
}
|
||||
|
||||
if unescapedSelector[0] == '(' {
|
||||
return parseCompositeType(unescapedSelector)
|
||||
} else {
|
||||
|
|
@ -138,7 +102,6 @@ func parseType(unescapedSelector string) (interface{}, string, error) {
|
|||
|
||||
func assembleArgs(args []interface{}) ([]ArgumentMarshaling, error) {
|
||||
arguments := make([]ArgumentMarshaling, 0)
|
||||
|
||||
for i, arg := range args {
|
||||
// generate dummy name to avoid unmarshal issues
|
||||
name := fmt.Sprintf("name%d", i)
|
||||
|
|
@ -149,20 +112,11 @@ func assembleArgs(args []interface{}) ([]ArgumentMarshaling, error) {
|
|||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to assemble components: %v", err)
|
||||
}
|
||||
// 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})
|
||||
arguments = append(arguments, ArgumentMarshaling{name, "tuple", "tuple", subArgs, false})
|
||||
} else {
|
||||
return nil, fmt.Errorf("failed to assemble args: unexpected type %T", arg)
|
||||
}
|
||||
}
|
||||
|
||||
return arguments, nil
|
||||
}
|
||||
|
||||
|
|
@ -175,9 +129,7 @@ func ParseSelector(unescapedSelector string) (SelectorMarshaling, error) {
|
|||
if err != nil {
|
||||
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err)
|
||||
}
|
||||
|
||||
args := []interface{}{}
|
||||
|
||||
if len(rest) >= 2 && rest[0] == '(' && rest[1] == ')' {
|
||||
rest = rest[2:]
|
||||
} else {
|
||||
|
|
@ -186,7 +138,6 @@ func ParseSelector(unescapedSelector string) (SelectorMarshaling, error) {
|
|||
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': %v", unescapedSelector, err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(rest) > 0 {
|
||||
return SelectorMarshaling{}, fmt.Errorf("failed to parse selector '%s': unexpected string '%s'", unescapedSelector, rest)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,3 @@
|
|||
// 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 (
|
||||
|
|
@ -26,23 +10,18 @@ import (
|
|||
func TestParseSelector(t *testing.T) {
|
||||
mkType := func(types ...interface{}) []ArgumentMarshaling {
|
||||
var result []ArgumentMarshaling
|
||||
|
||||
for i, typeOrComponents := range types {
|
||||
name := fmt.Sprintf("name%d", i)
|
||||
if typeName, ok := typeOrComponents.(string); ok {
|
||||
result = append(result, ArgumentMarshaling{name, typeName, typeName, nil, false})
|
||||
} else if components, ok := typeOrComponents.([]ArgumentMarshaling); ok {
|
||||
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 {
|
||||
log.Fatalf("unexpected type %T", typeOrComponents)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
name string
|
||||
|
|
@ -55,20 +34,12 @@ func TestParseSelector(t *testing.T) {
|
|||
{"singleNest(bytes32,uint8,(uint256,uint256),address)", "singleNest", mkType("bytes32", "uint8", mkType("uint256", "uint256"), "address")},
|
||||
{"multiNest(address,(uint256[],uint256),((address,bytes32),uint256))", "multiNest",
|
||||
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 {
|
||||
selector, err := ParseSelector(tt.input)
|
||||
if err != nil {
|
||||
t.Errorf("test %d: failed to parse selector '%v': %v", i, tt.input, err)
|
||||
}
|
||||
|
||||
if selector.Name != tt.name {
|
||||
t.Errorf("test %d: unexpected function name: '%s' != '%s'", i, selector.Name, tt.name)
|
||||
}
|
||||
|
|
@ -76,7 +47,6 @@ func TestParseSelector(t *testing.T) {
|
|||
if selector.Type != "function" {
|
||||
t.Errorf("test %d: unexpected type: '%s' != '%s'", i, selector.Type, "function")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(selector.Inputs, tt.args) {
|
||||
t.Errorf("test %d: unexpected args: '%v' != '%v'", i, selector.Inputs, tt.args)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import (
|
|||
// MakeTopics converts a filter query argument list into a filter topic set.
|
||||
func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
|
||||
topics := make([][]common.Hash, len(query))
|
||||
|
||||
for i, filter := range query {
|
||||
for _, rule := range filter {
|
||||
var topic common.Hash
|
||||
|
|
@ -82,9 +81,9 @@ func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
|
|||
//
|
||||
// We only convert stringS and bytes to hash, still need to deal with
|
||||
// array(both fixed-size and dynamic-size) and struct.
|
||||
|
||||
// Attempt to generate the topic from funky types
|
||||
val := reflect.ValueOf(rule)
|
||||
|
||||
switch {
|
||||
// static byte array
|
||||
case val.Kind() == reflect.Array && reflect.TypeOf(rule).Elem().Kind() == reflect.Uint8:
|
||||
|
|
@ -93,11 +92,9 @@ func MakeTopics(query ...[]interface{}) ([][]common.Hash, error) {
|
|||
return nil, fmt.Errorf("unsupported indexed type: %T", rule)
|
||||
}
|
||||
}
|
||||
|
||||
topics[i] = append(topics[i], topic)
|
||||
}
|
||||
}
|
||||
|
||||
return topics, nil
|
||||
}
|
||||
|
||||
|
|
@ -108,11 +105,9 @@ func genIntType(rule int64, size uint) []byte {
|
|||
// 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}
|
||||
}
|
||||
|
||||
for i := uint(0); i < size; i++ {
|
||||
topic[common.HashLength-i-1] = byte(rule >> (i * 8))
|
||||
}
|
||||
|
||||
return topic[:]
|
||||
}
|
||||
|
||||
|
|
@ -148,9 +143,7 @@ func parseTopicWithSetter(fields Arguments, topics []common.Hash, setter func(Ar
|
|||
if !arg.Indexed {
|
||||
return errors.New("non-indexed field in topic reconstruction")
|
||||
}
|
||||
|
||||
var reconstr interface{}
|
||||
|
||||
switch arg.Type.T {
|
||||
case TupleTy:
|
||||
return errors.New("tuple type in topic reconstruction")
|
||||
|
|
@ -162,14 +155,11 @@ func parseTopicWithSetter(fields Arguments, topics []common.Hash, setter func(Ar
|
|||
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())
|
||||
}
|
||||
|
||||
var tmp [24]byte
|
||||
|
||||
copy(tmp[:], topics[i][8:32])
|
||||
reconstr = tmp
|
||||
default:
|
||||
var err error
|
||||
|
||||
reconstr, err = toGoType(0, arg.Type, topics[i].Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2020 The go-ethereum Authors
|
||||
// Copyright 2019 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
|
||||
|
|
@ -29,7 +29,6 @@ func TestMakeTopics(t *testing.T) {
|
|||
type args struct {
|
||||
query [][]interface{}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
|
|
@ -124,7 +123,6 @@ func TestMakeTopics(t *testing.T) {
|
|||
t.Errorf("makeTopics() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("makeTopics() = %v, want %v", got, tt.want)
|
||||
}
|
||||
|
|
@ -357,7 +355,6 @@ func TestParseTopics(t *testing.T) {
|
|||
if err := ParseTopics(createObj, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
|
||||
t.Errorf("parseTopics() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
||||
resultObj := tt.args.resultObj()
|
||||
if !reflect.DeepEqual(createObj, resultObj) {
|
||||
t.Errorf("parseTopics() = %v, want %v", createObj, resultObj)
|
||||
|
|
@ -375,7 +372,6 @@ func TestParseTopicsIntoMap(t *testing.T) {
|
|||
if err := ParseTopicsIntoMap(outMap, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
|
||||
t.Errorf("parseTopicsIntoMap() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
|
||||
resultMap := tt.args.resultMap()
|
||||
if !reflect.DeepEqual(outMap, resultMap) {
|
||||
t.Errorf("parseTopicsIntoMap() = %v, want %v", outMap, resultMap)
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ import (
|
|||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
|
@ -72,7 +70,6 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
|||
if strings.Count(t, "[") != strings.Count(t, "]") {
|
||||
return Type{}, fmt.Errorf("invalid arg type in abi")
|
||||
}
|
||||
|
||||
typ.stringKind = t
|
||||
|
||||
// if there are brackets, get ready to go into slice/array mode and
|
||||
|
|
@ -85,7 +82,6 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
|||
}
|
||||
// recursively embed the type
|
||||
i := strings.LastIndex(t, "[")
|
||||
|
||||
embeddedType, err := NewType(t[:i], subInternal, components)
|
||||
if err != nil {
|
||||
return Type{}, err
|
||||
|
|
@ -105,17 +101,14 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
|||
// is an array
|
||||
typ.T = ArrayTy
|
||||
typ.Elem = &embeddedType
|
||||
|
||||
typ.Size, err = strconv.Atoi(intz[0])
|
||||
if err != nil {
|
||||
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
|
||||
}
|
||||
|
||||
typ.stringKind = embeddedType.stringKind + sliced
|
||||
} else {
|
||||
return Type{}, fmt.Errorf("invalid formatting of array type")
|
||||
}
|
||||
|
||||
return typ, err
|
||||
}
|
||||
// parse the type and size of the abi-type.
|
||||
|
|
@ -123,15 +116,12 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
|||
if len(matches) == 0 {
|
||||
return Type{}, fmt.Errorf("invalid type '%v'", t)
|
||||
}
|
||||
|
||||
parsedType := matches[0]
|
||||
|
||||
// varSize is the size of the variable
|
||||
var varSize int
|
||||
|
||||
if len(parsedType[3]) > 0 {
|
||||
var err error
|
||||
|
||||
varSize, err = strconv.Atoi(parsedType[2])
|
||||
if err != nil {
|
||||
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
|
||||
|
|
@ -162,10 +152,6 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
|||
if varSize == 0 {
|
||||
typ.T = BytesTy
|
||||
} else {
|
||||
if varSize > 32 {
|
||||
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
|
||||
}
|
||||
|
||||
typ.T = FixedBytesTy
|
||||
typ.Size = varSize
|
||||
}
|
||||
|
|
@ -175,50 +161,31 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
|||
elems []*Type
|
||||
names []string
|
||||
expression string // canonical parameter expression
|
||||
used = make(map[string]bool)
|
||||
)
|
||||
|
||||
expression += "("
|
||||
|
||||
overloadedNames := make(map[string]string)
|
||||
for idx, c := range components {
|
||||
cType, err := NewType(c.Type, c.InternalType, c.Components)
|
||||
if err != nil {
|
||||
return Type{}, err
|
||||
}
|
||||
|
||||
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] })
|
||||
|
||||
fieldName, err := overloadedArgName(c.Name, overloadedNames)
|
||||
if err != nil {
|
||||
return Type{}, err
|
||||
}
|
||||
|
||||
used[fieldName] = true
|
||||
|
||||
if !isValidFieldName(fieldName) {
|
||||
return Type{}, fmt.Errorf("field %d has invalid name", idx)
|
||||
}
|
||||
|
||||
overloadedNames[fieldName] = fieldName
|
||||
fields = append(fields, reflect.StructField{
|
||||
Name: fieldName, // reflect.StructOf will panic for any exported field.
|
||||
Type: cType.GetType(),
|
||||
Tag: reflect.StructTag("json:\"" + c.Name + "\""),
|
||||
})
|
||||
|
||||
elems = append(elems, &cType)
|
||||
names = append(names, c.Name)
|
||||
|
||||
expression += cType.stringKind
|
||||
if idx != len(components)-1 {
|
||||
expression += ","
|
||||
}
|
||||
}
|
||||
|
||||
expression += ")"
|
||||
|
||||
typ.TupleType = reflect.StructOf(fields)
|
||||
|
|
@ -234,7 +201,7 @@ func NewType(t string, internalType string, components []ArgumentMarshaling) (ty
|
|||
if internalType != "" && strings.HasPrefix(internalType, structPrefix) {
|
||||
// Foo.Bar type definition is not allowed in golang,
|
||||
// convert the format to FooBar
|
||||
typ.TupleRawName = strings.ReplaceAll(internalType[len(structPrefix):], ".", "")
|
||||
typ.TupleRawName = strings.Replace(internalType[len(structPrefix):], ".", "", -1)
|
||||
}
|
||||
|
||||
case "function":
|
||||
|
|
@ -283,6 +250,20 @@ 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.
|
||||
func (t Type) String() (out string) {
|
||||
return t.stringKind
|
||||
|
|
@ -307,29 +288,23 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
|
|||
// calculate offset if any
|
||||
offset := 0
|
||||
offsetReq := isDynamicType(*t.Elem)
|
||||
|
||||
if offsetReq {
|
||||
offset = getTypeSize(*t.Elem) * v.Len()
|
||||
}
|
||||
|
||||
var tail []byte
|
||||
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
val, err := t.Elem.pack(v.Index(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !offsetReq {
|
||||
ret = append(ret, val...)
|
||||
continue
|
||||
}
|
||||
|
||||
ret = append(ret, packNum(reflect.ValueOf(offset))...)
|
||||
offset += len(val)
|
||||
tail = append(tail, val...)
|
||||
}
|
||||
|
||||
return append(ret, tail...), nil
|
||||
case TupleTy:
|
||||
// (T1,...,Tk) for k >= 0 and any types T1, …, Tk
|
||||
|
|
@ -350,20 +325,16 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
|
|||
for _, elem := range t.TupleElems {
|
||||
offset += getTypeSize(*elem)
|
||||
}
|
||||
|
||||
var ret, tail []byte
|
||||
|
||||
for i, elem := range t.TupleElems {
|
||||
field := v.FieldByName(fieldmap[t.TupleRawNames[i]])
|
||||
if !field.IsValid() {
|
||||
return nil, fmt.Errorf("field %s for tuple not found in the given struct", t.TupleRawNames[i])
|
||||
}
|
||||
|
||||
val, err := elem.pack(field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if isDynamicType(*elem) {
|
||||
ret = append(ret, packNum(reflect.ValueOf(offset))...)
|
||||
tail = append(tail, val...)
|
||||
|
|
@ -372,7 +343,6 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
|
|||
ret = append(ret, val...)
|
||||
}
|
||||
}
|
||||
|
||||
return append(ret, tail...), nil
|
||||
|
||||
default:
|
||||
|
|
@ -400,10 +370,8 @@ func isDynamicType(t Type) bool {
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return t.T == StringTy || t.T == BytesTy || t.T == SliceTy || (t.T == ArrayTy && isDynamicType(*t.Elem))
|
||||
}
|
||||
|
||||
|
|
@ -421,43 +389,13 @@ func getTypeSize(t Type) int {
|
|||
if t.Elem.T == ArrayTy || t.Elem.T == TupleTy {
|
||||
return t.Size * getTypeSize(*t.Elem)
|
||||
}
|
||||
|
||||
return t.Size * 32
|
||||
} else if t.T == TupleTy && !isDynamicType(t) {
|
||||
total := 0
|
||||
for _, elem := range t.TupleElems {
|
||||
total += getTypeSize(*elem)
|
||||
}
|
||||
|
||||
return total
|
||||
}
|
||||
|
||||
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,7 +110,6 @@ func TestTypeRegexp(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("type %q: failed to parse type string: %v", tt.blob, err)
|
||||
}
|
||||
|
||||
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)))
|
||||
}
|
||||
|
|
@ -289,7 +288,6 @@ func TestTypeCheck(t *testing.T) {
|
|||
if err.Error() != test.err {
|
||||
t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -298,7 +296,6 @@ func TestTypeCheck(t *testing.T) {
|
|||
t.Errorf("%d failed. Expected no err but got: %v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if err == nil && len(test.err) != 0 {
|
||||
t.Errorf("%d failed. Expected err: %v but got none", i, test.err)
|
||||
continue
|
||||
|
|
@ -326,11 +323,9 @@ func TestInternalType(t *testing.T) {
|
|||
|
||||
blob := "tuple"
|
||||
typ, err := NewType(blob, internalType, components)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("type %q: failed to parse type string: %v", blob, err)
|
||||
}
|
||||
|
||||
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)))
|
||||
}
|
||||
|
|
@ -371,12 +366,3 @@ 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,7 +19,6 @@ package abi
|
|||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
|
||||
|
|
@ -34,83 +33,43 @@ var (
|
|||
)
|
||||
|
||||
// ReadInteger reads the integer based on its kind and returns the appropriate value.
|
||||
// nolint:gocognit
|
||||
func ReadInteger(typ Type, b []byte) (interface{}, error) {
|
||||
ret := new(big.Int).SetBytes(b)
|
||||
|
||||
func ReadInteger(typ Type, b []byte) interface{} {
|
||||
if typ.T == UintTy {
|
||||
u64, isu64 := ret.Uint64(), ret.IsUint64()
|
||||
|
||||
switch typ.Size {
|
||||
case 8:
|
||||
if !isu64 || u64 > math.MaxUint8 {
|
||||
return nil, errBadUint8
|
||||
}
|
||||
|
||||
return byte(u64), nil
|
||||
return b[len(b)-1]
|
||||
case 16:
|
||||
if !isu64 || u64 > math.MaxUint16 {
|
||||
return nil, errBadUint16
|
||||
}
|
||||
|
||||
return uint16(u64), nil
|
||||
return binary.BigEndian.Uint16(b[len(b)-2:])
|
||||
case 32:
|
||||
if !isu64 || u64 > math.MaxUint32 {
|
||||
return nil, errBadUint32
|
||||
}
|
||||
|
||||
return uint32(u64), nil
|
||||
return binary.BigEndian.Uint32(b[len(b)-4:])
|
||||
case 64:
|
||||
if !isu64 {
|
||||
return nil, errBadUint64
|
||||
}
|
||||
|
||||
return u64, nil
|
||||
return binary.BigEndian.Uint64(b[len(b)-8:])
|
||||
default:
|
||||
// the only case left for unsigned integer is uint256.
|
||||
return ret, nil
|
||||
return new(big.Int).SetBytes(b)
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
// On EVM, if the returned number > max int256, it is negative.
|
||||
// A number is > max int256 if the bit at position 255 is set.
|
||||
ret := new(big.Int).SetBytes(b)
|
||||
if ret.Bit(255) == 1 {
|
||||
ret.Add(MaxUint256, new(big.Int).Neg(ret))
|
||||
ret.Add(ret, common.Big1)
|
||||
ret.Neg(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
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -121,7 +80,6 @@ func readBool(word []byte) (bool, error) {
|
|||
return false, errBadBool
|
||||
}
|
||||
}
|
||||
|
||||
switch word[31] {
|
||||
case 0:
|
||||
return false, nil
|
||||
|
|
@ -139,13 +97,11 @@ func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
|
|||
if t.T != FunctionTy {
|
||||
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 {
|
||||
err = fmt.Errorf("abi: got improperly encoded function type, got %v", word)
|
||||
} else {
|
||||
copy(funcTy[:], word[0:24])
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -158,8 +114,8 @@ func ReadFixedBytes(t Type, word []byte) (interface{}, error) {
|
|||
array := reflect.New(t.GetType()).Elem()
|
||||
|
||||
reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
|
||||
|
||||
return array.Interface(), nil
|
||||
|
||||
}
|
||||
|
||||
// forEachUnpack iteratively unpack elements.
|
||||
|
|
@ -167,7 +123,6 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
|
|||
if size < 0 {
|
||||
return nil, fmt.Errorf("cannot marshal input to array, size is negative (%d)", size)
|
||||
}
|
||||
|
||||
if start+32*size > len(output) {
|
||||
return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
|
||||
}
|
||||
|
|
@ -205,14 +160,9 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
|
|||
|
||||
func forTupleUnpack(t Type, output []byte) (interface{}, error) {
|
||||
retval := reflect.New(t.GetType()).Elem()
|
||||
|
||||
virtualArgs := 0
|
||||
for index, elem := range t.TupleElems {
|
||||
marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if elem.T == ArrayTy && !isDynamicType(*elem) {
|
||||
// If we have a static array, like [3]uint256, these are coded as
|
||||
// just like uint256,uint256,uint256.
|
||||
|
|
@ -230,10 +180,11 @@ func forTupleUnpack(t Type, output []byte) (interface{}, error) {
|
|||
// coded as just like uint256,bool,uint256
|
||||
virtualArgs += getTypeSize(*elem)/32 - 1
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
retval.Field(index).Set(reflect.ValueOf(marshalledValue))
|
||||
}
|
||||
|
||||
return retval.Interface(), nil
|
||||
}
|
||||
|
||||
|
|
@ -267,10 +218,8 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return forTupleUnpack(t, output[begin:])
|
||||
}
|
||||
|
||||
return forTupleUnpack(t, output[index:])
|
||||
case SliceTy:
|
||||
return forEachUnpack(t, output[begin:], 0, length)
|
||||
|
|
@ -280,15 +229,13 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
|||
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 forEachUnpack(t, output[offset:], 0, t.Size)
|
||||
}
|
||||
|
||||
return forEachUnpack(t, output[index:], 0, t.Size)
|
||||
case StringTy: // variable arrays are written at the end of the return bytes
|
||||
return string(output[begin : begin+length]), nil
|
||||
case IntTy, UintTy:
|
||||
return ReadInteger(t, returnOutput)
|
||||
return ReadInteger(t, returnOutput), nil
|
||||
case BoolTy:
|
||||
return readBool(returnOutput)
|
||||
case AddressTy:
|
||||
|
|
@ -308,9 +255,8 @@ 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.
|
||||
func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
|
||||
bigOffsetEnd := new(big.Int).SetBytes(output[index : index+32])
|
||||
bigOffsetEnd := big.NewInt(0).SetBytes(output[index : index+32])
|
||||
bigOffsetEnd.Add(bigOffsetEnd, common.Big32)
|
||||
|
||||
outputLength := big.NewInt(int64(len(output)))
|
||||
|
||||
if bigOffsetEnd.Cmp(outputLength) > 0 {
|
||||
|
|
@ -322,9 +268,11 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
|
|||
}
|
||||
|
||||
offsetEnd := int(bigOffsetEnd.Uint64())
|
||||
lengthBig := new(big.Int).SetBytes(output[offsetEnd-32 : offsetEnd])
|
||||
lengthBig := big.NewInt(0).SetBytes(output[offsetEnd-32 : offsetEnd])
|
||||
|
||||
totalSize := new(big.Int).Add(bigOffsetEnd, lengthBig)
|
||||
totalSize := big.NewInt(0)
|
||||
totalSize.Add(totalSize, bigOffsetEnd)
|
||||
totalSize.Add(totalSize, lengthBig)
|
||||
if totalSize.BitLen() > 63 {
|
||||
return 0, 0, fmt.Errorf("abi: length larger than int64: %v", totalSize)
|
||||
}
|
||||
|
|
@ -332,25 +280,21 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
|
|||
if totalSize.Cmp(outputLength) > 0 {
|
||||
return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %v require %v", outputLength, totalSize)
|
||||
}
|
||||
|
||||
start = int(bigOffsetEnd.Uint64())
|
||||
length = int(lengthBig.Uint64())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// tuplePointsTo resolves the location reference for dynamic tuple.
|
||||
func tuplePointsTo(index int, output []byte) (start int, err error) {
|
||||
offset := new(big.Int).SetBytes(output[index : index+32])
|
||||
offset := big.NewInt(0).SetBytes(output[index : index+32])
|
||||
outputLen := big.NewInt(int64(len(output)))
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if offset.BitLen() > 63 {
|
||||
return 0, fmt.Errorf("abi offset larger than int64: %v", offset)
|
||||
}
|
||||
|
||||
return int(offset.Uint64()), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import (
|
|||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
|
@ -38,22 +37,18 @@ func TestUnpack(t *testing.T) {
|
|||
//Unpack
|
||||
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
|
||||
abi, err := JSON(strings.NewReader(def))
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
||||
}
|
||||
|
||||
encb, err := hex.DecodeString(test.packed)
|
||||
if err != nil {
|
||||
t.Fatalf("invalid hex %s: %v", test.packed, err)
|
||||
}
|
||||
|
||||
out, err := abi.Unpack("method", encb)
|
||||
if err != nil {
|
||||
t.Errorf("test %d (%v) failed: %v", i, test.def, err)
|
||||
return
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
|
|
@ -78,7 +73,6 @@ func (test unpackTest) checkError(err error) error {
|
|||
} else if len(test.err) > 0 {
|
||||
return fmt.Errorf("expected err: %v but got none", test.err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -207,23 +201,6 @@ var unpackTests = []unpackTest{
|
|||
IntOne *big.Int
|
||||
}{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.
|
||||
|
|
@ -234,24 +211,19 @@ func TestLocalUnpackTests(t *testing.T) {
|
|||
//Unpack
|
||||
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
|
||||
abi, err := JSON(strings.NewReader(def))
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
||||
}
|
||||
|
||||
encb, err := hex.DecodeString(test.enc)
|
||||
if err != nil {
|
||||
t.Fatalf("invalid hex %s: %v", test.enc, err)
|
||||
}
|
||||
|
||||
outptr := reflect.New(reflect.TypeOf(test.want))
|
||||
|
||||
err = abi.UnpackIntoInterface(outptr.Interface(), "method", encb)
|
||||
if err := test.checkError(err); err != nil {
|
||||
t.Errorf("test %d (%v) failed: %v", i, test.def, err)
|
||||
return
|
||||
}
|
||||
|
||||
out := outptr.Elem().Interface()
|
||||
if !reflect.DeepEqual(test.want, out) {
|
||||
t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.want, out)
|
||||
|
|
@ -279,16 +251,13 @@ func TestUnpackIntoInterfaceSetDynamicArrayOutput(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(out32) != 2 {
|
||||
t.Fatalf("expected array with 2 values, got %d", len(out32))
|
||||
}
|
||||
|
||||
expected := common.Hex2Bytes("3078313233343536373839300000000000000000000000000000000000000000")
|
||||
if !bytes.Equal(out32[0][:], expected) {
|
||||
t.Errorf("expected %x, got %x\n", expected, out32[0])
|
||||
}
|
||||
|
||||
expected = common.Hex2Bytes("3078303938373635343332310000000000000000000000000000000000000000")
|
||||
if !bytes.Equal(out32[1][:], expected) {
|
||||
t.Errorf("expected %x, got %x\n", expected, out32[1])
|
||||
|
|
@ -299,16 +268,13 @@ func TestUnpackIntoInterfaceSetDynamicArrayOutput(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if len(out15) != 2 {
|
||||
t.Fatalf("expected array with 2 values, got %d", len(out15))
|
||||
}
|
||||
|
||||
expected = common.Hex2Bytes("307830313233343500000000000000")
|
||||
if !bytes.Equal(out15[0][:], expected) {
|
||||
t.Errorf("expected %x, got %x\n", expected, out15[0])
|
||||
}
|
||||
|
||||
expected = common.Hex2Bytes("307839383736353400000000000000")
|
||||
if !bytes.Equal(out15[1][:], expected) {
|
||||
t.Errorf("expected %x, got %x\n", expected, out15[1])
|
||||
|
|
@ -323,7 +289,6 @@ type methodMultiOutput struct {
|
|||
func methodMultiReturn(require *require.Assertions) (ABI, []byte, methodMultiOutput) {
|
||||
const definition = `[
|
||||
{ "name" : "multi", "type": "function", "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
|
||||
|
||||
var expected = methodMultiOutput{big.NewInt(1), "hello"}
|
||||
|
||||
abi, err := JSON(strings.NewReader(definition))
|
||||
|
|
@ -334,7 +299,6 @@ func methodMultiReturn(require *require.Assertions) (ABI, []byte, methodMultiOut
|
|||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
|
||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005"))
|
||||
buff.Write(common.RightPadBytes([]byte(expected.String), 32))
|
||||
|
||||
return abi, buff.Bytes(), expected
|
||||
}
|
||||
|
||||
|
|
@ -351,7 +315,6 @@ func TestMethodMultiReturn(t *testing.T) {
|
|||
|
||||
abi, data, expected := methodMultiReturn(require.New(t))
|
||||
bigint := new(big.Int)
|
||||
|
||||
var testCases = []struct {
|
||||
dest interface{}
|
||||
expected interface{}
|
||||
|
|
@ -372,11 +335,6 @@ func TestMethodMultiReturn(t *testing.T) {
|
|||
&[]interface{}{&expected.Int, &expected.String},
|
||||
"",
|
||||
"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{}{&expected.Int, &expected.String},
|
||||
|
|
@ -403,13 +361,11 @@ func TestMethodMultiReturn(t *testing.T) {
|
|||
"abi: insufficient number of arguments for unpack, want 2, got 1",
|
||||
"Can not unpack into a slice with wrong types",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
err := abi.UnpackIntoInterface(tc.dest, "multi", data)
|
||||
|
||||
if tc.error == "" {
|
||||
require.Nil(err, "Should be able to unpack method outputs.")
|
||||
require.Equal(tc.expected, tc.dest)
|
||||
|
|
@ -422,27 +378,22 @@ func TestMethodMultiReturn(t *testing.T) {
|
|||
|
||||
func TestMultiReturnWithArray(t *testing.T) {
|
||||
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"type": "uint64[3]"}, {"type": "uint64"}]}]`
|
||||
|
||||
abi, err := JSON(strings.NewReader(definition))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
buff := new(bytes.Buffer)
|
||||
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000900000000000000000000000000000000000000000000000000000000000000090000000000000000000000000000000000000000000000000000000000000009"))
|
||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000008"))
|
||||
|
||||
ret1, ret1Exp := new([3]uint64), [3]uint64{9, 9, 9}
|
||||
ret2, ret2Exp := new(uint64), uint64(8)
|
||||
|
||||
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
||||
t.Error("array result", *ret1, "!= Expected", ret1Exp)
|
||||
}
|
||||
|
||||
if *ret2 != ret2Exp {
|
||||
t.Error("int result", *ret2, "!= Expected", ret2Exp)
|
||||
}
|
||||
|
|
@ -450,37 +401,29 @@ func TestMultiReturnWithArray(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"}]}]`
|
||||
|
||||
abi, err := JSON(strings.NewReader(definition))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
buff := new(bytes.Buffer)
|
||||
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000005c1b78ea0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000001a055690d9db80000000000000000000000000000ab1257528b3782fb40d7ed5f72e624b744dffb2f00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001048656c6c6f2c20457468657265756d2100000000000000000000000000000000"))
|
||||
|
||||
temp, _ := new(big.Int).SetString("30000000000000000000", 10)
|
||||
temp, _ := big.NewInt(0).SetString("30000000000000000000", 10)
|
||||
ret1, ret1Exp := new([3]*big.Int), [3]*big.Int{big.NewInt(1545304298), big.NewInt(6), temp}
|
||||
ret2, ret2Exp := new(common.Address), common.HexToAddress("ab1257528b3782fb40d7ed5f72e624b744dffb2f")
|
||||
ret3, ret3Exp := new([2]string), [2]string{"Ethereum", "Hello, Ethereum!"}
|
||||
ret4, ret4Exp := new(bool), false
|
||||
|
||||
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2, ret3, ret4}, "multi", buff.Bytes()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
||||
t.Error("big.Int array result", *ret1, "!= Expected", ret1Exp)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*ret2, ret2Exp) {
|
||||
t.Error("address result", *ret2, "!= Expected", ret2Exp)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*ret3, ret3Exp) {
|
||||
t.Error("string array result", *ret3, "!= Expected", ret3Exp)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*ret4, ret4Exp) {
|
||||
t.Error("bool result", *ret4, "!= Expected", ret4Exp)
|
||||
}
|
||||
|
|
@ -488,12 +431,10 @@ func TestMultiReturnWithStringArray(t *testing.T) {
|
|||
|
||||
func TestMultiReturnWithStringSlice(t *testing.T) {
|
||||
const definition = `[{"name" : "multi", "type": "function", "outputs": [{"name": "","type": "string[]"},{"name": "","type": "uint256[]"}]}]`
|
||||
|
||||
abi, err := JSON(strings.NewReader(definition))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
buff := new(bytes.Buffer)
|
||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) // output[0] offset
|
||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000120")) // output[1] offset
|
||||
|
|
@ -507,18 +448,14 @@ func TestMultiReturnWithStringSlice(t *testing.T) {
|
|||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // output[1] length
|
||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000064")) // output[1][0] value
|
||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000065")) // output[1][1] value
|
||||
|
||||
ret1, ret1Exp := new([]string), []string{"ethereum", "go-ethereum"}
|
||||
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 {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
||||
t.Error("string slice result", *ret1, "!= Expected", ret1Exp)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*ret2, ret2Exp) {
|
||||
t.Error("uint256 slice result", *ret2, "!= Expected", ret2Exp)
|
||||
}
|
||||
|
|
@ -530,12 +467,10 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
|
|||
// after such nested array argument should be read with the correct offset,
|
||||
// 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"}]}]`
|
||||
|
||||
abi, err := JSON(strings.NewReader(definition))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
buff := new(bytes.Buffer)
|
||||
// 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.
|
||||
|
|
@ -555,15 +490,12 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
|
|||
{{0x411, 0x412, 0x413}, {0x421, 0x422, 0x423}},
|
||||
}
|
||||
ret2, ret2Exp := new(uint64), uint64(0x9876)
|
||||
|
||||
if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*ret1, ret1Exp) {
|
||||
t.Error("array result", *ret1, "!= Expected", ret1Exp)
|
||||
}
|
||||
|
||||
if *ret2 != ret2Exp {
|
||||
t.Error("int result", *ret2, "!= Expected", ret2Exp)
|
||||
}
|
||||
|
|
@ -585,7 +517,6 @@ func TestUnmarshal(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
buff := new(bytes.Buffer)
|
||||
|
||||
// marshall mixed bytes (mixedBytes)
|
||||
|
|
@ -613,7 +544,6 @@ func TestUnmarshal(t *testing.T) {
|
|||
|
||||
// marshal int
|
||||
var Int *big.Int
|
||||
|
||||
err = abi.UnpackIntoInterface(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
@ -625,7 +555,6 @@ func TestUnmarshal(t *testing.T) {
|
|||
|
||||
// marshal bool
|
||||
var Bool bool
|
||||
|
||||
err = abi.UnpackIntoInterface(&Bool, "bool", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
@ -643,7 +572,6 @@ func TestUnmarshal(t *testing.T) {
|
|||
buff.Write(bytesOut)
|
||||
|
||||
var Bytes []byte
|
||||
|
||||
err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
@ -711,7 +639,6 @@ func TestUnmarshal(t *testing.T) {
|
|||
buff.Write(common.RightPadBytes([]byte("hello"), 32))
|
||||
|
||||
var hash common.Hash
|
||||
|
||||
err = abi.UnpackIntoInterface(&hash, "fixed", buff.Bytes())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
|
@ -725,7 +652,6 @@ func TestUnmarshal(t *testing.T) {
|
|||
// marshal error
|
||||
buff.Reset()
|
||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
|
||||
|
||||
err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes())
|
||||
if err == nil {
|
||||
t.Error("expected error")
|
||||
|
|
@ -742,12 +668,10 @@ func TestUnmarshal(t *testing.T) {
|
|||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003"))
|
||||
// marshal int array
|
||||
var intArray [3]*big.Int
|
||||
|
||||
err = abi.UnpackIntoInterface(&intArray, "intArraySingle", buff.Bytes())
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
var testAgainstIntArray [3]*big.Int
|
||||
testAgainstIntArray[0] = big.NewInt(1)
|
||||
testAgainstIntArray[1] = big.NewInt(2)
|
||||
|
|
@ -765,7 +689,6 @@ func TestUnmarshal(t *testing.T) {
|
|||
buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"))
|
||||
|
||||
var outAddr []common.Address
|
||||
|
||||
err = abi.UnpackIntoInterface(&outAddr, "addressSliceSingle", buff.Bytes())
|
||||
if err != nil {
|
||||
t.Fatal("didn't expect error:", err)
|
||||
|
|
@ -793,7 +716,6 @@ func TestUnmarshal(t *testing.T) {
|
|||
A []common.Address
|
||||
B []common.Address
|
||||
}
|
||||
|
||||
err = abi.UnpackIntoInterface(&outAddrStruct, "addressSliceDouble", buff.Bytes())
|
||||
if err != nil {
|
||||
t.Fatal("didn't expect error:", err)
|
||||
|
|
@ -814,7 +736,6 @@ func TestUnmarshal(t *testing.T) {
|
|||
if outAddrStruct.B[0] != (common.Address{2}) {
|
||||
t.Errorf("expected %x, got %x", common.Address{2}, outAddrStruct.B[0])
|
||||
}
|
||||
|
||||
if outAddrStruct.B[1] != (common.Address{3}) {
|
||||
t.Errorf("expected %x, got %x", common.Address{3}, outAddrStruct.B[1])
|
||||
}
|
||||
|
|
@ -831,12 +752,10 @@ func TestUnmarshal(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"}]}]}]`
|
||||
|
||||
abi, err := JSON(strings.NewReader(simpleTuple))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
buff := new(bytes.Buffer)
|
||||
|
||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // ret[a] = 1
|
||||
|
|
@ -847,11 +766,9 @@ func TestUnpackTuple(t *testing.T) {
|
|||
A *big.Int
|
||||
B *big.Int
|
||||
}
|
||||
|
||||
type r struct {
|
||||
Result v
|
||||
}
|
||||
|
||||
var ret0 = new(r)
|
||||
err = abi.UnpackIntoInterface(ret0, "tuple", buff.Bytes())
|
||||
|
||||
|
|
@ -861,7 +778,6 @@ func TestUnpackTuple(t *testing.T) {
|
|||
if ret0.Result.A.Cmp(big.NewInt(1)) != 0 {
|
||||
t.Errorf("unexpected value unpacked: want %x, got %x", 1, ret0.Result.A)
|
||||
}
|
||||
|
||||
if ret0.Result.B.Cmp(big.NewInt(-1)) != 0 {
|
||||
t.Errorf("unexpected value unpacked: want %x, got %x", -1, ret0.Result.B)
|
||||
}
|
||||
|
|
@ -878,7 +794,6 @@ func TestUnpackTuple(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
buff.Reset()
|
||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000080")) // s offset
|
||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")) // t.X = 0
|
||||
|
|
@ -912,9 +827,7 @@ func TestUnpackTuple(t *testing.T) {
|
|||
FieldT T `abi:"t"`
|
||||
A *big.Int
|
||||
}
|
||||
|
||||
var ret Ret
|
||||
|
||||
var expected = Ret{
|
||||
FieldS: S{
|
||||
A: big.NewInt(1),
|
||||
|
|
@ -934,7 +847,6 @@ func TestUnpackTuple(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(ret, expected) {
|
||||
t.Error("unexpected unpack value")
|
||||
}
|
||||
|
|
@ -996,193 +908,16 @@ func TestOOMMaliciousInput(t *testing.T) {
|
|||
for i, test := range oomTests {
|
||||
def := fmt.Sprintf(`[{ "name" : "method", "type": "function", "outputs": %s}]`, test.def)
|
||||
abi, err := JSON(strings.NewReader(def))
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("invalid ABI definition %s: %v", def, err)
|
||||
}
|
||||
|
||||
encb, err := hex.DecodeString(test.enc)
|
||||
if err != nil {
|
||||
t.Fatalf("invalid hex: %s" + test.enc)
|
||||
}
|
||||
|
||||
_, err = abi.Methods["method"].Outputs.UnpackValues(encb)
|
||||
if err == nil {
|
||||
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])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
// 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,7 +178,6 @@ type Backend interface {
|
|||
// safely used to calculate a signature from.
|
||||
//
|
||||
// The hash is calculated as
|
||||
//
|
||||
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
|
||||
//
|
||||
// This gives context to the signed message and prevents signing of transactions.
|
||||
|
|
@ -191,7 +190,6 @@ func TextHash(data []byte) []byte {
|
|||
// safely used to calculate a signature from.
|
||||
//
|
||||
// The hash is calculated as
|
||||
//
|
||||
// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
|
||||
//
|
||||
// This gives context to the signed message and prevents signing of transactions.
|
||||
|
|
@ -199,7 +197,6 @@ func TextAndHash(data []byte) ([]byte, string) {
|
|||
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), string(data))
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
hasher.Write([]byte(msg))
|
||||
|
||||
return hasher.Sum(nil), msg
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import (
|
|||
func TestTextHash(t *testing.T) {
|
||||
hash := TextHash([]byte("Hello Joe"))
|
||||
want := hexutil.MustDecode("0xa080337ae51c4e064c189e113edd0ba391df9206e2f49db658bb32cf2911730b")
|
||||
|
||||
if !bytes.Equal(hash, want) {
|
||||
t.Fatalf("wrong hash: %x", hash)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ var ErrInvalidPassphrase = errors.New("invalid password")
|
|||
// second time.
|
||||
var ErrWalletAlreadyOpen = errors.New("wallet already open")
|
||||
|
||||
// ErrWalletClosed is returned if a wallet is offline.
|
||||
// ErrWalletClosed is returned if a wallet is attempted to be opened the
|
||||
// second time.
|
||||
var ErrWalletClosed = errors.New("wallet closed")
|
||||
|
||||
// AuthNeededError is returned by backends for signing requests where the user
|
||||
|
|
|
|||
29
accounts/external/backend.go
vendored
29
accounts/external/backend.go
vendored
|
|
@ -45,7 +45,6 @@ func NewExternalBackend(endpoint string) (*ExternalBackend, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ExternalBackend{
|
||||
signers: []accounts.Wallet{signer},
|
||||
}, nil
|
||||
|
|
@ -74,7 +73,6 @@ func NewExternalSigner(endpoint string) (*ExternalSigner, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extsigner := &ExternalSigner{
|
||||
client: client,
|
||||
endpoint: endpoint,
|
||||
|
|
@ -84,9 +82,7 @@ func NewExternalSigner(endpoint string) (*ExternalSigner, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extsigner.status = fmt.Sprintf("ok [version=%v]", version)
|
||||
|
||||
return extsigner, nil
|
||||
}
|
||||
|
||||
|
|
@ -111,13 +107,11 @@ func (api *ExternalSigner) Close() error {
|
|||
|
||||
func (api *ExternalSigner) Accounts() []accounts.Account {
|
||||
var accnts []accounts.Account
|
||||
|
||||
res, err := api.listAccounts()
|
||||
if err != nil {
|
||||
log.Error("account listing failed", "error", err)
|
||||
return accnts
|
||||
}
|
||||
|
||||
for _, addr := range res {
|
||||
accnts = append(accnts, accounts.Account{
|
||||
URL: accounts.URL{
|
||||
|
|
@ -127,31 +121,26 @@ func (api *ExternalSigner) Accounts() []accounts.Account {
|
|||
Address: addr,
|
||||
})
|
||||
}
|
||||
|
||||
api.cacheMu.Lock()
|
||||
api.cache = accnts
|
||||
api.cacheMu.Unlock()
|
||||
|
||||
return accnts
|
||||
}
|
||||
|
||||
func (api *ExternalSigner) Contains(account accounts.Account) bool {
|
||||
api.cacheMu.RLock()
|
||||
defer api.cacheMu.RUnlock()
|
||||
|
||||
if api.cache == nil {
|
||||
// If we haven't already fetched the accounts, it's time to do so now
|
||||
api.cacheMu.RUnlock()
|
||||
api.Accounts()
|
||||
api.cacheMu.RLock()
|
||||
}
|
||||
|
||||
for _, a := range api.cache {
|
||||
if a.Address == account.Address && (account.URL == (accounts.URL{}) || account.URL == api.URL()) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -163,10 +152,13 @@ func (api *ExternalSigner) SelfDerive(bases []accounts.DerivationPath, chain eth
|
|||
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
|
||||
func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
|
||||
var res hexutil.Bytes
|
||||
|
||||
var signAddress = common.NewMixedcaseAddress(account.Address)
|
||||
if err := api.client.Call(&res, "account_signData",
|
||||
mimeType,
|
||||
|
|
@ -178,13 +170,11 @@ func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, d
|
|||
if mimeType == accounts.MimetypeClique && (res[64] == 27 || res[64] == 28) {
|
||||
res[64] -= 27 // Transform V from 27/28 to 0/1 for Clique use
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]byte, error) {
|
||||
var signature hexutil.Bytes
|
||||
|
||||
var signAddress = common.NewMixedcaseAddress(account.Address)
|
||||
if err := api.client.Call(&signature, "account_signData",
|
||||
accounts.MimetypeTextPlain,
|
||||
|
|
@ -192,13 +182,11 @@ func (api *ExternalSigner) SignText(account accounts.Account, text []byte) ([]by
|
|||
hexutil.Encode(text)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if signature[64] == 27 || signature[64] == 28 {
|
||||
// If clef is used as a backend, it may already have transformed
|
||||
// the signature to ethereum-type signature.
|
||||
signature[64] -= 27 // Transform V from Ethereum-legacy to 0/1
|
||||
}
|
||||
|
||||
return signature, nil
|
||||
}
|
||||
|
||||
|
|
@ -214,14 +202,11 @@ type signTransactionResult struct {
|
|||
// transaction overrides the chainID parameter.
|
||||
func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||
data := hexutil.Bytes(tx.Data())
|
||||
|
||||
var to *common.MixedcaseAddress
|
||||
|
||||
if tx.To() != nil {
|
||||
t := common.NewMixedcaseAddress(*tx.To())
|
||||
to = &t
|
||||
}
|
||||
|
||||
args := &apitypes.SendTxArgs{
|
||||
Data: &data,
|
||||
Nonce: hexutil.Uint64(tx.Nonce()),
|
||||
|
|
@ -244,23 +229,19 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
|
|||
if chainID != nil && chainID.Sign() != 0 {
|
||||
args.ChainID = (*hexutil.Big)(chainID)
|
||||
}
|
||||
|
||||
if tx.Type() != types.LegacyTxType {
|
||||
// However, if the user asked for a particular chain id, then we should
|
||||
// use that instead.
|
||||
if tx.ChainId().Sign() != 0 {
|
||||
args.ChainID = (*hexutil.Big)(tx.ChainId())
|
||||
}
|
||||
|
||||
accessList := tx.AccessList()
|
||||
args.AccessList = &accessList
|
||||
}
|
||||
|
||||
var res signTransactionResult
|
||||
if err := api.client.Call(&res, "account_signTransaction", args); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res.Tx, nil
|
||||
}
|
||||
|
||||
|
|
@ -280,7 +261,6 @@ func (api *ExternalSigner) listAccounts() ([]common.Address, error) {
|
|||
if err := api.client.Call(&res, "account_list"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
|
|
@ -289,6 +269,5 @@ func (api *ExternalSigner) pingVersion() (string, error) {
|
|||
if err := api.client.Call(&v, "account_version"); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ var DefaultBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60,
|
|||
var LegacyLedgerBaseDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}
|
||||
|
||||
// DerivationPath represents the computer friendly version of a hierarchical
|
||||
// deterministic wallet account derivation path.
|
||||
// deterministic wallet account derivaion path.
|
||||
//
|
||||
// The BIP-32 spec https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||
// defines derivation paths to be of the form:
|
||||
|
|
@ -70,7 +70,6 @@ func ParseDerivationPath(path string) (DerivationPath, error) {
|
|||
|
||||
// Handle absolute or relative paths
|
||||
components := strings.Split(path, "/")
|
||||
|
||||
switch {
|
||||
case len(components) == 0:
|
||||
return nil, errors.New("empty derivation path")
|
||||
|
|
@ -88,11 +87,9 @@ func ParseDerivationPath(path string) (DerivationPath, error) {
|
|||
if len(components) == 0 {
|
||||
return nil, errors.New("empty derivation path") // Empty relative paths
|
||||
}
|
||||
|
||||
for _, component := range components {
|
||||
// Ignore any user added whitespace
|
||||
component = strings.TrimSpace(component)
|
||||
|
||||
var value uint32
|
||||
|
||||
// Handle hardened paths
|
||||
|
|
@ -105,22 +102,18 @@ func ParseDerivationPath(path string) (DerivationPath, error) {
|
|||
if !ok {
|
||||
return nil, fmt.Errorf("invalid component: %s", component)
|
||||
}
|
||||
|
||||
max := math.MaxUint32 - value
|
||||
if bigval.Sign() < 0 || bigval.Cmp(big.NewInt(int64(max))) > 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 hardened range [0, %d]", bigval, max)
|
||||
}
|
||||
|
||||
value += uint32(bigval.Uint64())
|
||||
|
||||
// Append and repeat
|
||||
result = append(result, value)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
|
|
@ -128,21 +121,17 @@ func ParseDerivationPath(path string) (DerivationPath, error) {
|
|||
// to its canonical representation.
|
||||
func (path DerivationPath) String() string {
|
||||
result := "m"
|
||||
|
||||
for _, component := range path {
|
||||
var hardened bool
|
||||
|
||||
if component >= 0x80000000 {
|
||||
component -= 0x80000000
|
||||
hardened = true
|
||||
}
|
||||
|
||||
result = fmt.Sprintf("%s/%d", result, component)
|
||||
if hardened {
|
||||
result += "'"
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
|
@ -154,14 +143,11 @@ func (path DerivationPath) MarshalJSON() ([]byte, error) {
|
|||
// UnmarshalJSON a json-serialized string back into a derivation path
|
||||
func (path *DerivationPath) UnmarshalJSON(b []byte) error {
|
||||
var dp string
|
||||
|
||||
var err error
|
||||
if err = json.Unmarshal(b, &dp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*path, err = ParseDerivationPath(dp)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +158,6 @@ func DefaultIterator(base DerivationPath) func() DerivationPath {
|
|||
copy(path[:], base[:])
|
||||
// Set it back by one, so the first call gives the first result
|
||||
path[len(path)-1]--
|
||||
|
||||
return func() DerivationPath {
|
||||
path[len(path)-1]++
|
||||
return path
|
||||
|
|
@ -187,7 +172,6 @@ func LedgerLiveIterator(base DerivationPath) func() DerivationPath {
|
|||
copy(path[:], base[:])
|
||||
// Set it back by one, so the first call gives the first result
|
||||
path[2]--
|
||||
|
||||
return func() DerivationPath {
|
||||
// ledgerLivePathIterator iterates on the third component
|
||||
path[2]++
|
||||
|
|
|
|||
|
|
@ -81,7 +81,6 @@ func TestHDPathParsing(t *testing.T) {
|
|||
|
||||
func testDerive(t *testing.T, next func() DerivationPath, expected []string) {
|
||||
t.Helper()
|
||||
|
||||
for i, want := range expected {
|
||||
if have := next(); fmt.Sprintf("%v", have) != want {
|
||||
t.Errorf("step %d, have %v, want %v", i, have, want)
|
||||
|
|
|
|||
|
|
@ -27,8 +27,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
|
@ -60,7 +59,6 @@ func (err *AmbiguousAddrError) Error() string {
|
|||
files += ", "
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("multiple keys match address (%s)", files)
|
||||
}
|
||||
|
||||
|
|
@ -81,10 +79,9 @@ func newAccountCache(keydir string) (*accountCache, chan struct{}) {
|
|||
keydir: keydir,
|
||||
byAddr: make(map[common.Address][]accounts.Account),
|
||||
notify: make(chan struct{}, 1),
|
||||
fileC: fileCache{all: mapset.NewThreadUnsafeSet[string]()},
|
||||
fileC: fileCache{all: mapset.NewThreadUnsafeSet()},
|
||||
}
|
||||
ac.watcher = newWatcher(ac)
|
||||
|
||||
return ac, ac.notify
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +91,6 @@ func (ac *accountCache) accounts() []accounts.Account {
|
|||
defer ac.mu.Unlock()
|
||||
cpy := make([]accounts.Account, len(ac.all))
|
||||
copy(cpy, ac.all)
|
||||
|
||||
return cpy
|
||||
}
|
||||
|
||||
|
|
@ -102,7 +98,6 @@ func (ac *accountCache) hasAddress(addr common.Address) bool {
|
|||
ac.maybeReload()
|
||||
ac.mu.Lock()
|
||||
defer ac.mu.Unlock()
|
||||
|
||||
return len(ac.byAddr[addr]) > 0
|
||||
}
|
||||
|
||||
|
|
@ -143,7 +138,6 @@ func (ac *accountCache) deleteByFile(path string) {
|
|||
if i < len(ac.all) && ac.all[i].URL.Path == path {
|
||||
removed := ac.all[i]
|
||||
ac.all = append(ac.all[:i], ac.all[i+1:]...)
|
||||
|
||||
if ba := removeAccount(ac.byAddr[removed.Address], removed); len(ba) == 0 {
|
||||
delete(ac.byAddr, removed.Address)
|
||||
} else {
|
||||
|
|
@ -152,22 +146,12 @@ 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 {
|
||||
for i := range slice {
|
||||
if slice[i] == elem {
|
||||
return append(slice[:i], slice[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
return slice
|
||||
}
|
||||
|
||||
|
|
@ -180,24 +164,20 @@ func (ac *accountCache) find(a accounts.Account) (accounts.Account, error) {
|
|||
if (a.Address != common.Address{}) {
|
||||
matches = ac.byAddr[a.Address]
|
||||
}
|
||||
|
||||
if a.URL.Path != "" {
|
||||
// If only the basename is specified, complete the path.
|
||||
if !strings.ContainsRune(a.URL.Path, filepath.Separator) {
|
||||
a.URL.Path = filepath.Join(ac.keydir, a.URL.Path)
|
||||
}
|
||||
|
||||
for i := range matches {
|
||||
if matches[i].URL == a.URL {
|
||||
return matches[i], nil
|
||||
}
|
||||
}
|
||||
|
||||
if (a.Address == common.Address{}) {
|
||||
return accounts.Account{}, ErrNoMatch
|
||||
}
|
||||
}
|
||||
|
||||
switch len(matches) {
|
||||
case 1:
|
||||
return matches[0], nil
|
||||
|
|
@ -207,7 +187,6 @@ func (ac *accountCache) find(a accounts.Account) (accounts.Account, error) {
|
|||
err := &AmbiguousAddrError{Addr: a.Address, Matches: make([]accounts.Account, len(matches))}
|
||||
copy(err.Matches, matches)
|
||||
sort.Sort(accountsByURL(err.Matches))
|
||||
|
||||
return accounts.Account{}, err
|
||||
}
|
||||
}
|
||||
|
|
@ -219,7 +198,6 @@ func (ac *accountCache) maybeReload() {
|
|||
ac.mu.Unlock()
|
||||
return // A watcher is running and will keep the cache up-to-date.
|
||||
}
|
||||
|
||||
if ac.throttle == nil {
|
||||
ac.throttle = time.NewTimer(0)
|
||||
} else {
|
||||
|
|
@ -240,11 +218,9 @@ func (ac *accountCache) maybeReload() {
|
|||
func (ac *accountCache) close() {
|
||||
ac.mu.Lock()
|
||||
ac.watcher.close()
|
||||
|
||||
if ac.throttle != nil {
|
||||
ac.throttle.Stop()
|
||||
}
|
||||
|
||||
if ac.notify != nil {
|
||||
close(ac.notify)
|
||||
ac.notify = nil
|
||||
|
|
@ -261,7 +237,6 @@ func (ac *accountCache) scanAccounts() error {
|
|||
log.Debug("Failed to reload keystore contents", "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if creates.Cardinality() == 0 && deletes.Cardinality() == 0 && updates.Cardinality() == 0 {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -272,21 +247,18 @@ func (ac *accountCache) scanAccounts() error {
|
|||
Address string `json:"address"`
|
||||
}
|
||||
)
|
||||
|
||||
readAccount := func(path string) *accounts.Account {
|
||||
fd, err := os.Open(path)
|
||||
if err != nil {
|
||||
log.Trace("Failed to open keystore file", "path", path, "err", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
defer fd.Close()
|
||||
buf.Reset(fd)
|
||||
// Parse the address.
|
||||
key.Address = ""
|
||||
err = json.NewDecoder(buf).Decode(&key)
|
||||
addr := common.HexToAddress(key.Address)
|
||||
|
||||
switch {
|
||||
case err != nil:
|
||||
log.Debug("Failed to decode keystore key", "path", path, "err", err)
|
||||
|
|
@ -298,30 +270,26 @@ func (ac *accountCache) scanAccounts() error {
|
|||
URL: accounts.URL{Scheme: KeyStoreScheme, Path: path},
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
// Process all the file diffs
|
||||
start := time.Now()
|
||||
|
||||
for _, path := range creates.ToSlice() {
|
||||
for _, p := 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 {
|
||||
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()
|
||||
|
||||
select {
|
||||
|
|
@ -329,6 +297,5 @@ func (ac *accountCache) scanAccounts() error {
|
|||
default:
|
||||
}
|
||||
log.Trace("Handled keystore changes", "time", end.Sub(start))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package keystore
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
|
@ -50,52 +51,16 @@ 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) {
|
||||
t.Parallel()
|
||||
|
||||
dir, ks := tmpKeyStore(t, false)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// Ensure the watcher is started before adding any files.
|
||||
ks.Accounts()
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
|
||||
if !waitWatcherStart(ks) {
|
||||
t.Fatal("keystore watcher didn't start in time")
|
||||
}
|
||||
// Move in the files.
|
||||
wantAccounts := make([]accounts.Account, len(cachetestAccounts))
|
||||
for i := range cachetestAccounts {
|
||||
|
|
@ -109,14 +74,28 @@ func TestWatchNewFile(t *testing.T) {
|
|||
}
|
||||
|
||||
// ks should see the accounts.
|
||||
if err := waitForAccounts(wantAccounts, ks); err != nil {
|
||||
t.Error(err)
|
||||
var list []accounts.Account
|
||||
for d := 200 * time.Millisecond; d < 5*time.Second; d *= 2 {
|
||||
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) {
|
||||
t.Parallel()
|
||||
|
||||
// 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()))
|
||||
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
||||
|
||||
|
|
@ -124,14 +103,11 @@ func TestWatchNoDir(t *testing.T) {
|
|||
if len(list) > 0 {
|
||||
t.Error("initial account list not empty:", list)
|
||||
}
|
||||
// The watcher should start, and then exit.
|
||||
if !waitWatcherStart(ks) {
|
||||
t.Fatal("keystore watcher didn't start in time")
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
// Create the directory and copy a key file into it.
|
||||
os.MkdirAll(dir, 0700)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
file := filepath.Join(dir, "aaa")
|
||||
if err := cp.CopyFile(file, cachetestAccounts[0].URL.Path); err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -140,7 +116,6 @@ func TestWatchNoDir(t *testing.T) {
|
|||
// ks should see the account.
|
||||
wantAccounts := []accounts.Account{cachetestAccounts[0]}
|
||||
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
|
||||
|
||||
for d := 200 * time.Millisecond; d < 8*time.Second; d *= 2 {
|
||||
list = ks.Accounts()
|
||||
if reflect.DeepEqual(list, wantAccounts) {
|
||||
|
|
@ -150,10 +125,8 @@ func TestWatchNoDir(t *testing.T) {
|
|||
default:
|
||||
t.Fatalf("wasn't notified of new accounts")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(d)
|
||||
}
|
||||
t.Errorf("\ngot %v\nwant %v", list, wantAccounts)
|
||||
|
|
@ -161,7 +134,6 @@ func TestWatchNoDir(t *testing.T) {
|
|||
|
||||
func TestCacheInitialReload(t *testing.T) {
|
||||
cache, _ := newAccountCache(cachetestDir)
|
||||
|
||||
accounts := cache.accounts()
|
||||
if !reflect.DeepEqual(accounts, cachetestAccounts) {
|
||||
t.Fatalf("got initial accounts: %swant %s", spew.Sdump(accounts), spew.Sdump(cachetestAccounts))
|
||||
|
|
@ -213,18 +185,15 @@ func TestCacheAddDeleteOrder(t *testing.T) {
|
|||
wantAccounts := make([]accounts.Account, len(accs))
|
||||
copy(wantAccounts, accs)
|
||||
sort.Sort(accountsByURL(wantAccounts))
|
||||
|
||||
list := cache.accounts()
|
||||
if !reflect.DeepEqual(list, wantAccounts) {
|
||||
t.Fatalf("got accounts: %s\nwant %s", spew.Sdump(accs), spew.Sdump(wantAccounts))
|
||||
}
|
||||
|
||||
for _, a := range accs {
|
||||
if !cache.hasAddress(a.Address) {
|
||||
t.Errorf("expected hasAccount(%x) to return true", a.Address)
|
||||
}
|
||||
}
|
||||
|
||||
if cache.hasAddress(common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e")) {
|
||||
t.Errorf("expected hasAccount(%x) to return false", common.HexToAddress("fd9bd350f08ee3c0c19b85a8e16114a11a60aa4e"))
|
||||
}
|
||||
|
|
@ -242,17 +211,14 @@ func TestCacheAddDeleteOrder(t *testing.T) {
|
|||
wantAccounts[5],
|
||||
}
|
||||
list = cache.accounts()
|
||||
|
||||
if !reflect.DeepEqual(list, wantAccountsAfterDelete) {
|
||||
t.Fatalf("got accounts after delete: %s\nwant %s", spew.Sdump(list), spew.Sdump(wantAccountsAfterDelete))
|
||||
}
|
||||
|
||||
for _, a := range wantAccountsAfterDelete {
|
||||
if !cache.hasAddress(a.Address) {
|
||||
t.Errorf("expected hasAccount(%x) to return true", a.Address)
|
||||
}
|
||||
}
|
||||
|
||||
if cache.hasAddress(wantAccounts[0].Address) {
|
||||
t.Errorf("expected hasAccount(%x) to return false", wantAccounts[0].Address)
|
||||
}
|
||||
|
|
@ -289,7 +255,6 @@ func TestCacheFind(t *testing.T) {
|
|||
Address: common.HexToAddress("f466859ead1932d743d622cb74fc058882e8648a"),
|
||||
URL: accounts.URL{Scheme: KeyStoreScheme, Path: filepath.Join(dir, "something")},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
Query accounts.Account
|
||||
WantResult accounts.Account
|
||||
|
|
@ -325,7 +290,6 @@ func TestCacheFind(t *testing.T) {
|
|||
t.Errorf("test %d: error mismatch for query %v\ngot %q\nwant %q", i, test.Query, err, test.WantError)
|
||||
continue
|
||||
}
|
||||
|
||||
if a != test.WantResult {
|
||||
t.Errorf("test %d: result mismatch for query %v\ngot %v\nwant %v", i, test.Query, a, test.WantResult)
|
||||
continue
|
||||
|
|
@ -333,12 +297,31 @@ 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
|
||||
// is noticed by the watcher, and the account cache is updated accordingly
|
||||
func TestUpdatedKeyfileContents(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// Create a temporary keystore to test with
|
||||
// Create a temporary kesytore 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()))
|
||||
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
|
||||
|
||||
|
|
@ -346,10 +329,8 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
|||
if len(list) > 0 {
|
||||
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.
|
||||
os.MkdirAll(dir, 0700)
|
||||
defer os.RemoveAll(dir)
|
||||
|
|
@ -363,72 +344,63 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
|||
// ks should see the account.
|
||||
wantAccounts := []accounts.Account{cachetestAccounts[0]}
|
||||
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
|
||||
|
||||
if err := waitForAccounts(wantAccounts, ks); err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
// needed so that modTime of `file` is different to its current value after forceCopyFile
|
||||
time.Sleep(time.Second)
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
|
||||
// Now replace file contents
|
||||
if err := forceCopyFile(file, cachetestAccounts[1].URL.Path); err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
wantAccounts = []accounts.Account{cachetestAccounts[1]}
|
||||
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
|
||||
|
||||
if err := waitForAccounts(wantAccounts, ks); err != nil {
|
||||
t.Errorf("First replacement failed")
|
||||
t.Error(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// needed so that modTime of `file` is different to its current value after forceCopyFile
|
||||
time.Sleep(time.Second)
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
|
||||
// Now replace file contents again
|
||||
if err := forceCopyFile(file, cachetestAccounts[2].URL.Path); err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
wantAccounts = []accounts.Account{cachetestAccounts[2]}
|
||||
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
|
||||
|
||||
if err := waitForAccounts(wantAccounts, ks); err != nil {
|
||||
t.Errorf("Second replacement failed")
|
||||
t.Error(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// needed so that modTime of `file` is different to its current value after os.WriteFile
|
||||
time.Sleep(time.Second)
|
||||
// needed so that modTime of `file` is different to its current value after ioutil.WriteFile
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
|
||||
// Now replace file contents with crap
|
||||
if err := os.WriteFile(file, []byte("foo"), 0600); err != nil {
|
||||
if err := ioutil.WriteFile(file, []byte("foo"), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := waitForAccounts([]accounts.Account{}, ks); err != nil {
|
||||
t.Errorf("Emptying account file failed")
|
||||
t.Error(err)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// forceCopyFile is like cp.CopyFile, but doesn't complain if the destination exists.
|
||||
func forceCopyFile(dst, src string) error {
|
||||
data, err := os.ReadFile(src)
|
||||
data, err := ioutil.ReadFile(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.WriteFile(dst, data, 0644)
|
||||
return ioutil.WriteFile(dst, data, 0644)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,46 +17,44 @@
|
|||
package keystore
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
|
||||
mapset "github.com/deckarep/golang-set"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
)
|
||||
|
||||
// fileCache is a cache of files seen during scan of keystore.
|
||||
type fileCache struct {
|
||||
all mapset.Set[string] // Set of all files from the keystore folder
|
||||
all mapset.Set // Set of all files from the keystore folder
|
||||
lastMod time.Time // Last time instance when a file was modified
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// scan performs a new scan on the given directory, compares against the already
|
||||
// cached filenames, and returns file sets: creates, deletes, updates.
|
||||
func (fc *fileCache) scan(keyDir string) (mapset.Set[string], mapset.Set[string], mapset.Set[string], error) {
|
||||
func (fc *fileCache) scan(keyDir string) (mapset.Set, mapset.Set, mapset.Set, error) {
|
||||
t0 := time.Now()
|
||||
|
||||
// List all the files from the keystore folder
|
||||
files, err := os.ReadDir(keyDir)
|
||||
// List all the fails from the keystore folder
|
||||
files, err := ioutil.ReadDir(keyDir)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
t1 := time.Now()
|
||||
|
||||
fc.mu.Lock()
|
||||
defer fc.mu.Unlock()
|
||||
|
||||
// Iterate all the files and gather their metadata
|
||||
all := mapset.NewThreadUnsafeSet[string]()
|
||||
mods := mapset.NewThreadUnsafeSet[string]()
|
||||
all := mapset.NewThreadUnsafeSet()
|
||||
mods := mapset.NewThreadUnsafeSet()
|
||||
|
||||
var newLastMod time.Time
|
||||
|
||||
for _, fi := range files {
|
||||
path := filepath.Join(keyDir, fi.Name())
|
||||
// Skip any non-key files from the folder
|
||||
|
|
@ -64,24 +62,17 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set[string], mapset.Set[string]
|
|||
log.Trace("Ignoring file on account scan", "path", path)
|
||||
continue
|
||||
}
|
||||
// Gather the set of all and freshly modified files
|
||||
// Gather the set of all and fresly modified files
|
||||
all.Add(path)
|
||||
|
||||
info, err := fi.Info()
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
modified := info.ModTime()
|
||||
modified := fi.ModTime()
|
||||
if modified.After(fc.lastMod) {
|
||||
mods.Add(path)
|
||||
}
|
||||
|
||||
if modified.After(newLastMod) {
|
||||
newLastMod = modified
|
||||
}
|
||||
}
|
||||
|
||||
t2 := time.Now()
|
||||
|
||||
// Update the tracked files and return the three sets
|
||||
|
|
@ -94,20 +85,18 @@ func (fc *fileCache) scan(keyDir string) (mapset.Set[string], mapset.Set[string]
|
|||
|
||||
// Report on the scanning stats and return
|
||||
log.Debug("FS scan times", "list", t1.Sub(t0), "set", t2.Sub(t1), "diff", t3.Sub(t2))
|
||||
|
||||
return creates, deletes, updates, nil
|
||||
}
|
||||
|
||||
// nonKeyFile ignores editor backups, hidden files and folders/symlinks.
|
||||
func nonKeyFile(fi os.DirEntry) bool {
|
||||
func nonKeyFile(fi os.FileInfo) bool {
|
||||
// Skip editor backups and UNIX-style hidden files.
|
||||
if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") {
|
||||
return true
|
||||
}
|
||||
// Skip misc special files, directories (yes, symlinks too).
|
||||
if fi.IsDir() || !fi.Type().IsRegular() {
|
||||
if fi.IsDir() || fi.Mode()&os.ModeType != 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
|
@ -98,32 +99,26 @@ func (k *Key) MarshalJSON() (j []byte, err error) {
|
|||
version,
|
||||
}
|
||||
j, err = json.Marshal(jStruct)
|
||||
|
||||
return j, err
|
||||
}
|
||||
|
||||
func (k *Key) UnmarshalJSON(j []byte) (err error) {
|
||||
keyJSON := new(plainKeyJSON)
|
||||
|
||||
err = json.Unmarshal(j, &keyJSON)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u := new(uuid.UUID)
|
||||
|
||||
*u, err = uuid.Parse(keyJSON.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
k.Id = *u
|
||||
|
||||
addr, err := hex.DecodeString(keyJSON.Address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
privkey, err := crypto.HexToECDSA(keyJSON.PrivateKey)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -140,13 +135,11 @@ func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
|
|||
if err != nil {
|
||||
panic(fmt.Sprintf("Could not create random uuid: %v", err))
|
||||
}
|
||||
|
||||
key := &Key{
|
||||
Id: id,
|
||||
Address: crypto.PubkeyToAddress(privateKeyECDSA.PublicKey),
|
||||
PrivateKey: privateKeyECDSA,
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
|
|
@ -155,24 +148,19 @@ func newKeyFromECDSA(privateKeyECDSA *ecdsa.PrivateKey) *Key {
|
|||
// retry until the first byte is 0.
|
||||
func NewKeyForDirectICAP(rand io.Reader) *Key {
|
||||
randBytes := make([]byte, 64)
|
||||
|
||||
_, err := rand.Read(randBytes)
|
||||
if err != nil {
|
||||
panic("key generation: could not read from random source: " + err.Error())
|
||||
}
|
||||
|
||||
reader := bytes.NewReader(randBytes)
|
||||
|
||||
privateKeyECDSA, err := ecdsa.GenerateKey(crypto.S256(), reader)
|
||||
if err != nil {
|
||||
panic("key generation: ecdsa.GenerateKey failed: " + err.Error())
|
||||
}
|
||||
|
||||
key := newKeyFromECDSA(privateKeyECDSA)
|
||||
if !strings.HasPrefix(key.Address.Hex(), "0x00") {
|
||||
return NewKeyForDirectICAP(rand)
|
||||
}
|
||||
|
||||
return key
|
||||
}
|
||||
|
||||
|
|
@ -181,7 +169,6 @@ func newKey(rand io.Reader) (*Key, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newKeyFromECDSA(privateKeyECDSA), nil
|
||||
}
|
||||
|
||||
|
|
@ -190,7 +177,6 @@ func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Accou
|
|||
if err != nil {
|
||||
return nil, accounts.Account{}, err
|
||||
}
|
||||
|
||||
a := accounts.Account{
|
||||
Address: key.Address,
|
||||
URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))},
|
||||
|
|
@ -199,7 +185,6 @@ func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Accou
|
|||
zeroKey(key.PrivateKey)
|
||||
return nil, a, err
|
||||
}
|
||||
|
||||
return key, a, err
|
||||
}
|
||||
|
||||
|
|
@ -212,20 +197,16 @@ func writeTemporaryKeyFile(file string, content []byte) (string, error) {
|
|||
}
|
||||
// Atomic write: create a temporary hidden file first
|
||||
// then move it into place. TempFile assigns mode 0600.
|
||||
f, err := os.CreateTemp(filepath.Dir(file), "."+filepath.Base(file)+".tmp")
|
||||
f, err := ioutil.TempFile(filepath.Dir(file), "."+filepath.Base(file)+".tmp")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if _, err := f.Write(content); err != nil {
|
||||
f.Close()
|
||||
os.Remove(f.Name())
|
||||
|
||||
return "", err
|
||||
}
|
||||
|
||||
f.Close()
|
||||
|
||||
return f.Name(), nil
|
||||
}
|
||||
|
||||
|
|
@ -234,7 +215,6 @@ func writeKeyFile(file string, content []byte) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Rename(name, file)
|
||||
}
|
||||
|
||||
|
|
@ -247,14 +227,12 @@ func keyFileName(keyAddr common.Address) string {
|
|||
|
||||
func toISO8601(t time.Time) string {
|
||||
var tz string
|
||||
|
||||
name, offset := t.Zone()
|
||||
if name == "UTC" {
|
||||
tz = "Z"
|
||||
} else {
|
||||
tz = fmt.Sprintf("%03d00", offset/3600)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,6 @@ func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore {
|
|||
keydir, _ = filepath.Abs(keydir)
|
||||
ks := &KeyStore{storage: &keyStorePassphrase{keydir, scryptN, scryptP, false}}
|
||||
ks.init(keydir)
|
||||
|
||||
return ks
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +93,6 @@ func NewPlaintextKeyStore(keydir string) *KeyStore {
|
|||
keydir, _ = filepath.Abs(keydir)
|
||||
ks := &KeyStore{storage: &keyStorePlain{keydir}}
|
||||
ks.init(keydir)
|
||||
|
||||
return ks
|
||||
}
|
||||
|
||||
|
|
@ -116,7 +114,6 @@ func (ks *KeyStore) init(keydir string) {
|
|||
// Create the initial list of wallets from the cache
|
||||
accs := ks.cache.accounts()
|
||||
ks.wallets = make([]accounts.Wallet, len(accs))
|
||||
|
||||
for i := 0; i < len(accs); i++ {
|
||||
ks.wallets[i] = &keystoreWallet{account: accs[i], keystore: ks}
|
||||
}
|
||||
|
|
@ -133,7 +130,6 @@ func (ks *KeyStore) Wallets() []accounts.Wallet {
|
|||
|
||||
cpy := make([]accounts.Wallet, len(ks.wallets))
|
||||
copy(cpy, ks.wallets)
|
||||
|
||||
return cpy
|
||||
}
|
||||
|
||||
|
|
@ -162,14 +158,12 @@ func (ks *KeyStore) refreshWallets() {
|
|||
|
||||
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
|
||||
wallets = append(wallets, wallet)
|
||||
|
||||
continue
|
||||
}
|
||||
// If the account is the same as the first wallet, keep it
|
||||
if ks.wallets[0].Accounts()[0] == account {
|
||||
wallets = append(wallets, ks.wallets[0])
|
||||
ks.wallets = ks.wallets[1:]
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
|
@ -177,7 +171,6 @@ func (ks *KeyStore) refreshWallets() {
|
|||
for _, wallet := range ks.wallets {
|
||||
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
|
||||
}
|
||||
|
||||
ks.wallets = wallets
|
||||
ks.mu.Unlock()
|
||||
|
||||
|
|
@ -202,7 +195,6 @@ func (ks *KeyStore) Subscribe(sink chan<- accounts.WalletEvent) event.Subscripti
|
|||
ks.updating = true
|
||||
go ks.updater()
|
||||
}
|
||||
|
||||
return sub
|
||||
}
|
||||
|
||||
|
|
@ -226,7 +218,6 @@ func (ks *KeyStore) updater() {
|
|||
if ks.updateScope.Count() == 0 {
|
||||
ks.updating = false
|
||||
ks.mu.Unlock()
|
||||
|
||||
return
|
||||
}
|
||||
ks.mu.Unlock()
|
||||
|
|
@ -253,7 +244,6 @@ func (ks *KeyStore) Delete(a accounts.Account, passphrase string) error {
|
|||
if key != nil {
|
||||
zeroKey(key.PrivateKey)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -265,7 +255,6 @@ func (ks *KeyStore) Delete(a accounts.Account, passphrase string) error {
|
|||
ks.cache.delete(a)
|
||||
ks.refreshWallets()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -296,7 +285,6 @@ 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
|
||||
signer := types.LatestSignerForChainID(chainID)
|
||||
|
||||
return types.SignTx(tx, signer, unlockedKey.PrivateKey)
|
||||
}
|
||||
|
||||
|
|
@ -308,9 +296,7 @@ func (ks *KeyStore) SignHashWithPassphrase(a accounts.Account, passphrase string
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer zeroKey(key.PrivateKey)
|
||||
|
||||
return crypto.Sign(hash, key.PrivateKey)
|
||||
}
|
||||
|
||||
|
|
@ -324,7 +310,6 @@ func (ks *KeyStore) SignTxWithPassphrase(a accounts.Account, passphrase string,
|
|||
defer zeroKey(key.PrivateKey)
|
||||
// Depending on the presence of the chain ID, sign with or without replay protection.
|
||||
signer := types.LatestSignerForChainID(chainID)
|
||||
|
||||
return types.SignTx(tx, signer, key.PrivateKey)
|
||||
}
|
||||
|
||||
|
|
@ -342,7 +327,6 @@ func (ks *KeyStore) Lock(addr common.Address) error {
|
|||
} else {
|
||||
ks.mu.Unlock()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -361,7 +345,6 @@ func (ks *KeyStore) TimedUnlock(a accounts.Account, passphrase string, timeout t
|
|||
|
||||
ks.mu.Lock()
|
||||
defer ks.mu.Unlock()
|
||||
|
||||
u, found := ks.unlocked[a.Address]
|
||||
if found {
|
||||
if u.abort == nil {
|
||||
|
|
@ -373,16 +356,13 @@ func (ks *KeyStore) TimedUnlock(a accounts.Account, passphrase string, timeout t
|
|||
// Terminate the expire goroutine and replace it below.
|
||||
close(u.abort)
|
||||
}
|
||||
|
||||
if timeout > 0 {
|
||||
u = &unlocked{Key: key, abort: make(chan struct{})}
|
||||
go ks.expire(a.Address, u, timeout)
|
||||
} else {
|
||||
u = &unlocked{Key: key}
|
||||
}
|
||||
|
||||
ks.unlocked[a.Address] = u
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -392,7 +372,6 @@ func (ks *KeyStore) Find(a accounts.Account) (accounts.Account, error) {
|
|||
ks.cache.mu.Lock()
|
||||
a, err := ks.cache.find(a)
|
||||
ks.cache.mu.Unlock()
|
||||
|
||||
return a, err
|
||||
}
|
||||
|
||||
|
|
@ -401,9 +380,7 @@ func (ks *KeyStore) getDecryptedKey(a accounts.Account, auth string) (accounts.A
|
|||
if err != nil {
|
||||
return a, nil, err
|
||||
}
|
||||
|
||||
key, err := ks.storage.GetKey(a.Address, a.URL.Path, auth)
|
||||
|
||||
return a, key, err
|
||||
}
|
||||
|
||||
|
|
@ -438,7 +415,6 @@ func (ks *KeyStore) NewAccount(passphrase string) (accounts.Account, error) {
|
|||
// than waiting for file system notifications to pick it up.
|
||||
ks.cache.add(account)
|
||||
ks.refreshWallets()
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
||||
|
|
@ -448,14 +424,12 @@ func (ks *KeyStore) Export(a accounts.Account, passphrase, newPassphrase string)
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var N, P int
|
||||
if store, ok := ks.storage.(*keyStorePassphrase); ok {
|
||||
N, P = store.scryptN, store.scryptP
|
||||
} else {
|
||||
N, P = StandardScryptN, StandardScryptP
|
||||
}
|
||||
|
||||
return EncryptKey(key, newPassphrase, N, P)
|
||||
}
|
||||
|
||||
|
|
@ -465,11 +439,9 @@ func (ks *KeyStore) Import(keyJSON []byte, passphrase, newPassphrase string) (ac
|
|||
if key != nil && key.PrivateKey != nil {
|
||||
defer zeroKey(key.PrivateKey)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return accounts.Account{}, err
|
||||
}
|
||||
|
||||
ks.importMu.Lock()
|
||||
defer ks.importMu.Unlock()
|
||||
|
||||
|
|
@ -478,7 +450,6 @@ func (ks *KeyStore) Import(keyJSON []byte, passphrase, newPassphrase string) (ac
|
|||
Address: key.Address,
|
||||
}, ErrAccountAlreadyExists
|
||||
}
|
||||
|
||||
return ks.importKey(key, newPassphrase)
|
||||
}
|
||||
|
||||
|
|
@ -493,7 +464,6 @@ func (ks *KeyStore) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (acco
|
|||
Address: key.Address,
|
||||
}, ErrAccountAlreadyExists
|
||||
}
|
||||
|
||||
return ks.importKey(key, passphrase)
|
||||
}
|
||||
|
||||
|
|
@ -502,10 +472,8 @@ func (ks *KeyStore) importKey(key *Key, passphrase string) (accounts.Account, er
|
|||
if err := ks.storage.StoreKey(a.URL.Path, key, passphrase); err != nil {
|
||||
return accounts.Account{}, err
|
||||
}
|
||||
|
||||
ks.cache.add(a)
|
||||
ks.refreshWallets()
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
|
|
@ -515,7 +483,6 @@ func (ks *KeyStore) Update(a accounts.Account, passphrase, newPassphrase string)
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ks.storage.StoreKey(a.URL.Path, key, newPassphrase)
|
||||
}
|
||||
|
||||
|
|
@ -526,22 +493,11 @@ func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (account
|
|||
if err != nil {
|
||||
return a, err
|
||||
}
|
||||
|
||||
ks.cache.add(a)
|
||||
ks.refreshWallets()
|
||||
|
||||
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.
|
||||
func zeroKey(k *ecdsa.PrivateKey) {
|
||||
b := k.D.Bits()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package keystore
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"runtime"
|
||||
|
|
@ -37,51 +38,44 @@ var testSigData = make([]byte, 32)
|
|||
|
||||
func TestKeyStore(t *testing.T) {
|
||||
dir, ks := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
a, err := ks.NewAccount("foo")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(a.URL.Path, dir) {
|
||||
t.Errorf("account file %s doesn't have dir prefix", a.URL)
|
||||
}
|
||||
|
||||
stat, err := os.Stat(a.URL.Path)
|
||||
if err != nil {
|
||||
t.Fatalf("account file %s doesn't exist (%v)", a.URL, err)
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" && stat.Mode() != 0600 {
|
||||
t.Fatalf("account file has wrong mode: got %o, want %o", stat.Mode(), 0600)
|
||||
}
|
||||
|
||||
if !ks.HasAddress(a.Address) {
|
||||
t.Errorf("HasAccount(%x) should've returned true", a.Address)
|
||||
}
|
||||
|
||||
if err := ks.Update(a, "foo", "bar"); err != nil {
|
||||
t.Errorf("Update error: %v", err)
|
||||
}
|
||||
|
||||
if err := ks.Delete(a, "bar"); err != nil {
|
||||
t.Errorf("Delete error: %v", err)
|
||||
}
|
||||
|
||||
if common.FileExist(a.URL.Path) {
|
||||
t.Errorf("account file %s should be gone after Delete", a.URL)
|
||||
}
|
||||
|
||||
if ks.HasAddress(a.Address) {
|
||||
t.Errorf("HasAccount(%x) should've returned true after Delete", a.Address)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSign(t *testing.T) {
|
||||
_, ks := tmpKeyStore(t, true)
|
||||
dir, ks := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
pass := "" // not used but required by API
|
||||
|
||||
a1, err := ks.NewAccount(pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -89,17 +83,16 @@ func TestSign(t *testing.T) {
|
|||
if err := ks.Unlock(a1, ""); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignWithPassphrase(t *testing.T) {
|
||||
_, ks := tmpKeyStore(t, true)
|
||||
dir, ks := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
pass := "passwd"
|
||||
|
||||
acc, err := ks.NewAccount(pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -124,11 +117,10 @@ func TestSignWithPassphrase(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestTimedUnlock(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, ks := tmpKeyStore(t, true)
|
||||
dir, ks := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
pass := "foo"
|
||||
|
||||
a1, err := ks.NewAccount(pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -153,7 +145,6 @@ func TestTimedUnlock(t *testing.T) {
|
|||
|
||||
// Signing fails again after automatic locking
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
|
||||
_, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
|
||||
if err != ErrLocked {
|
||||
t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
|
||||
|
|
@ -161,11 +152,10 @@ func TestTimedUnlock(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestOverrideUnlock(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, ks := tmpKeyStore(t, false)
|
||||
dir, ks := tmpKeyStore(t, false)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
pass := "foo"
|
||||
|
||||
a1, err := ks.NewAccount(pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -195,7 +185,6 @@ func TestOverrideUnlock(t *testing.T) {
|
|||
|
||||
// Signing fails again after automatic locking
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
|
||||
_, err = ks.SignHash(accounts.Account{Address: a1.Address}, testSigData)
|
||||
if err != ErrLocked {
|
||||
t.Fatal("Signing should've failed with ErrLocked timeout expired, got ", err)
|
||||
|
|
@ -204,8 +193,8 @@ func TestOverrideUnlock(t *testing.T) {
|
|||
|
||||
// This test should fail under -race if signing races the expiration goroutine.
|
||||
func TestSignRace(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, ks := tmpKeyStore(t, false)
|
||||
dir, ks := tmpKeyStore(t, false)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// Create a test account.
|
||||
a1, err := ks.NewAccount("")
|
||||
|
|
@ -216,7 +205,6 @@ func TestSignRace(t *testing.T) {
|
|||
if err := ks.TimedUnlock(a1, "", 15*time.Millisecond); err != nil {
|
||||
t.Fatal("could not unlock the test account", err)
|
||||
}
|
||||
|
||||
end := time.Now().Add(500 * time.Millisecond)
|
||||
for time.Now().Before(end) {
|
||||
if _, err := ks.SignHash(accounts.Account{Address: a1.Address}, testSigData); err == ErrLocked {
|
||||
|
|
@ -225,41 +213,25 @@ func TestSignRace(t *testing.T) {
|
|||
t.Errorf("Sign error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
}
|
||||
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
|
||||
// addition and removal of wallet event subscriptions.
|
||||
func TestWalletNotifierLifecycle(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Create a temporary keystore to test with
|
||||
_, ks := tmpKeyStore(t, false)
|
||||
// Create a temporary kesytore to test with
|
||||
dir, ks := tmpKeyStore(t, false)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// Ensure that the notification updater is not running yet
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
ks.mu.RLock()
|
||||
updating := ks.updating
|
||||
ks.mu.RUnlock()
|
||||
|
||||
if ks.isUpdating() {
|
||||
if updating {
|
||||
t.Errorf("wallet notifier running without subscribers")
|
||||
}
|
||||
// Subscribe to the wallet feed and ensure the updater boots up
|
||||
|
|
@ -270,28 +242,38 @@ func TestWalletNotifierLifecycle(t *testing.T) {
|
|||
// Create a new subscription
|
||||
subs[i] = ks.Subscribe(updates)
|
||||
|
||||
if !waitForKsUpdating(t, ks, true, 250*time.Millisecond) {
|
||||
// Ensure the notifier comes online
|
||||
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)
|
||||
}
|
||||
}
|
||||
// Close all but one sub
|
||||
for i := 0; i < len(subs)-1; i++ {
|
||||
// Unsubscribe and ensure the updater terminates eventually
|
||||
for i := 0; i < len(subs); i++ {
|
||||
// Close an existing subscription
|
||||
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)
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
type walletEvent struct {
|
||||
accounts.WalletEvent
|
||||
|
|
@ -301,7 +283,8 @@ type walletEvent struct {
|
|||
// Tests that wallet notifications and correctly fired when accounts are added
|
||||
// or deleted from the keystore.
|
||||
func TestWalletNotifications(t *testing.T) {
|
||||
_, ks := tmpKeyStore(t, false)
|
||||
dir, ks := tmpKeyStore(t, false)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// Subscribe to the wallet feed and collect events.
|
||||
var (
|
||||
|
|
@ -309,9 +292,7 @@ func TestWalletNotifications(t *testing.T) {
|
|||
updates = make(chan accounts.WalletEvent)
|
||||
sub = ks.Subscribe(updates)
|
||||
)
|
||||
|
||||
defer sub.Unsubscribe()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
|
|
@ -329,7 +310,6 @@ func TestWalletNotifications(t *testing.T) {
|
|||
live = make(map[common.Address]accounts.Account)
|
||||
wantEvents []walletEvent
|
||||
)
|
||||
|
||||
for i := 0; i < 1024; i++ {
|
||||
if create := len(live) == 0 || rand.Int()%4 > 0; create {
|
||||
// Add a new account and ensure wallet notifications arrives
|
||||
|
|
@ -337,7 +317,6 @@ func TestWalletNotifications(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("failed to create test account: %v", err)
|
||||
}
|
||||
|
||||
live[account.Address] = account
|
||||
wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletArrived}, account})
|
||||
} else {
|
||||
|
|
@ -347,11 +326,9 @@ func TestWalletNotifications(t *testing.T) {
|
|||
account = a
|
||||
break
|
||||
}
|
||||
|
||||
if err := ks.Delete(account, ""); err != nil {
|
||||
t.Fatalf("failed to delete test account: %v", err)
|
||||
}
|
||||
|
||||
delete(live, account.Address)
|
||||
wantEvents = append(wantEvents, walletEvent{accounts.WalletEvent{Kind: accounts.WalletDropped}, account})
|
||||
}
|
||||
|
|
@ -359,32 +336,27 @@ func TestWalletNotifications(t *testing.T) {
|
|||
|
||||
// Shut down the event collector and check events.
|
||||
sub.Unsubscribe()
|
||||
|
||||
for ev := range updates {
|
||||
events = append(events, walletEvent{ev, ev.Wallet.Accounts()[0]})
|
||||
}
|
||||
|
||||
checkAccounts(t, live, ks.Wallets())
|
||||
checkEvents(t, wantEvents, events)
|
||||
}
|
||||
|
||||
// TestImportExport tests the import functionality of a keystore.
|
||||
func TestImportECDSA(t *testing.T) {
|
||||
_, ks := tmpKeyStore(t, true)
|
||||
|
||||
dir, ks := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
key, err := crypto.GenerateKey()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to generate key: %v", key)
|
||||
}
|
||||
|
||||
if _, err = ks.ImportECDSA(key, "old"); err != nil {
|
||||
t.Errorf("importing failed: %v", err)
|
||||
}
|
||||
|
||||
if _, err = ks.ImportECDSA(key, "old"); err == nil {
|
||||
t.Errorf("importing same key twice succeeded")
|
||||
}
|
||||
|
||||
if _, err = ks.ImportECDSA(key, "new"); err == nil {
|
||||
t.Errorf("importing same key twice succeeded")
|
||||
}
|
||||
|
|
@ -392,71 +364,62 @@ func TestImportECDSA(t *testing.T) {
|
|||
|
||||
// TestImportECDSA tests the import and export functionality of a keystore.
|
||||
func TestImportExport(t *testing.T) {
|
||||
_, ks := tmpKeyStore(t, true)
|
||||
|
||||
dir, ks := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
acc, err := ks.NewAccount("old")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create account: %v", acc)
|
||||
}
|
||||
|
||||
json, err := ks.Export(acc, "old", "new")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to export account: %v", acc)
|
||||
}
|
||||
|
||||
_, ks2 := tmpKeyStore(t, true)
|
||||
dir2, ks2 := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir2)
|
||||
if _, err = ks2.Import(json, "old", "old"); err == nil {
|
||||
t.Errorf("importing with invalid password succeeded")
|
||||
}
|
||||
|
||||
acc2, err := ks2.Import(json, "new", "new")
|
||||
if err != nil {
|
||||
t.Errorf("importing failed: %v", err)
|
||||
}
|
||||
|
||||
if acc.Address != acc2.Address {
|
||||
t.Error("imported account does not match exported account")
|
||||
}
|
||||
|
||||
if _, err = ks2.Import(json, "new", "new"); err == nil {
|
||||
t.Errorf("importing a key twice succeeded")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TestImportRace tests the keystore on races.
|
||||
// This test should fail under -race if importing races.
|
||||
func TestImportRace(t *testing.T) {
|
||||
_, ks := tmpKeyStore(t, true)
|
||||
|
||||
dir, ks := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
acc, err := ks.NewAccount("old")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create account: %v", acc)
|
||||
}
|
||||
|
||||
json, err := ks.Export(acc, "old", "new")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to export account: %v", acc)
|
||||
}
|
||||
|
||||
_, ks2 := tmpKeyStore(t, true)
|
||||
|
||||
dir2, ks2 := tmpKeyStore(t, true)
|
||||
defer os.RemoveAll(dir2)
|
||||
var atom uint32
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(2)
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
if _, err := ks2.Import(json, "new", "new"); err != nil {
|
||||
atomic.AddUint32(&atom, 1)
|
||||
}
|
||||
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
if atom != 1 {
|
||||
t.Errorf("Import is racy")
|
||||
}
|
||||
|
|
@ -468,14 +431,11 @@ 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))
|
||||
return
|
||||
}
|
||||
|
||||
liveList := make([]accounts.Account, 0, len(live))
|
||||
for _, account := range live {
|
||||
liveList = append(liveList, account)
|
||||
}
|
||||
|
||||
sort.Sort(accountsByURL(liveList))
|
||||
|
||||
for j, wallet := range wallets {
|
||||
if accs := wallet.Accounts(); len(accs) != 1 {
|
||||
t.Errorf("wallet %d: contains invalid number of accounts: have %d, want 1", j, len(accs))
|
||||
|
|
@ -489,15 +449,12 @@ func checkAccounts(t *testing.T, live map[common.Address]accounts.Account, walle
|
|||
func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) {
|
||||
for _, wantEv := range want {
|
||||
nmatch := 0
|
||||
|
||||
for ; len(have) > 0; nmatch++ {
|
||||
if have[0].Kind != wantEv.Kind || have[0].a != wantEv.a {
|
||||
break
|
||||
}
|
||||
|
||||
have = have[1:]
|
||||
}
|
||||
|
||||
if nmatch == 0 {
|
||||
t.Fatalf("can't find event with Kind=%v for %x", wantEv.Kind, wantEv.a.Address)
|
||||
}
|
||||
|
|
@ -505,12 +462,13 @@ func checkEvents(t *testing.T, want []walletEvent, have []walletEvent) {
|
|||
}
|
||||
|
||||
func tmpKeyStore(t *testing.T, encrypted bool) (string, *KeyStore) {
|
||||
d := t.TempDir()
|
||||
|
||||
d, err := ioutil.TempDir("", "eth-keystore-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
newKs := NewPlaintextKeyStore
|
||||
if encrypted {
|
||||
newKs = func(kd string) *KeyStore { return NewKeyStore(kd, veryLightScryptN, veryLightScryptP) }
|
||||
}
|
||||
|
||||
return d, newKs(d)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
|
|
@ -81,11 +82,10 @@ type keyStorePassphrase struct {
|
|||
|
||||
func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string) (*Key, error) {
|
||||
// Load the key from the keystore and decrypt its contents
|
||||
keyjson, err := os.ReadFile(filename)
|
||||
keyjson, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, err := DecryptKey(keyjson, auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -94,7 +94,6 @@ func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string)
|
|||
if key.Address != addr {
|
||||
return nil, fmt.Errorf("key content mismatch: have account %x, want %x", key.Address, addr)
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
|
|
@ -114,7 +113,6 @@ func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) er
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ks.skipKeyFileVerification {
|
||||
// Verify that we can decrypt the file with the given password.
|
||||
_, err = ks.GetKey(key.Address, tmpName, auth)
|
||||
|
|
@ -129,7 +127,6 @@ func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) er
|
|||
return fmt.Errorf(msg, tmpName, err)
|
||||
}
|
||||
}
|
||||
|
||||
return os.Rename(tmpName, filename)
|
||||
}
|
||||
|
||||
|
|
@ -137,34 +134,30 @@ func (ks keyStorePassphrase) JoinPath(filename string) string {
|
|||
if filepath.IsAbs(filename) {
|
||||
return filename
|
||||
}
|
||||
|
||||
return filepath.Join(ks.keysDirPath, filename)
|
||||
}
|
||||
|
||||
// Encryptdata encrypts the data given as 'data' with the password 'auth'.
|
||||
func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error) {
|
||||
|
||||
salt := make([]byte, 32)
|
||||
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
|
||||
panic("reading from crypto/rand failed: " + err.Error())
|
||||
}
|
||||
|
||||
derivedKey, err := scrypt.Key(auth, salt, scryptN, scryptR, scryptP, scryptDKLen)
|
||||
if err != nil {
|
||||
return CryptoJSON{}, err
|
||||
}
|
||||
|
||||
encryptKey := derivedKey[:16]
|
||||
|
||||
iv := make([]byte, aes.BlockSize) // 16
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
panic("reading from crypto/rand failed: " + err.Error())
|
||||
}
|
||||
|
||||
cipherText, err := aesCTRXOR(encryptKey, data, iv)
|
||||
if err != nil {
|
||||
return CryptoJSON{}, err
|
||||
}
|
||||
|
||||
mac := crypto.Keccak256(derivedKey[16:32], cipherText)
|
||||
|
||||
scryptParamsJSON := make(map[string]interface{}, 5)
|
||||
|
|
@ -185,7 +178,6 @@ func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error)
|
|||
KDFParams: scryptParamsJSON,
|
||||
MAC: hex.EncodeToString(mac),
|
||||
}
|
||||
|
||||
return cryptoStruct, nil
|
||||
}
|
||||
|
||||
|
|
@ -193,19 +185,16 @@ func EncryptDataV3(data, auth []byte, scryptN, scryptP int) (CryptoJSON, error)
|
|||
// blob that can be decrypted later on.
|
||||
func EncryptKey(key *Key, auth string, scryptN, scryptP int) ([]byte, error) {
|
||||
keyBytes := math.PaddedBigBytes(key.PrivateKey.D, 32)
|
||||
|
||||
cryptoStruct, err := EncryptDataV3(keyBytes, []byte(auth), scryptN, scryptP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encryptedKeyJSONV3 := encryptedKeyJSONV3{
|
||||
hex.EncodeToString(key.Address[:]),
|
||||
cryptoStruct,
|
||||
key.Id.String(),
|
||||
version,
|
||||
}
|
||||
|
||||
return json.Marshal(encryptedKeyJSONV3)
|
||||
}
|
||||
|
||||
|
|
@ -221,34 +210,28 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
|
|||
keyBytes, keyId []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if version, ok := m["version"].(string); ok && version == "1" {
|
||||
k := new(encryptedKeyJSONV1)
|
||||
if err := json.Unmarshal(keyjson, k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keyBytes, keyId, err = decryptKeyV1(k, auth)
|
||||
} else {
|
||||
k := new(encryptedKeyJSONV3)
|
||||
if err := json.Unmarshal(keyjson, k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keyBytes, keyId, err = decryptKeyV3(k, auth)
|
||||
}
|
||||
// Handle any decryption errors and return the key
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key := crypto.ToECDSAUnsafe(keyBytes)
|
||||
|
||||
id, err := uuid.FromBytes(keyId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Key{
|
||||
Id: id,
|
||||
Address: crypto.PubkeyToAddress(key.PublicKey),
|
||||
|
|
@ -260,7 +243,6 @@ func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
|
|||
if cryptoJson.Cipher != "aes-128-ctr" {
|
||||
return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher)
|
||||
}
|
||||
|
||||
mac, err := hex.DecodeString(cryptoJson.MAC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -290,7 +272,6 @@ func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return plainText, err
|
||||
}
|
||||
|
||||
|
|
@ -298,19 +279,15 @@ func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byt
|
|||
if keyProtected.Version != version {
|
||||
return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version)
|
||||
}
|
||||
|
||||
keyUUID, err := uuid.Parse(keyProtected.Id)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
keyId = keyUUID[:]
|
||||
|
||||
plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return plainText, keyId, err
|
||||
}
|
||||
|
||||
|
|
@ -319,9 +296,7 @@ func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byt
|
|||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
keyId = keyUUID[:]
|
||||
|
||||
mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
|
@ -351,36 +326,30 @@ func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byt
|
|||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return plainText, keyId, err
|
||||
}
|
||||
|
||||
func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
|
||||
authArray := []byte(auth)
|
||||
|
||||
salt, err := hex.DecodeString(cryptoJSON.KDFParams["salt"].(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dkLen := ensureInt(cryptoJSON.KDFParams["dklen"])
|
||||
|
||||
if cryptoJSON.KDF == keyHeaderKDF {
|
||||
n := ensureInt(cryptoJSON.KDFParams["n"])
|
||||
r := ensureInt(cryptoJSON.KDFParams["r"])
|
||||
p := ensureInt(cryptoJSON.KDFParams["p"])
|
||||
|
||||
return scrypt.Key(authArray, salt, n, r, p, dkLen)
|
||||
|
||||
} else if cryptoJSON.KDF == "pbkdf2" {
|
||||
c := ensureInt(cryptoJSON.KDFParams["c"])
|
||||
|
||||
prf := cryptoJSON.KDFParams["prf"].(string)
|
||||
if prf != "hmac-sha256" {
|
||||
return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf)
|
||||
}
|
||||
|
||||
key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
|
|
@ -395,6 +364,5 @@ func ensureInt(x interface{}) int {
|
|||
if !ok {
|
||||
res = int(x.(float64))
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
package keystore
|
||||
|
||||
import (
|
||||
"os"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
|
|
@ -30,11 +30,10 @@ const (
|
|||
|
||||
// Tests that a json key file can be decrypted and encrypted in multiple rounds.
|
||||
func TestKeyEncryptDecrypt(t *testing.T) {
|
||||
keyjson, err := os.ReadFile("testdata/very-light-scrypt.json")
|
||||
keyjson, err := ioutil.ReadFile("testdata/very-light-scrypt.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
password := ""
|
||||
address := common.HexToAddress("45dea0fb0bba44f4fcf290bba71fd57d7117cbb8")
|
||||
|
||||
|
|
@ -49,12 +48,11 @@ func TestKeyEncryptDecrypt(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("test %d: json key failed to decrypt: %v", i, err)
|
||||
}
|
||||
|
||||
if 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
|
||||
password += "new data appended" // nolint: gosec
|
||||
password += "new data appended"
|
||||
if keyjson, err = EncryptKey(key, password, veryLightScryptN, veryLightScryptP); err != nil {
|
||||
t.Errorf("test %d: failed to recrypt key %v", i, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,18 +34,14 @@ func (ks keyStorePlain) GetKey(addr common.Address, filename, auth string) (*Key
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer fd.Close()
|
||||
|
||||
key := new(Key)
|
||||
if err := json.NewDecoder(fd).Decode(key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if key.Address != addr {
|
||||
return nil, fmt.Errorf("key content mismatch: have address %x, want %x", key.Address, addr)
|
||||
}
|
||||
|
||||
return key, nil
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +50,6 @@ func (ks keyStorePlain) StoreKey(filename string, key *Key, auth string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return writeKeyFile(filename, content)
|
||||
}
|
||||
|
||||
|
|
@ -62,6 +57,5 @@ func (ks keyStorePlain) JoinPath(filename string) string {
|
|||
if filepath.IsAbs(filename) {
|
||||
return filename
|
||||
}
|
||||
|
||||
return filepath.Join(ks.keysDirPath, filename)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import (
|
|||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
|
@ -30,74 +32,69 @@ import (
|
|||
)
|
||||
|
||||
func tmpKeyStoreIface(t *testing.T, encrypted bool) (dir string, ks keyStore) {
|
||||
d := t.TempDir()
|
||||
d, err := ioutil.TempDir("", "geth-keystore-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if encrypted {
|
||||
ks = &keyStorePassphrase{d, veryLightScryptN, veryLightScryptP, true}
|
||||
} else {
|
||||
ks = &keyStorePlain{d}
|
||||
}
|
||||
|
||||
return d, ks
|
||||
}
|
||||
|
||||
func TestKeyStorePlain(t *testing.T) {
|
||||
_, ks := tmpKeyStoreIface(t, false)
|
||||
dir, ks := tmpKeyStoreIface(t, false)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
pass := "" // not used but required by API
|
||||
|
||||
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
k2, err := ks.GetKey(k1.Address, account.URL.Path, pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(k1.Address, k2.Address) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyStorePassphrase(t *testing.T) {
|
||||
_, ks := tmpKeyStoreIface(t, true)
|
||||
dir, ks := tmpKeyStoreIface(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
pass := "foo"
|
||||
|
||||
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
k2, err := ks.GetKey(k1.Address, account.URL.Path, pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(k1.Address, k2.Address) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(k1.PrivateKey, k2.PrivateKey) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
|
||||
_, ks := tmpKeyStoreIface(t, true)
|
||||
dir, ks := tmpKeyStoreIface(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
pass := "foo"
|
||||
|
||||
k1, account, err := storeNewKey(ks, rand.Reader, pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
@ -105,22 +102,20 @@ func TestKeyStorePassphraseDecryptionFail(t *testing.T) {
|
|||
|
||||
func TestImportPreSaleKey(t *testing.T) {
|
||||
dir, ks := tmpKeyStoreIface(t, true)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// file content of a presale key file generated with:
|
||||
// python pyethsaletool.py genwallet
|
||||
// with password "foo"
|
||||
fileContent := "{\"encseed\": \"26d87f5f2bf9835f9a47eefae571bc09f9107bb13d54ff12a4ec095d01f83897494cf34f7bed2ed34126ecba9db7b62de56c9d7cd136520a0427bfb11b8954ba7ac39b90d4650d3448e31185affcd74226a68f1e94b1108e6e0a4a91cdd83eba\", \"ethaddr\": \"d4584b5f6229b7be90727b0fc8c6b91bb427821f\", \"email\": \"gustav.simonsson@gmail.com\", \"btcaddr\": \"1EVknXyFC68kKNLkh6YnKzW41svSRoaAcx\"}"
|
||||
pass := "foo"
|
||||
|
||||
account, _, err := importPreSaleKey(ks, []byte(fileContent), pass)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if account.Address != common.HexToAddress("d4584b5f6229b7be90727b0fc8c6b91bb427821f") {
|
||||
t.Errorf("imported account has wrong address %x", account.Address)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(account.URL.Path, dir) {
|
||||
t.Errorf("imported account file not in keystore directory: %q", account.URL)
|
||||
}
|
||||
|
|
@ -196,19 +191,15 @@ func TestV1_1(t *testing.T) {
|
|||
|
||||
func TestV1_2(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ks := &keyStorePassphrase{"testdata/v1", LightScryptN, LightScryptP, true}
|
||||
addr := common.HexToAddress("cb61d5a9c4896fb9658090b597ef0e7be6f7b67e")
|
||||
file := "testdata/v1/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e/cb61d5a9c4896fb9658090b597ef0e7be6f7b67e"
|
||||
|
||||
k, err := ks.GetKey(addr, file, "g")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
privHex := hex.EncodeToString(crypto.FromECDSA(k.PrivateKey))
|
||||
expectedHex := "d1b1178d3529626a1a93e073f65028370d14c7eb0936eb42abef05db6f37ad7d"
|
||||
|
||||
if privHex != expectedHex {
|
||||
t.Fatal(fmt.Errorf("Unexpected privkey: %v, expected %v", privHex, expectedHex))
|
||||
}
|
||||
|
|
@ -219,7 +210,6 @@ func testDecryptV3(test KeyStoreTestV3, t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
privHex := hex.EncodeToString(privBytes)
|
||||
if test.Priv != privHex {
|
||||
t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex))
|
||||
|
|
@ -231,7 +221,6 @@ func testDecryptV1(test KeyStoreTestV1, t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
privHex := hex.EncodeToString(privBytes)
|
||||
if test.Priv != privHex {
|
||||
t.Fatal(fmt.Errorf("Decrypted bytes not equal to test, expected %v have %v", test.Priv, privHex))
|
||||
|
|
@ -240,29 +229,24 @@ func testDecryptV1(test KeyStoreTestV1, t *testing.T) {
|
|||
|
||||
func loadKeyStoreTestV3(file string, t *testing.T) map[string]KeyStoreTestV3 {
|
||||
tests := make(map[string]KeyStoreTestV3)
|
||||
|
||||
err := common.LoadJSON(file, &tests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return tests
|
||||
}
|
||||
|
||||
func loadKeyStoreTestV1(file string, t *testing.T) map[string]KeyStoreTestV1 {
|
||||
tests := make(map[string]KeyStoreTestV1)
|
||||
|
||||
err := common.LoadJSON(file, &tests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return tests
|
||||
}
|
||||
|
||||
func TestKeyForDirectICAP(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
key := NewKeyForDirectICAP(rand.Reader)
|
||||
if !strings.HasPrefix(key.Address.Hex(), "0x00") {
|
||||
t.Errorf("Expected first address byte to be zero, have: %s", key.Address.Hex())
|
||||
|
|
|
|||
|
|
@ -37,12 +37,10 @@ func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accou
|
|||
if err != nil {
|
||||
return accounts.Account{}, nil, err
|
||||
}
|
||||
|
||||
key.Id, err = uuid.NewRandom()
|
||||
if err != nil {
|
||||
return accounts.Account{}, nil, err
|
||||
}
|
||||
|
||||
a := accounts.Account{
|
||||
Address: key.Address,
|
||||
URL: accounts.URL{
|
||||
|
|
@ -51,7 +49,6 @@ func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accou
|
|||
},
|
||||
}
|
||||
err = keyStore.StoreKey(a.URL.Path, key, password)
|
||||
|
||||
return a, key, err
|
||||
}
|
||||
|
||||
|
|
@ -62,21 +59,17 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
|
|||
Email string
|
||||
BtcAddr string
|
||||
}{}
|
||||
|
||||
err = json.Unmarshal(fileContent, &preSaleKeyStruct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encSeedBytes, err := hex.DecodeString(preSaleKeyStruct.EncSeed)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid hex in encSeed")
|
||||
}
|
||||
|
||||
if len(encSeedBytes) < 16 {
|
||||
return nil, errors.New("invalid encSeed, too short")
|
||||
}
|
||||
|
||||
iv := encSeedBytes[:16]
|
||||
cipherText := encSeedBytes[16:]
|
||||
/*
|
||||
|
|
@ -88,12 +81,10 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
|
|||
*/
|
||||
passBytes := []byte(password)
|
||||
derivedKey := pbkdf2.Key(passBytes, passBytes, 2000, 16, sha256.New)
|
||||
|
||||
plainText, err := aesCBCDecrypt(derivedKey, cipherText, iv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ethPriv := crypto.Keccak256(plainText)
|
||||
ecKey := crypto.ToECDSAUnsafe(ethPriv)
|
||||
|
||||
|
|
@ -104,11 +95,9 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
|
|||
}
|
||||
derivedAddr := hex.EncodeToString(key.Address.Bytes()) // needed because .Hex() gives leading "0x"
|
||||
expectedAddr := preSaleKeyStruct.EthAddr
|
||||
|
||||
if derivedAddr != expectedAddr {
|
||||
err = fmt.Errorf("decrypted addr '%s' not equal to expected addr '%s'", derivedAddr, expectedAddr)
|
||||
}
|
||||
|
||||
return key, err
|
||||
}
|
||||
|
||||
|
|
@ -118,11 +107,9 @@ func aesCTRXOR(key, inText, iv []byte) ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stream := cipher.NewCTR(aesBlock, iv)
|
||||
outText := make([]byte, len(inText))
|
||||
stream.XORKeyStream(outText, inText)
|
||||
|
||||
return outText, err
|
||||
}
|
||||
|
||||
|
|
@ -131,16 +118,13 @@ func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
decrypter := cipher.NewCBCDecrypter(aesBlock, iv)
|
||||
paddedPlaintext := make([]byte, len(cipherText))
|
||||
decrypter.CryptBlocks(paddedPlaintext, cipherText)
|
||||
|
||||
plaintext := pkcs7Unpad(paddedPlaintext)
|
||||
if plaintext == nil {
|
||||
return nil, ErrDecrypt
|
||||
}
|
||||
|
||||
return plaintext, err
|
||||
}
|
||||
|
||||
|
|
@ -162,6 +146,5 @@ func pkcs7Unpad(in []byte) []byte {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return in[:len(in)-int(padding)]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ func (w *keystoreWallet) Status() (string, error) {
|
|||
if _, ok := w.keystore.unlocked[w.account.Address]; ok {
|
||||
return "Unlocked", nil
|
||||
}
|
||||
|
||||
return "Locked", nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,29 +22,26 @@ package keystore
|
|||
import (
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/rjeczalik/notify"
|
||||
)
|
||||
|
||||
type watcher struct {
|
||||
ac *accountCache
|
||||
running bool // set to true when runloop begins
|
||||
runEnded bool // set to true when runloop ends
|
||||
starting bool // set to true prior to runloop starting
|
||||
starting bool
|
||||
running bool
|
||||
ev chan notify.EventInfo
|
||||
quit chan struct{}
|
||||
}
|
||||
|
||||
func newWatcher(ac *accountCache) *watcher {
|
||||
return &watcher{
|
||||
ac: ac,
|
||||
ev: make(chan notify.EventInfo, 10),
|
||||
quit: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// enabled returns false on systems not supported.
|
||||
func (*watcher) enabled() bool { return true }
|
||||
|
||||
// starts the watcher loop in the background.
|
||||
// Start a watcher in the background if that's not already in progress.
|
||||
// The caller must hold w.ac.mu.
|
||||
|
|
@ -52,7 +49,6 @@ func (w *watcher) start() {
|
|||
if w.starting || w.running {
|
||||
return
|
||||
}
|
||||
|
||||
w.starting = true
|
||||
go w.loop()
|
||||
}
|
||||
|
|
@ -66,27 +62,16 @@ func (w *watcher) loop() {
|
|||
w.ac.mu.Lock()
|
||||
w.running = false
|
||||
w.starting = false
|
||||
w.runEnded = true
|
||||
w.ac.mu.Unlock()
|
||||
}()
|
||||
|
||||
logger := log.New("path", w.ac.keydir)
|
||||
|
||||
// Create new watcher.
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
log.Error("Failed to start filesystem watcher", "err", err)
|
||||
if err := notify.Watch(w.ac.keydir, w.ev, notify.All); err != nil {
|
||||
logger.Trace("Failed to watch keystore folder", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
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 notify.Stop(w.ev)
|
||||
logger.Trace("Started watching keystore folder")
|
||||
defer logger.Trace("Stopped watching keystore folder")
|
||||
|
||||
w.ac.mu.Lock()
|
||||
|
|
@ -106,34 +91,18 @@ func (w *watcher) loop() {
|
|||
<-debounce.C
|
||||
}
|
||||
defer debounce.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-w.quit:
|
||||
return
|
||||
case _, ok := <-watcher.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
case <-w.ev:
|
||||
// Trigger the scan (with delay), if not already triggered
|
||||
if !rescanTriggered {
|
||||
debounce.Reset(debounceDuration)
|
||||
|
||||
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:
|
||||
w.ac.scanAccounts()
|
||||
|
||||
rescanTriggered = false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,14 +22,8 @@
|
|||
|
||||
package keystore
|
||||
|
||||
type watcher struct {
|
||||
running bool
|
||||
runEnded bool
|
||||
}
|
||||
type watcher struct{ running bool }
|
||||
|
||||
func newWatcher(*accountCache) *watcher { return new(watcher) }
|
||||
func (*watcher) start() {}
|
||||
func (*watcher) close() {}
|
||||
|
||||
// enabled returns false on systems not supported.
|
||||
func (*watcher) enabled() bool { return false }
|
||||
|
|
|
|||
|
|
@ -87,12 +87,10 @@ func NewManager(config *Config, backends ...Backend) *Manager {
|
|||
quit: make(chan chan error),
|
||||
term: make(chan struct{}),
|
||||
}
|
||||
|
||||
for _, backend := range backends {
|
||||
kind := reflect.TypeOf(backend)
|
||||
am.backends[kind] = append(am.backends[kind], backend)
|
||||
}
|
||||
|
||||
go am.update()
|
||||
|
||||
return am
|
||||
|
|
@ -102,7 +100,6 @@ func NewManager(config *Config, backends ...Backend) *Manager {
|
|||
func (am *Manager) Close() error {
|
||||
errc := make(chan error)
|
||||
am.quit <- errc
|
||||
|
||||
return <-errc
|
||||
}
|
||||
|
||||
|
|
@ -116,7 +113,6 @@ func (am *Manager) Config() *Config {
|
|||
func (am *Manager) AddBackend(backend Backend) {
|
||||
done := make(chan struct{})
|
||||
am.newBackends <- newBackendEvent{backend, done}
|
||||
|
||||
<-done
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +125,6 @@ func (am *Manager) update() {
|
|||
for _, sub := range am.updaters {
|
||||
sub.Unsubscribe()
|
||||
}
|
||||
|
||||
am.updaters = nil
|
||||
am.lock.Unlock()
|
||||
}()
|
||||
|
|
@ -166,7 +161,6 @@ func (am *Manager) update() {
|
|||
// Signals event emitters the loop is not receiving values
|
||||
// to prevent them from getting stuck.
|
||||
close(am.term)
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -192,7 +186,6 @@ func (am *Manager) Wallets() []Wallet {
|
|||
func (am *Manager) walletsNoLock() []Wallet {
|
||||
cpy := make([]Wallet, len(am.wallets))
|
||||
copy(cpy, am.wallets)
|
||||
|
||||
return cpy
|
||||
}
|
||||
|
||||
|
|
@ -205,13 +198,11 @@ func (am *Manager) Wallet(url string) (Wallet, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, wallet := range am.walletsNoLock() {
|
||||
if wallet.URL() == parsed {
|
||||
return wallet, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ErrUnknownWallet
|
||||
}
|
||||
|
||||
|
|
@ -221,13 +212,11 @@ func (am *Manager) Accounts() []common.Address {
|
|||
defer am.lock.RUnlock()
|
||||
|
||||
addresses := make([]common.Address, 0) // return [] instead of nil if empty
|
||||
|
||||
for _, wallet := range am.wallets {
|
||||
for _, account := range wallet.Accounts() {
|
||||
addresses = append(addresses, account.Address)
|
||||
}
|
||||
}
|
||||
|
||||
return addresses
|
||||
}
|
||||
|
||||
|
|
@ -243,7 +232,6 @@ func (am *Manager) Find(account Account) (Wallet, error) {
|
|||
return wallet, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, ErrUnknownAccount
|
||||
}
|
||||
|
||||
|
|
@ -264,10 +252,8 @@ func merge(slice []Wallet, wallets ...Wallet) []Wallet {
|
|||
slice = append(slice, wallet)
|
||||
continue
|
||||
}
|
||||
|
||||
slice = append(slice[:n], append([]Wallet{wallet}, slice[n:]...)...)
|
||||
}
|
||||
|
||||
return slice
|
||||
}
|
||||
|
||||
|
|
@ -280,9 +266,7 @@ func drop(slice []Wallet, wallets ...Wallet) []Wallet {
|
|||
// Wallet not found, may happen during startup
|
||||
continue
|
||||
}
|
||||
|
||||
slice = append(slice[:n], slice[n+1:]...)
|
||||
}
|
||||
|
||||
return slice
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,33 +36,26 @@ func (ca commandAPDU) serialize() ([]byte, error) {
|
|||
if err := binary.Write(buf, binary.BigEndian, ca.Cla); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := binary.Write(buf, binary.BigEndian, ca.Ins); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := binary.Write(buf, binary.BigEndian, ca.P1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := binary.Write(buf, binary.BigEndian, ca.P2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ca.Data) > 0 {
|
||||
if err := binary.Write(buf, binary.BigEndian, uint8(len(ca.Data))); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := binary.Write(buf, binary.BigEndian, ca.Data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := binary.Write(buf, binary.BigEndian, ca.Le); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
|
|
@ -84,14 +77,11 @@ func (ra *responseAPDU) deserialize(data []byte) error {
|
|||
if err := binary.Read(buf, binary.BigEndian, &ra.Data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := binary.Read(buf, binary.BigEndian, &ra.Sw1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := binary.Read(buf, binary.BigEndian, &ra.Sw2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ package scwallet
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
|
@ -88,21 +88,18 @@ type Hub struct {
|
|||
|
||||
func (hub *Hub) readPairings() error {
|
||||
hub.pairings = make(map[string]smartcardPairing)
|
||||
|
||||
pairingFile, err := os.Open(filepath.Join(hub.datadir, "smartcards.json"))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
pairingData, err := io.ReadAll(pairingFile)
|
||||
pairingData, err := ioutil.ReadAll(pairingFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var pairings []smartcardPairing
|
||||
if err := json.Unmarshal(pairingData, &pairings); err != nil {
|
||||
return err
|
||||
|
|
@ -111,7 +108,6 @@ func (hub *Hub) readPairings() error {
|
|||
for _, pairing := range pairings {
|
||||
hub.pairings[string(pairing.PublicKey)] = pairing
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -143,7 +139,6 @@ func (hub *Hub) pairing(wallet *Wallet) *smartcardPairing {
|
|||
if pairing, ok := hub.pairings[string(wallet.PublicKey)]; ok {
|
||||
return &pairing
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -153,7 +148,6 @@ func (hub *Hub) setPairing(wallet *Wallet, pairing *smartcardPairing) error {
|
|||
} else {
|
||||
hub.pairings[string(wallet.PublicKey)] = *pairing
|
||||
}
|
||||
|
||||
return hub.writePairings()
|
||||
}
|
||||
|
||||
|
|
@ -163,7 +157,6 @@ func NewHub(daemonPath string, scheme string, datadir string) (*Hub, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hub := &Hub{
|
||||
scheme: scheme,
|
||||
context: context,
|
||||
|
|
@ -174,9 +167,7 @@ func NewHub(daemonPath string, scheme string, datadir string) (*Hub, error) {
|
|||
if err := hub.readPairings(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hub.refreshWallets()
|
||||
|
||||
return hub, nil
|
||||
}
|
||||
|
||||
|
|
@ -193,9 +184,7 @@ func (hub *Hub) Wallets() []accounts.Wallet {
|
|||
for _, wallet := range hub.wallets {
|
||||
cpy = append(cpy, wallet)
|
||||
}
|
||||
|
||||
sort.Sort(accounts.WalletsByURL(cpy))
|
||||
|
||||
return cpy
|
||||
}
|
||||
|
||||
|
|
@ -236,10 +225,8 @@ func (hub *Hub) refreshWallets() {
|
|||
if err := wallet.ping(); err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
wallet.Close()
|
||||
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
|
||||
|
||||
delete(hub.wallets, reader)
|
||||
}
|
||||
// New card detected, try to connect to it
|
||||
|
|
@ -248,12 +235,10 @@ func (hub *Hub) refreshWallets() {
|
|||
log.Debug("Failed to open smart card", "reader", reader, "err", err)
|
||||
continue
|
||||
}
|
||||
|
||||
wallet := NewWallet(hub, card)
|
||||
if err = wallet.connect(); err != nil {
|
||||
log.Debug("Failed to connect to smart card", "reader", reader, "err", err)
|
||||
card.Disconnect(pcsc.LeaveCard)
|
||||
|
||||
continue
|
||||
}
|
||||
// Card connected, start tracking in amongs the wallets
|
||||
|
|
@ -265,11 +250,9 @@ func (hub *Hub) refreshWallets() {
|
|||
if _, ok := seen[reader]; !ok {
|
||||
wallet.Close()
|
||||
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
|
||||
|
||||
delete(hub.wallets, reader)
|
||||
}
|
||||
}
|
||||
|
||||
hub.refreshed = time.Now()
|
||||
hub.stateLock.Unlock()
|
||||
|
||||
|
|
@ -293,7 +276,6 @@ func (hub *Hub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
|
|||
hub.updating = true
|
||||
go hub.updater()
|
||||
}
|
||||
|
||||
return sub
|
||||
}
|
||||
|
||||
|
|
@ -313,7 +295,6 @@ func (hub *Hub) updater() {
|
|||
if hub.updateScope.Count() == 0 {
|
||||
hub.updating = false
|
||||
hub.stateLock.Unlock()
|
||||
|
||||
return
|
||||
}
|
||||
hub.stateLock.Unlock()
|
||||
|
|
|
|||
|
|
@ -67,14 +67,11 @@ func NewSecureChannelSession(card *pcsc.Card, keyData []byte) (*SecureChannelSes
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cardPublic, err := crypto.UnmarshalPubkey(keyData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not unmarshal public key from card: %v", err)
|
||||
}
|
||||
|
||||
secret, _ := key.Curve.ScalarMult(cardPublic.X, cardPublic.Y, key.D.Bytes())
|
||||
|
||||
return &SecureChannelSession{
|
||||
card: card,
|
||||
secret: secret.Bytes(),
|
||||
|
|
@ -111,7 +108,6 @@ func (s *SecureChannelSession) Pair(pairingPassword []byte) error {
|
|||
md.Reset()
|
||||
md.Write(secretHash[:])
|
||||
md.Write(cardChallenge)
|
||||
|
||||
response, err = s.pair(pairP1LastStep, md.Sum(nil))
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -136,11 +132,9 @@ func (s *SecureChannelSession) Unpair() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.PairingKey = nil
|
||||
// Close channel
|
||||
s.iv = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -183,9 +177,8 @@ func (s *SecureChannelSession) mutuallyAuthenticate() error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if response.Sw1 != 0x90 || response.Sw2 != 0x00 {
|
||||
return fmt.Errorf("got unexpected response from MUTUALLY_AUTHENTICATE: %#x%x", response.Sw1, response.Sw2)
|
||||
return fmt.Errorf("got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2)
|
||||
}
|
||||
|
||||
if len(response.Data) != scSecretLength {
|
||||
|
|
@ -229,7 +222,6 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
meta := [16]byte{cla, ins, p1, p2, byte(len(data) + scBlockSize)}
|
||||
if err = s.updateIV(meta[:], data); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -253,7 +245,6 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
|
|||
rmeta := [16]byte{byte(len(response.Data))}
|
||||
rmac := response.Data[:len(s.iv)]
|
||||
rdata := response.Data[len(s.iv):]
|
||||
|
||||
plainData, err := s.decryptAPDU(rdata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -262,7 +253,6 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
|
|||
if err = s.updateIV(rmeta[:], rdata); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !bytes.Equal(s.iv, rmac) {
|
||||
return nil, fmt.Errorf("invalid MAC in response")
|
||||
}
|
||||
|
|
@ -271,7 +261,7 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
|
|||
rapdu.deserialize(plainData)
|
||||
|
||||
if rapdu.Sw1 != sw1Ok {
|
||||
return nil, fmt.Errorf("unexpected response status Cla=%#x, Ins=%#x, Sw=%#x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
|
||||
return nil, fmt.Errorf("unexpected response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
|
||||
}
|
||||
|
||||
return rapdu, nil
|
||||
|
|
@ -282,7 +272,6 @@ func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
|
|||
if len(data) > maxPayloadSize {
|
||||
return nil, fmt.Errorf("payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize)
|
||||
}
|
||||
|
||||
data = pad(data, 0x80)
|
||||
|
||||
ret := make([]byte, len(data))
|
||||
|
|
@ -291,10 +280,8 @@ func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
crypter := cipher.NewCBCEncrypter(a, s.iv)
|
||||
crypter.CryptBlocks(ret, data)
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
|
|
@ -303,7 +290,6 @@ func pad(data []byte, terminator byte) []byte {
|
|||
padded := make([]byte, (len(data)/16+1)*16)
|
||||
copy(padded, data)
|
||||
padded[len(data)] = terminator
|
||||
|
||||
return padded
|
||||
}
|
||||
|
||||
|
|
@ -318,7 +304,6 @@ func (s *SecureChannelSession) decryptAPDU(data []byte) ([]byte, error) {
|
|||
|
||||
crypter := cipher.NewCBCDecrypter(a, s.iv)
|
||||
crypter.CryptBlocks(ret, data)
|
||||
|
||||
return unpad(ret, 0x80)
|
||||
}
|
||||
|
||||
|
|
@ -334,7 +319,6 @@ 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 0")
|
||||
}
|
||||
|
||||
|
|
@ -342,17 +326,14 @@ func unpad(data []byte, terminator byte) ([]byte, error) {
|
|||
// each message exchanged.
|
||||
func (s *SecureChannelSession) updateIV(meta, data []byte) error {
|
||||
data = pad(data, 0)
|
||||
|
||||
a, err := aes.NewCipher(s.sessionMacKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
crypter := cipher.NewCBCEncrypter(a, make([]byte, 16))
|
||||
crypter.CryptBlocks(meta, meta)
|
||||
crypter.CryptBlocks(data, data)
|
||||
// The first 16 bytes of the last block is the MAC
|
||||
s.iv = data[len(data)-32 : len(data)-16]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -99,8 +99,8 @@ const (
|
|||
P1DeriveKeyFromCurrent = uint8(0x10)
|
||||
statusP1WalletStatus = uint8(0x00)
|
||||
statusP1Path = uint8(0x01)
|
||||
signP1PrecomputedHash = uint8(0x00)
|
||||
signP2OnlyBlock = uint8(0x00)
|
||||
signP1PrecomputedHash = uint8(0x01)
|
||||
signP2OnlyBlock = uint8(0x81)
|
||||
exportP1Any = uint8(0x00)
|
||||
exportP2Pubkey = uint8(0x01)
|
||||
)
|
||||
|
|
@ -132,7 +132,6 @@ func NewWallet(hub *Hub, card *pcsc.Card) *Wallet {
|
|||
Hub: hub,
|
||||
card: card,
|
||||
}
|
||||
|
||||
return wallet
|
||||
}
|
||||
|
||||
|
|
@ -168,7 +167,7 @@ func transmit(card *pcsc.Card, command *commandAPDU) (*responseAPDU, error) {
|
|||
}
|
||||
|
||||
if response.Sw1 != sw1Ok {
|
||||
return nil, fmt.Errorf("unexpected insecure response status Cla=%#x, Ins=%#x, Sw=%#x%x", command.Cla, command.Ins, response.Sw1, response.Sw2)
|
||||
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 response, nil
|
||||
|
|
@ -203,7 +202,6 @@ func (w *Wallet) connect() error {
|
|||
Wallet: w,
|
||||
Channel: channel,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -224,7 +222,6 @@ func (w *Wallet) doselect() (*applicationInfo, error) {
|
|||
if _, err := asn1.UnmarshalWithParams(response.Data, appinfo, "tag:4"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return appinfo, nil
|
||||
}
|
||||
|
||||
|
|
@ -237,11 +234,9 @@ func (w *Wallet) ping() error {
|
|||
if !w.session.paired() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := w.session.walletStatus(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -250,7 +245,6 @@ func (w *Wallet) release() error {
|
|||
if w.session != nil {
|
||||
return w.session.release()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -260,16 +254,13 @@ func (w *Wallet) pair(puk []byte) error {
|
|||
if w.session.paired() {
|
||||
return fmt.Errorf("wallet already paired")
|
||||
}
|
||||
|
||||
pairing, err := w.session.pair(puk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = w.Hub.setPairing(w, &pairing); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return w.session.authenticate(pairing)
|
||||
}
|
||||
|
||||
|
|
@ -281,19 +272,15 @@ func (w *Wallet) Unpair(pin []byte) error {
|
|||
if !w.session.paired() {
|
||||
return fmt.Errorf("wallet %x not paired", w.PublicKey)
|
||||
}
|
||||
|
||||
if err := w.session.verifyPin(pin); err != nil {
|
||||
return fmt.Errorf("failed to verify pin: %s", err)
|
||||
}
|
||||
|
||||
if err := w.session.unpair(); err != nil {
|
||||
return fmt.Errorf("failed to unpair: %s", err)
|
||||
}
|
||||
|
||||
if err := w.Hub.setPairing(w, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -323,7 +310,6 @@ func (w *Wallet) Status() (string, error) {
|
|||
if err != nil {
|
||||
return fmt.Sprintf("Failed: %v", err), err
|
||||
}
|
||||
|
||||
switch {
|
||||
case !w.session.verified && status.PinRetryCount == 0 && status.PukRetryCount == 0:
|
||||
return "Bricked, waiting for full wipe", nil
|
||||
|
|
@ -398,7 +384,6 @@ func (w *Wallet) Open(passphrase string) error {
|
|||
w.log.Error("PIN needs to be at least 6 digits")
|
||||
return ErrPINNeeded
|
||||
}
|
||||
|
||||
if err := w.session.verifyPin([]byte(passphrase)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -407,7 +392,6 @@ func (w *Wallet) Open(passphrase string) error {
|
|||
w.log.Error("PUK needs to be at least 12 digits")
|
||||
return ErrPINUnblockNeeded
|
||||
}
|
||||
|
||||
if err := w.session.unblockPin([]byte(passphrase)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -433,7 +417,6 @@ func (w *Wallet) Close() error {
|
|||
|
||||
// Terminate the self-derivations
|
||||
var derr error
|
||||
|
||||
if dQuit != nil {
|
||||
errc := make(chan error)
|
||||
dQuit <- errc
|
||||
|
|
@ -449,7 +432,6 @@ func (w *Wallet) Close() error {
|
|||
if err := w.release(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return derr
|
||||
}
|
||||
|
||||
|
|
@ -465,7 +447,6 @@ func (w *Wallet) selfDerive() {
|
|||
errc chan error
|
||||
err error
|
||||
)
|
||||
|
||||
for errc == nil && err == nil {
|
||||
// Wait until either derivation or termination is requested
|
||||
select {
|
||||
|
|
@ -480,10 +461,8 @@ func (w *Wallet) selfDerive() {
|
|||
if w.session == nil || w.deriveChain == nil {
|
||||
w.lock.Unlock()
|
||||
reqc <- struct{}{}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
pairing := w.Hub.pairing(w)
|
||||
|
||||
// Device lock obtained, derive the next batch of accounts
|
||||
|
|
@ -496,7 +475,6 @@ func (w *Wallet) selfDerive() {
|
|||
|
||||
context = context.Background()
|
||||
)
|
||||
|
||||
for i := 0; i < len(nextAddrs); i++ {
|
||||
for empty := false; !empty; {
|
||||
// Retrieve the next derived Ethereum account
|
||||
|
|
@ -505,7 +483,6 @@ func (w *Wallet) selfDerive() {
|
|||
w.log.Warn("Smartcard wallet account derivation failed", "err", err)
|
||||
break
|
||||
}
|
||||
|
||||
nextAddrs[i] = nextAcc.Address
|
||||
}
|
||||
// Check the account's status against the current chain state
|
||||
|
|
@ -513,13 +490,11 @@ func (w *Wallet) selfDerive() {
|
|||
balance *big.Int
|
||||
nonce uint64
|
||||
)
|
||||
|
||||
balance, err = w.deriveChain.BalanceAt(context, nextAddrs[i], nil)
|
||||
if err != nil {
|
||||
w.log.Warn("Smartcard wallet balance retrieval failed", "err", err)
|
||||
break
|
||||
}
|
||||
|
||||
nonce, err = w.deriveChain.NonceAt(context, nextAddrs[i], nil)
|
||||
if err != nil {
|
||||
w.log.Warn("Smartcard wallet nonce retrieval failed", "err", err)
|
||||
|
|
@ -528,7 +503,6 @@ func (w *Wallet) selfDerive() {
|
|||
// If the next account is empty, stop self-derivation, but add for the last base path
|
||||
if balance.Sign() == 0 && nonce == 0 {
|
||||
empty = true
|
||||
|
||||
if i < len(nextAddrs)-1 {
|
||||
break
|
||||
}
|
||||
|
|
@ -542,7 +516,6 @@ func (w *Wallet) selfDerive() {
|
|||
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)
|
||||
}
|
||||
|
||||
pairing.Accounts[nextAddrs[i]] = path
|
||||
|
||||
// Fetch the next potential account
|
||||
|
|
@ -565,7 +538,6 @@ func (w *Wallet) selfDerive() {
|
|||
|
||||
// Notify the user of termination and loop after a bit of time (to avoid trashing)
|
||||
reqc <- struct{}{}
|
||||
|
||||
if err == nil {
|
||||
select {
|
||||
case errc = <-w.deriveQuit:
|
||||
|
|
@ -605,12 +577,9 @@ func (w *Wallet) Accounts() []accounts.Account {
|
|||
for address, path := range pairing.Accounts {
|
||||
ret = append(ret, w.makeAccount(address, path))
|
||||
}
|
||||
|
||||
sort.Sort(accounts.AccountsByURL(ret))
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -630,7 +599,6 @@ func (w *Wallet) Contains(account accounts.Account) bool {
|
|||
_, ok := pairing.Accounts[account.Address]
|
||||
return ok
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -657,7 +625,6 @@ func (w *Wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
|
|||
if pin {
|
||||
pairing := w.Hub.pairing(w)
|
||||
pairing.Accounts[account.Address] = path
|
||||
|
||||
if err := w.Hub.setPairing(w, pairing); err != nil {
|
||||
return accounts.Account{}, err
|
||||
}
|
||||
|
|
@ -689,7 +656,6 @@ func (w *Wallet) SelfDerive(bases []accounts.DerivationPath, chain ethereum.Chai
|
|||
w.deriveNextPaths[i] = make(accounts.DerivationPath, len(base))
|
||||
copy(w.deriveNextPaths[i][:], base[:])
|
||||
}
|
||||
|
||||
w.deriveNextAddrs = make([]common.Address, len(bases))
|
||||
w.deriveChain = chain
|
||||
}
|
||||
|
|
@ -735,12 +701,10 @@ 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) {
|
||||
signer := types.LatestSignerForChainID(chainID)
|
||||
hash := signer.Hash(tx)
|
||||
|
||||
sig, err := w.signHash(account, hash[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tx.WithSignature(signer, sig)
|
||||
}
|
||||
|
||||
|
|
@ -795,7 +759,6 @@ func (w *Wallet) SignTxWithPassphrase(account accounts.Account, passphrase strin
|
|||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return w.SignTx(account, tx, chainID)
|
||||
}
|
||||
|
||||
|
|
@ -852,7 +815,6 @@ func (s *Session) unpair() error {
|
|||
if !s.verified {
|
||||
return fmt.Errorf("unpair requires that the PIN be verified")
|
||||
}
|
||||
|
||||
return s.Channel.Unpair()
|
||||
}
|
||||
|
||||
|
|
@ -861,9 +823,7 @@ func (s *Session) verifyPin(pin []byte) error {
|
|||
if _, err := s.Channel.transmitEncrypted(claSCWallet, insVerifyPin, 0, 0, pin); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.verified = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -873,9 +833,7 @@ func (s *Session) unblockPin(pukpin []byte) error {
|
|||
if _, err := s.Channel.transmitEncrypted(claSCWallet, insUnblockPin, 0, 0, pukpin); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.verified = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -894,10 +852,8 @@ func (s *Session) authenticate(pairing smartcardPairing) error {
|
|||
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)
|
||||
}
|
||||
|
||||
s.Channel.PairingKey = pairing.PairingKey
|
||||
s.Channel.PairingIndex = pairing.PairingIndex
|
||||
|
||||
return s.Channel.Open()
|
||||
}
|
||||
|
||||
|
|
@ -919,22 +875,18 @@ func (s *Session) walletStatus() (*walletStatus, error) {
|
|||
if _, err := asn1.UnmarshalWithParams(response.Data, status, "tag:3"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// derivationPath fetches the wallet's current derivation path from the card.
|
||||
//
|
||||
//lint:ignore U1000 needs to be added to the console interface
|
||||
func (s *Session) derivationPath() (accounts.DerivationPath, error) {
|
||||
response, err := s.Channel.transmitEncrypted(claSCWallet, insStatus, statusP1Path, 0, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := bytes.NewReader(response.Data)
|
||||
path := make(accounts.DerivationPath, len(response.Data)/4)
|
||||
|
||||
return path, binary.Read(buf, binary.BigEndian, &path)
|
||||
}
|
||||
|
||||
|
|
@ -953,7 +905,6 @@ func (s *Session) initialize(seed []byte) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if status == "Online" {
|
||||
return fmt.Errorf("card is already initialized, cowardly refusing to proceed")
|
||||
}
|
||||
|
|
@ -975,7 +926,6 @@ func (s *Session) initialize(seed []byte) error {
|
|||
id.PublicKey = crypto.FromECDSAPub(&key.PublicKey)
|
||||
id.PrivateKey = seed[:32]
|
||||
id.ChainCode = seed[32:]
|
||||
|
||||
data, err := asn1.Marshal(id)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -985,7 +935,6 @@ func (s *Session) initialize(seed []byte) error {
|
|||
data[0] = 0xA1
|
||||
|
||||
_, err = s.Channel.transmitEncrypted(claSCWallet, insLoadKey, 0x02, 0, data)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -997,7 +946,6 @@ func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error)
|
|||
}
|
||||
|
||||
var p1 uint8
|
||||
|
||||
switch startingPoint {
|
||||
case derivationpath.StartingPointMaster:
|
||||
p1 = P1DeriveKeyFromMaster
|
||||
|
|
@ -1030,7 +978,6 @@ func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error)
|
|||
if _, err := asn1.UnmarshalWithParams(response.Data, sigdata, "tag:0"); err != nil {
|
||||
return accounts.Account{}, err
|
||||
}
|
||||
|
||||
rbytes, sbytes := sigdata.Signature.R.Bytes(), sigdata.Signature.S.Bytes()
|
||||
sig := make([]byte, 65)
|
||||
copy(sig[32-len(rbytes):32], rbytes)
|
||||
|
|
@ -1039,17 +986,14 @@ func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error)
|
|||
if err := confirmPublicKey(sig, sigdata.PublicKey); err != nil {
|
||||
return accounts.Account{}, err
|
||||
}
|
||||
|
||||
pub, err := crypto.UnmarshalPubkey(sigdata.PublicKey)
|
||||
if err != nil {
|
||||
return accounts.Account{}, err
|
||||
}
|
||||
|
||||
return s.Wallet.makeAccount(crypto.PubkeyToAddress(*pub), path), nil
|
||||
}
|
||||
|
||||
// keyExport contains information on an exported keypair.
|
||||
//
|
||||
//lint:ignore U1000 needs to be added to the console interface
|
||||
type keyExport struct {
|
||||
PublicKey []byte `asn1:"tag:0"`
|
||||
|
|
@ -1057,19 +1001,16 @@ type keyExport struct {
|
|||
}
|
||||
|
||||
// publicKey returns the public key for the current derivation path.
|
||||
//
|
||||
//lint:ignore U1000 needs to be added to the console interface
|
||||
func (s *Session) publicKey() ([]byte, error) {
|
||||
response, err := s.Channel.transmitEncrypted(claSCWallet, insExportKey, exportP1Any, exportP2Pubkey, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keys := new(keyExport)
|
||||
if _, err := asn1.UnmarshalWithParams(response.Data, keys, "tag:1"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return keys.PublicKey, nil
|
||||
}
|
||||
|
||||
|
|
@ -1087,19 +1028,16 @@ type signatureData struct {
|
|||
// recovering the v value.
|
||||
func (s *Session) sign(path accounts.DerivationPath, hash []byte) ([]byte, error) {
|
||||
startTime := time.Now()
|
||||
|
||||
_, err := s.derive(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deriveTime := time.Now()
|
||||
|
||||
response, err := s.Channel.transmitEncrypted(claSCWallet, insSign, signP1PrecomputedHash, signP2OnlyBlock, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sigdata := new(signatureData)
|
||||
if _, err := asn1.UnmarshalWithParams(response.Data, sigdata, "tag:0"); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -1115,7 +1053,6 @@ func (s *Session) sign(path accounts.DerivationPath, hash []byte) ([]byte, error
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug("Signed using smartcard", "deriveTime", deriveTime.Sub(startTime), "signingTime", time.Since(deriveTime))
|
||||
|
||||
return sig, nil
|
||||
|
|
@ -1131,7 +1068,6 @@ func confirmPublicKey(sig, pubkey []byte) error {
|
|||
// recover the v value and produce a recoverable signature.
|
||||
func makeRecoverableSignature(hash, sig, expectedPubkey []byte) ([]byte, error) {
|
||||
var libraryError error
|
||||
|
||||
for v := 0; v < 2; v++ {
|
||||
sig[64] = byte(v)
|
||||
if pubkey, err := crypto.Ecrecover(hash, sig); err == nil {
|
||||
|
|
@ -1142,10 +1078,8 @@ func makeRecoverableSignature(hash, sig, expectedPubkey []byte) ([]byte, error)
|
|||
libraryError = err
|
||||
}
|
||||
}
|
||||
|
||||
if libraryError != nil {
|
||||
return nil, libraryError
|
||||
}
|
||||
|
||||
return nil, ErrPubkeyMismatch
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ func parseURL(url string) (URL, error) {
|
|||
if len(parts) != 2 || parts[0] == "" {
|
||||
return URL{}, errors.New("protocol scheme missing")
|
||||
}
|
||||
|
||||
return URL{
|
||||
Scheme: parts[0],
|
||||
Path: parts[1],
|
||||
|
|
@ -58,7 +57,6 @@ func (u URL) String() string {
|
|||
if u.Scheme != "" {
|
||||
return fmt.Sprintf("%s://%s", u.Scheme, u.Path)
|
||||
}
|
||||
|
||||
return u.Path
|
||||
}
|
||||
|
||||
|
|
@ -68,7 +66,6 @@ func (u URL) TerminalString() string {
|
|||
if len(url) > 32 {
|
||||
return url[:31] + ".."
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
|
|
@ -80,20 +77,16 @@ func (u URL) MarshalJSON() ([]byte, error) {
|
|||
// UnmarshalJSON parses url.
|
||||
func (u *URL) UnmarshalJSON(input []byte) error {
|
||||
var textURL string
|
||||
|
||||
err := json.Unmarshal(input, &textURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
url, err := parseURL(textURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u.Scheme = url.Scheme
|
||||
u.Path = url.Path
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -102,10 +95,10 @@ func (u *URL) UnmarshalJSON(input []byte) error {
|
|||
// -1 if x < y
|
||||
// 0 if x == y
|
||||
// +1 if x > y
|
||||
//
|
||||
func (u URL) Cmp(url URL) int {
|
||||
if u.Scheme == url.Scheme {
|
||||
return strings.Compare(u.Path, url.Path)
|
||||
}
|
||||
|
||||
return strings.Compare(u.Scheme, url.Scheme)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,19 +25,16 @@ func TestURLParsing(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if url.Scheme != "https" {
|
||||
t.Errorf("expected: %v, got: %v", "https", url.Scheme)
|
||||
}
|
||||
|
||||
if url.Path != "ethereum.org" {
|
||||
t.Errorf("expected: %v, got: %v", "ethereum.org", url.Path)
|
||||
}
|
||||
|
||||
for _, u := range []string{"ethereum.org", ""} {
|
||||
if _, err = parseURL(u); err == nil {
|
||||
t.Errorf("input %v, expected err, got: nil", u)
|
||||
}
|
||||
_, err = parseURL("ethereum.org")
|
||||
if err == nil {
|
||||
t.Error("expected err, got: nil")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -55,12 +52,10 @@ func TestURLString(t *testing.T) {
|
|||
|
||||
func TestURLMarshalJSON(t *testing.T) {
|
||||
url := URL{Scheme: "https", Path: "ethereum.org"}
|
||||
|
||||
json, err := url.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if string(json) != "\"https://ethereum.org\"" {
|
||||
t.Errorf("expected: %v, got: %v", "\"https://ethereum.org\"", string(json))
|
||||
}
|
||||
|
|
@ -68,16 +63,13 @@ func TestURLMarshalJSON(t *testing.T) {
|
|||
|
||||
func TestURLUnmarshalJSON(t *testing.T) {
|
||||
url := &URL{}
|
||||
|
||||
err := url.UnmarshalJSON([]byte("\"https://ethereum.org\""))
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if url.Scheme != "https" {
|
||||
t.Errorf("expected: %v, got: %v", "https", url.Scheme)
|
||||
}
|
||||
|
||||
if url.Path != "ethereum.org" {
|
||||
t.Errorf("expected: %v, got: %v", "https", url.Path)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,28 +71,18 @@ type Hub struct {
|
|||
// NewLedgerHub creates a new hardware wallet manager for Ledger devices.
|
||||
func NewLedgerHub() (*Hub, error) {
|
||||
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
|
||||
0x0000, /* Ledger Blue */
|
||||
0x0001, /* Ledger Nano S */
|
||||
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 */
|
||||
0x1015, /* HID + U2F + WebUSB Ledger Nano S */
|
||||
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 */
|
||||
0x1011, /* HID + WebUSB Ledger Nano S */
|
||||
0x4011, /* HID + WebUSB Ledger Nano X */
|
||||
0x5011, /* HID + WebUSB Ledger Nano S Plus */
|
||||
0x6011, /* HID + WebUSB Ledger Nano FTS */
|
||||
}, 0xffa0, 0, newLedgerDriver)
|
||||
}
|
||||
|
||||
|
|
@ -112,7 +102,6 @@ func newHub(scheme string, vendorID uint16, productIDs []uint16, usageID uint16,
|
|||
if !usb.Supported() {
|
||||
return nil, errors.New("unsupported platform")
|
||||
}
|
||||
|
||||
hub := &Hub{
|
||||
scheme: scheme,
|
||||
vendorID: vendorID,
|
||||
|
|
@ -123,7 +112,6 @@ func newHub(scheme string, vendorID uint16, productIDs []uint16, usageID uint16,
|
|||
quit: make(chan chan error),
|
||||
}
|
||||
hub.refreshWallets()
|
||||
|
||||
return hub, nil
|
||||
}
|
||||
|
||||
|
|
@ -138,7 +126,6 @@ func (hub *Hub) Wallets() []accounts.Wallet {
|
|||
|
||||
cpy := make([]accounts.Wallet, len(hub.wallets))
|
||||
copy(cpy, hub.wallets)
|
||||
|
||||
return cpy
|
||||
}
|
||||
|
||||
|
|
@ -173,22 +160,17 @@ func (hub *Hub) refreshWallets() {
|
|||
return
|
||||
}
|
||||
}
|
||||
|
||||
infos, err := usb.Enumerate(hub.vendorID, 0)
|
||||
if err != nil {
|
||||
failcount := atomic.AddUint32(&hub.enumFails, 1)
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
// See rationale before the enumeration why this is needed and only on Linux.
|
||||
hub.commsLock.Unlock()
|
||||
}
|
||||
|
||||
log.Error("Failed to enumerate USB devices", "hub", hub.scheme,
|
||||
"vendor", hub.vendorID, "failcount", failcount, "err", err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
atomic.StoreUint32(&hub.enumFails, 0)
|
||||
|
||||
for _, info := range infos {
|
||||
|
|
@ -200,7 +182,6 @@ func (hub *Hub) refreshWallets() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
// See rationale before the enumeration why this is needed and only on Linux.
|
||||
hub.commsLock.Unlock()
|
||||
|
|
@ -234,14 +215,12 @@ func (hub *Hub) refreshWallets() {
|
|||
|
||||
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
|
||||
wallets = append(wallets, wallet)
|
||||
|
||||
continue
|
||||
}
|
||||
// If the device is the same as the first wallet, keep it
|
||||
if hub.wallets[0].URL().Cmp(url) == 0 {
|
||||
wallets = append(wallets, hub.wallets[0])
|
||||
hub.wallets = hub.wallets[1:]
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
|
@ -249,7 +228,6 @@ func (hub *Hub) refreshWallets() {
|
|||
for _, wallet := range hub.wallets {
|
||||
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletDropped})
|
||||
}
|
||||
|
||||
hub.refreshed = time.Now()
|
||||
hub.wallets = wallets
|
||||
hub.stateLock.Unlock()
|
||||
|
|
@ -275,7 +253,6 @@ func (hub *Hub) Subscribe(sink chan<- accounts.WalletEvent) event.Subscription {
|
|||
hub.updating = true
|
||||
go hub.updater()
|
||||
}
|
||||
|
||||
return sub
|
||||
}
|
||||
|
||||
|
|
@ -295,7 +272,6 @@ func (hub *Hub) updater() {
|
|||
if hub.updateScope.Count() == 0 {
|
||||
hub.updating = false
|
||||
hub.stateLock.Unlock()
|
||||
|
||||
return
|
||||
}
|
||||
hub.stateLock.Unlock()
|
||||
|
|
|
|||
|
|
@ -59,8 +59,6 @@ const (
|
|||
ledgerP1InitTransactionData ledgerParam1 = 0x00 // First 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
|
||||
|
||||
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
|
||||
|
|
@ -94,15 +92,12 @@ func (w *ledgerDriver) Status() (string, error) {
|
|||
if w.failure != nil {
|
||||
return fmt.Sprintf("Failed: %v", w.failure), w.failure
|
||||
}
|
||||
|
||||
if w.browser {
|
||||
return "Ethereum app in browser mode", w.failure
|
||||
}
|
||||
|
||||
if w.offline() {
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -125,14 +120,12 @@ func (w *ledgerDriver) Open(device io.ReadWriter, passphrase string) error {
|
|||
if err == errLedgerReplyInvalidHeader {
|
||||
w.browser = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
// Try to resolve the Ethereum app's version, will fail prior to v1.0.2
|
||||
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
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -150,7 +143,6 @@ func (w *ledgerDriver) Heartbeat() error {
|
|||
w.failure = err
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -221,15 +213,12 @@ func (w *ledgerDriver) ledgerVersion() ([3]byte, error) {
|
|||
if err != nil {
|
||||
return [3]byte{}, err
|
||||
}
|
||||
|
||||
if len(reply) != 4 {
|
||||
return [3]byte{}, errLedgerInvalidVersionReply
|
||||
}
|
||||
// Cache the version for future reference
|
||||
var version [3]byte
|
||||
|
||||
copy(version[:], reply[1:])
|
||||
|
||||
return version, nil
|
||||
}
|
||||
|
||||
|
|
@ -268,7 +257,6 @@ func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, er
|
|||
// Flatten the derivation path into the Ledger request
|
||||
path := make([]byte, 1+4*len(derivationPath))
|
||||
path[0] = byte(len(derivationPath))
|
||||
|
||||
for i, component := range derivationPath {
|
||||
binary.BigEndian.PutUint32(path[1+4*i:], component)
|
||||
}
|
||||
|
|
@ -281,14 +269,12 @@ func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, er
|
|||
if len(reply) < 1 || len(reply) < 1+int(reply[0]) {
|
||||
return common.Address{}, errors.New("reply lacks public key entry")
|
||||
}
|
||||
|
||||
reply = reply[1+int(reply[0]):]
|
||||
|
||||
// Extract the Ethereum hex address string
|
||||
if len(reply) < 1 || len(reply) < 1+int(reply[0]) {
|
||||
return common.Address{}, errors.New("reply lacks address entry")
|
||||
}
|
||||
|
||||
hexstr := reply[1 : 1+int(reply[0])]
|
||||
|
||||
// Decode the hex string into an Ethereum address and return
|
||||
|
|
@ -296,7 +282,6 @@ func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, er
|
|||
if _, err = hex.Decode(address[:], hexstr); err != nil {
|
||||
return common.Address{}, err
|
||||
}
|
||||
|
||||
return address, nil
|
||||
}
|
||||
|
||||
|
|
@ -338,7 +323,6 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
|
|||
// Flatten the derivation path into the Ledger request
|
||||
path := make([]byte, 1+4*len(derivationPath))
|
||||
path[0] = byte(len(derivationPath))
|
||||
|
||||
for i, component := range derivationPath {
|
||||
binary.BigEndian.PutUint32(path[1+4*i:], component)
|
||||
}
|
||||
|
|
@ -347,7 +331,6 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
|
|||
txrlp []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if chainID == 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
|
||||
|
|
@ -357,7 +340,6 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
|
|||
return common.Address{}, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
payload := append(path, txrlp...)
|
||||
|
||||
// Send the request and wait for the response
|
||||
|
|
@ -365,15 +347,9 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
|
|||
op = ledgerP1InitTransactionData
|
||||
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 {
|
||||
// Calculate the size of the next data chunk
|
||||
chunk := 255
|
||||
if chunk > len(payload) {
|
||||
chunk = len(payload)
|
||||
}
|
||||
|
|
@ -390,7 +366,6 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
|
|||
if len(reply) != crypto.SignatureLength {
|
||||
return common.Address{}, nil, errors.New("reply lacks signature")
|
||||
}
|
||||
|
||||
signature := append(reply[1:], reply[0])
|
||||
|
||||
// Create the correct signer and signature transform based on the chain ID
|
||||
|
|
@ -401,17 +376,14 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
|
|||
signer = types.NewEIP155Signer(chainID)
|
||||
signature[64] -= byte(chainID.Uint64()*2 + 35)
|
||||
}
|
||||
|
||||
signed, err := tx.WithSignature(signer, signature)
|
||||
if err != nil {
|
||||
return common.Address{}, nil, err
|
||||
}
|
||||
|
||||
sender, err := types.Sender(signer, signed)
|
||||
if err != nil {
|
||||
return common.Address{}, nil, err
|
||||
}
|
||||
|
||||
return sender, signed, nil
|
||||
}
|
||||
|
||||
|
|
@ -435,6 +407,8 @@ func (w *ledgerDriver) ledgerSign(derivationPath []uint32, tx *types.Transaction
|
|||
// domain hash | 32 bytes
|
||||
// message hash | 32 bytes
|
||||
//
|
||||
//
|
||||
//
|
||||
// And the output data is:
|
||||
//
|
||||
// Description | Length
|
||||
|
|
@ -446,7 +420,6 @@ func (w *ledgerDriver) ledgerSignTypedMessage(derivationPath []uint32, domainHas
|
|||
// Flatten the derivation path into the Ledger request
|
||||
path := make([]byte, 1+4*len(derivationPath))
|
||||
path[0] = byte(len(derivationPath))
|
||||
|
||||
for i, component := range derivationPath {
|
||||
binary.BigEndian.PutUint32(path[1+4*i:], component)
|
||||
}
|
||||
|
|
@ -472,9 +445,7 @@ func (w *ledgerDriver) ledgerSignTypedMessage(derivationPath []uint32, domainHas
|
|||
if len(reply) != crypto.SignatureLength {
|
||||
return nil, errors.New("reply lacks signature")
|
||||
}
|
||||
|
||||
signature := append(reply[1:], reply[0])
|
||||
|
||||
return signature, nil
|
||||
}
|
||||
|
||||
|
|
@ -538,22 +509,18 @@ func (w *ledgerDriver) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 l
|
|||
}
|
||||
// Send over to the device
|
||||
w.log.Trace("Data chunk sent to the Ledger", "chunk", hexutil.Bytes(chunk))
|
||||
|
||||
if _, err := w.device.Write(chunk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Stream the reply back from the wallet in 64 byte chunks
|
||||
var reply []byte
|
||||
|
||||
chunk = chunk[:64] // Yeah, we surely have enough space
|
||||
|
||||
for {
|
||||
// Read the next chunk from the Ledger wallet
|
||||
if _, err := io.ReadFull(w.device, chunk); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
w.log.Trace("Data chunk received from the Ledger", "chunk", hexutil.Bytes(chunk))
|
||||
|
||||
// Make sure the transport header matches
|
||||
|
|
@ -577,6 +544,5 @@ func (w *ledgerDriver) ledgerExchange(opcode ledgerOpcode, p1 ledgerParam1, p2 l
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
return reply[:len(reply)-2], nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,15 +73,12 @@ func (w *trezorDriver) Status() (string, error) {
|
|||
if w.failure != nil {
|
||||
return fmt.Sprintf("Failed: %v", w.failure), w.failure
|
||||
}
|
||||
|
||||
if w.device == nil {
|
||||
return "Closed", w.failure
|
||||
}
|
||||
|
||||
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' online", w.version[0], w.version[1], w.version[2], w.label), w.failure
|
||||
}
|
||||
|
||||
|
|
@ -110,14 +107,12 @@ func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
|
|||
if _, err := w.trezorExchange(&trezor.Initialize{}, features); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()}
|
||||
w.label = features.GetLabel()
|
||||
|
||||
// Do a manual ping, forcing the device to ask for its PIN and Passphrase
|
||||
askPin := true
|
||||
askPassphrase := true
|
||||
|
||||
res, err := w.trezorExchange(&trezor.Ping{PinProtection: &askPin, PassphraseProtection: &askPassphrase}, new(trezor.PinMatrixRequest), new(trezor.PassphraseRequest), new(trezor.Success))
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -130,7 +125,6 @@ func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
|
|||
case 1:
|
||||
w.pinwait = false
|
||||
w.passphrasewait = true
|
||||
|
||||
return ErrTrezorPassphraseNeeded
|
||||
case 2:
|
||||
return nil // responded with trezor.Success
|
||||
|
|
@ -140,12 +134,10 @@ func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
|
|||
if w.pinwait {
|
||||
w.pinwait = false
|
||||
res, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success), new(trezor.PassphraseRequest))
|
||||
|
||||
if err != nil {
|
||||
w.failure = err
|
||||
return err
|
||||
}
|
||||
|
||||
if res == 1 {
|
||||
w.passphrasewait = true
|
||||
return ErrTrezorPassphraseNeeded
|
||||
|
|
@ -175,7 +167,6 @@ func (w *trezorDriver) Heartbeat() error {
|
|||
w.failure = err
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -191,7 +182,6 @@ func (w *trezorDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio
|
|||
if w.device == nil {
|
||||
return common.Address{}, nil, accounts.ErrWalletClosed
|
||||
}
|
||||
|
||||
return w.trezorSign(path, tx, chainID)
|
||||
}
|
||||
|
||||
|
|
@ -214,7 +204,6 @@ func (w *trezorDriver) trezorDerive(derivationPath []uint32) (common.Address, er
|
|||
if addr := address.GetAddressHex(); len(addr) > 0 { // Newer firmwares use hexadecimal formats
|
||||
return common.HexToAddress(addr), nil
|
||||
}
|
||||
|
||||
return common.Address{}, errors.New("missing derived address")
|
||||
}
|
||||
|
||||
|
|
@ -233,20 +222,17 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
|
|||
Value: tx.Value().Bytes(),
|
||||
DataLength: &length,
|
||||
}
|
||||
|
||||
if to := tx.To(); to != nil {
|
||||
// Non contract deploy, set recipient explicitly
|
||||
hex := to.Hex()
|
||||
request.ToHex = &hex // Newer firmwares (old will ignore)
|
||||
request.ToBin = (*to)[:] // Older firmwares (new will ignore)
|
||||
}
|
||||
|
||||
if length > 1024 { // Send the data chunked if that was requested
|
||||
request.DataInitialChunk, data = data[:1024], data[1024:]
|
||||
} else {
|
||||
request.DataInitialChunk, data = data, nil
|
||||
}
|
||||
|
||||
if chainID != nil { // EIP-155 transaction, set chain ID explicitly (only 32 bit is supported!?)
|
||||
id := uint32(chainID.Int64())
|
||||
request.ChainId = &id
|
||||
|
|
@ -256,7 +242,6 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
|
|||
if _, err := w.trezorExchange(request, response); err != nil {
|
||||
return common.Address{}, nil, err
|
||||
}
|
||||
|
||||
for response.DataLength != nil && int(*response.DataLength) <= len(data) {
|
||||
chunk := data[:*response.DataLength]
|
||||
data = data[*response.DataLength:]
|
||||
|
|
@ -269,7 +254,6 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
|
|||
if len(response.GetSignatureR()) == 0 || len(response.GetSignatureS()) == 0 || response.GetSignatureV() == 0 {
|
||||
return common.Address{}, nil, errors.New("reply lacks signature")
|
||||
}
|
||||
|
||||
signature := append(append(response.GetSignatureR(), response.GetSignatureS()...), byte(response.GetSignatureV()))
|
||||
|
||||
// Create the correct signer and signature transform based on the chain ID
|
||||
|
|
@ -287,12 +271,10 @@ func (w *trezorDriver) trezorSign(derivationPath []uint32, tx *types.Transaction
|
|||
if err != nil {
|
||||
return common.Address{}, nil, err
|
||||
}
|
||||
|
||||
sender, err := types.Sender(signer, signed)
|
||||
if err != nil {
|
||||
return common.Address{}, nil, err
|
||||
}
|
||||
|
||||
return sender, signed, nil
|
||||
}
|
||||
|
||||
|
|
@ -305,7 +287,6 @@ func (w *trezorDriver) trezorExchange(req proto.Message, results ...proto.Messag
|
|||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
payload := make([]byte, 8+len(data))
|
||||
copy(payload, []byte{0x23, 0x23})
|
||||
binary.BigEndian.PutUint16(payload[2:], trezor.Type(req))
|
||||
|
|
@ -328,7 +309,6 @@ func (w *trezorDriver) trezorExchange(req proto.Message, results ...proto.Messag
|
|||
}
|
||||
// Send over to the device
|
||||
w.log.Trace("Data chunk sent to the Trezor", "chunk", hexutil.Bytes(chunk))
|
||||
|
||||
if _, err := w.device.Write(chunk); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
|
@ -338,13 +318,11 @@ func (w *trezorDriver) trezorExchange(req proto.Message, results ...proto.Messag
|
|||
kind uint16
|
||||
reply []byte
|
||||
)
|
||||
|
||||
for {
|
||||
// Read the next chunk from the Trezor wallet
|
||||
if _, err := io.ReadFull(w.device, chunk); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
w.log.Trace("Data chunk received from the Trezor", "chunk", hexutil.Bytes(chunk))
|
||||
|
||||
// Make sure the transport header matches
|
||||
|
|
@ -376,25 +354,20 @@ func (w *trezorDriver) trezorExchange(req proto.Message, results ...proto.Messag
|
|||
if err := proto.Unmarshal(reply, failure); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return 0, errors.New("trezor: " + failure.GetMessage())
|
||||
}
|
||||
|
||||
if kind == uint16(trezor.MessageType_MessageType_ButtonRequest) {
|
||||
// Trezor is waiting for user confirmation, ack and wait for the next message
|
||||
return w.trezorExchange(&trezor.ButtonAck{}, results...)
|
||||
}
|
||||
|
||||
for i, res := range results {
|
||||
if trezor.Type(res) == kind {
|
||||
return i, proto.Unmarshal(reply, res)
|
||||
}
|
||||
}
|
||||
|
||||
expected := make([]string, len(results))
|
||||
for i, res := range results {
|
||||
expected[i] = trezor.Name(trezor.Type(res))
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("trezor: expected reply types %s, got %s", expected, trezor.Name(kind))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,6 @@ var Failure_FailureType_value = map[string]int32{
|
|||
func (x Failure_FailureType) Enum() *Failure_FailureType {
|
||||
p := new(Failure_FailureType)
|
||||
*p = x
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
|
|
@ -87,9 +86,7 @@ func (x *Failure_FailureType) UnmarshalJSON(data []byte) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*x = Failure_FailureType(value)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -158,7 +155,6 @@ var ButtonRequest_ButtonRequestType_value = map[string]int32{
|
|||
func (x ButtonRequest_ButtonRequestType) Enum() *ButtonRequest_ButtonRequestType {
|
||||
p := new(ButtonRequest_ButtonRequestType)
|
||||
*p = x
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
|
|
@ -171,9 +167,7 @@ func (x *ButtonRequest_ButtonRequestType) UnmarshalJSON(data []byte) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*x = ButtonRequest_ButtonRequestType(value)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -206,7 +200,6 @@ var PinMatrixRequest_PinMatrixRequestType_value = map[string]int32{
|
|||
func (x PinMatrixRequest_PinMatrixRequestType) Enum() *PinMatrixRequest_PinMatrixRequestType {
|
||||
p := new(PinMatrixRequest_PinMatrixRequestType)
|
||||
*p = x
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
|
|
@ -219,9 +212,7 @@ func (x *PinMatrixRequest_PinMatrixRequestType) UnmarshalJSON(data []byte) error
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*x = PinMatrixRequest_PinMatrixRequestType(value)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -268,7 +259,6 @@ func (m *Success) GetMessage() string {
|
|||
if m != nil && m.Message != nil {
|
||||
return *m.Message
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -312,7 +302,6 @@ func (m *Failure) GetCode() Failure_FailureType {
|
|||
if m != nil && m.Code != nil {
|
||||
return *m.Code
|
||||
}
|
||||
|
||||
return Failure_Failure_UnexpectedMessage
|
||||
}
|
||||
|
||||
|
|
@ -320,7 +309,6 @@ func (m *Failure) GetMessage() string {
|
|||
if m != nil && m.Message != nil {
|
||||
return *m.Message
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -365,7 +353,6 @@ func (m *ButtonRequest) GetCode() ButtonRequest_ButtonRequestType {
|
|||
if m != nil && m.Code != nil {
|
||||
return *m.Code
|
||||
}
|
||||
|
||||
return ButtonRequest_ButtonRequest_Other
|
||||
}
|
||||
|
||||
|
|
@ -373,7 +360,6 @@ func (m *ButtonRequest) GetData() string {
|
|||
if m != nil && m.Data != nil {
|
||||
return *m.Data
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -451,7 +437,6 @@ func (m *PinMatrixRequest) GetType() PinMatrixRequest_PinMatrixRequestType {
|
|||
if m != nil && m.Type != nil {
|
||||
return *m.Type
|
||||
}
|
||||
|
||||
return PinMatrixRequest_PinMatrixRequestType_Current
|
||||
}
|
||||
|
||||
|
|
@ -494,7 +479,6 @@ func (m *PinMatrixAck) GetPin() string {
|
|||
if m != nil && m.Pin != nil {
|
||||
return *m.Pin
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -538,7 +522,6 @@ func (m *PassphraseRequest) GetOnDevice() bool {
|
|||
if m != nil && m.OnDevice != nil {
|
||||
return *m.OnDevice
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -582,7 +565,6 @@ func (m *PassphraseAck) GetPassphrase() string {
|
|||
if m != nil && m.Passphrase != nil {
|
||||
return *m.Passphrase
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -590,7 +572,6 @@ func (m *PassphraseAck) GetState() []byte {
|
|||
if m != nil {
|
||||
return m.State
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -633,7 +614,6 @@ func (m *PassphraseStateRequest) GetState() []byte {
|
|||
if m != nil {
|
||||
return m.State
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -716,7 +696,6 @@ func (m *HDNodeType) GetDepth() uint32 {
|
|||
if m != nil && m.Depth != nil {
|
||||
return *m.Depth
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -724,7 +703,6 @@ func (m *HDNodeType) GetFingerprint() uint32 {
|
|||
if m != nil && m.Fingerprint != nil {
|
||||
return *m.Fingerprint
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -732,7 +710,6 @@ func (m *HDNodeType) GetChildNum() uint32 {
|
|||
if m != nil && m.ChildNum != nil {
|
||||
return *m.ChildNum
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -740,7 +717,6 @@ func (m *HDNodeType) GetChainCode() []byte {
|
|||
if m != nil {
|
||||
return m.ChainCode
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -748,7 +724,6 @@ func (m *HDNodeType) GetPrivateKey() []byte {
|
|||
if m != nil {
|
||||
return m.PrivateKey
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -756,7 +731,6 @@ func (m *HDNodeType) GetPublicKey() []byte {
|
|||
if m != nil {
|
||||
return m.PublicKey
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ func (m *EthereumGetPublicKey) GetAddressN() []uint32 {
|
|||
if m != nil {
|
||||
return m.AddressN
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -71,7 +70,6 @@ func (m *EthereumGetPublicKey) GetShowDisplay() bool {
|
|||
if m != nil && m.ShowDisplay != nil {
|
||||
return *m.ShowDisplay
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -115,7 +113,6 @@ func (m *EthereumPublicKey) GetNode() *HDNodeType {
|
|||
if m != nil {
|
||||
return m.Node
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -123,7 +120,6 @@ func (m *EthereumPublicKey) GetXpub() string {
|
|||
if m != nil && m.Xpub != nil {
|
||||
return *m.Xpub
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -169,7 +165,6 @@ func (m *EthereumGetAddress) GetAddressN() []uint32 {
|
|||
if m != nil {
|
||||
return m.AddressN
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -177,7 +172,6 @@ func (m *EthereumGetAddress) GetShowDisplay() bool {
|
|||
if m != nil && m.ShowDisplay != nil {
|
||||
return *m.ShowDisplay
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -221,7 +215,6 @@ func (m *EthereumAddress) GetAddressBin() []byte {
|
|||
if m != nil {
|
||||
return m.AddressBin
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -229,7 +222,6 @@ func (m *EthereumAddress) GetAddressHex() string {
|
|||
if m != nil && m.AddressHex != nil {
|
||||
return *m.AddressHex
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -286,7 +278,6 @@ func (m *EthereumSignTx) GetAddressN() []uint32 {
|
|||
if m != nil {
|
||||
return m.AddressN
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -294,7 +285,6 @@ func (m *EthereumSignTx) GetNonce() []byte {
|
|||
if m != nil {
|
||||
return m.Nonce
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -302,7 +292,6 @@ func (m *EthereumSignTx) GetGasPrice() []byte {
|
|||
if m != nil {
|
||||
return m.GasPrice
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -310,7 +299,6 @@ func (m *EthereumSignTx) GetGasLimit() []byte {
|
|||
if m != nil {
|
||||
return m.GasLimit
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -318,7 +306,6 @@ func (m *EthereumSignTx) GetToBin() []byte {
|
|||
if m != nil {
|
||||
return m.ToBin
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -326,7 +313,6 @@ func (m *EthereumSignTx) GetToHex() string {
|
|||
if m != nil && m.ToHex != nil {
|
||||
return *m.ToHex
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -334,7 +320,6 @@ func (m *EthereumSignTx) GetValue() []byte {
|
|||
if m != nil {
|
||||
return m.Value
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -342,7 +327,6 @@ func (m *EthereumSignTx) GetDataInitialChunk() []byte {
|
|||
if m != nil {
|
||||
return m.DataInitialChunk
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -350,7 +334,6 @@ func (m *EthereumSignTx) GetDataLength() uint32 {
|
|||
if m != nil && m.DataLength != nil {
|
||||
return *m.DataLength
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -358,7 +341,6 @@ func (m *EthereumSignTx) GetChainId() uint32 {
|
|||
if m != nil && m.ChainId != nil {
|
||||
return *m.ChainId
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -366,7 +348,6 @@ func (m *EthereumSignTx) GetTxType() uint32 {
|
|||
if m != nil && m.TxType != nil {
|
||||
return *m.TxType
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -415,7 +396,6 @@ func (m *EthereumTxRequest) GetDataLength() uint32 {
|
|||
if m != nil && m.DataLength != nil {
|
||||
return *m.DataLength
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -423,7 +403,6 @@ func (m *EthereumTxRequest) GetSignatureV() uint32 {
|
|||
if m != nil && m.SignatureV != nil {
|
||||
return *m.SignatureV
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -431,7 +410,6 @@ func (m *EthereumTxRequest) GetSignatureR() []byte {
|
|||
if m != nil {
|
||||
return m.SignatureR
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -439,7 +417,6 @@ func (m *EthereumTxRequest) GetSignatureS() []byte {
|
|||
if m != nil {
|
||||
return m.SignatureS
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -482,7 +459,6 @@ func (m *EthereumTxAck) GetDataChunk() []byte {
|
|||
if m != nil {
|
||||
return m.DataChunk
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -528,7 +504,6 @@ func (m *EthereumSignMessage) GetAddressN() []uint32 {
|
|||
if m != nil {
|
||||
return m.AddressN
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -536,7 +511,6 @@ func (m *EthereumSignMessage) GetMessage() []byte {
|
|||
if m != nil {
|
||||
return m.Message
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -581,7 +555,6 @@ func (m *EthereumMessageSignature) GetAddressBin() []byte {
|
|||
if m != nil {
|
||||
return m.AddressBin
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -589,7 +562,6 @@ func (m *EthereumMessageSignature) GetSignature() []byte {
|
|||
if m != nil {
|
||||
return m.Signature
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -597,7 +569,6 @@ func (m *EthereumMessageSignature) GetAddressHex() string {
|
|||
if m != nil && m.AddressHex != nil {
|
||||
return *m.AddressHex
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -645,7 +616,6 @@ func (m *EthereumVerifyMessage) GetAddressBin() []byte {
|
|||
if m != nil {
|
||||
return m.AddressBin
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -653,7 +623,6 @@ func (m *EthereumVerifyMessage) GetSignature() []byte {
|
|||
if m != nil {
|
||||
return m.Signature
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -661,7 +630,6 @@ func (m *EthereumVerifyMessage) GetMessage() []byte {
|
|||
if m != nil {
|
||||
return m.Message
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -669,7 +637,6 @@ func (m *EthereumVerifyMessage) GetAddressHex() string {
|
|||
if m != nil && m.AddressHex != nil {
|
||||
return *m.AddressHex
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ var ApplySettings_PassphraseSourceType_value = map[string]int32{
|
|||
func (x ApplySettings_PassphraseSourceType) Enum() *ApplySettings_PassphraseSourceType {
|
||||
p := new(ApplySettings_PassphraseSourceType)
|
||||
*p = x
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
|
|
@ -59,9 +58,7 @@ func (x *ApplySettings_PassphraseSourceType) UnmarshalJSON(data []byte) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*x = ApplySettings_PassphraseSourceType(value)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -97,7 +94,6 @@ var RecoveryDevice_RecoveryDeviceType_value = map[string]int32{
|
|||
func (x RecoveryDevice_RecoveryDeviceType) Enum() *RecoveryDevice_RecoveryDeviceType {
|
||||
p := new(RecoveryDevice_RecoveryDeviceType)
|
||||
*p = x
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
|
|
@ -110,9 +106,7 @@ func (x *RecoveryDevice_RecoveryDeviceType) UnmarshalJSON(data []byte) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*x = RecoveryDevice_RecoveryDeviceType(value)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +139,6 @@ var WordRequest_WordRequestType_value = map[string]int32{
|
|||
func (x WordRequest_WordRequestType) Enum() *WordRequest_WordRequestType {
|
||||
p := new(WordRequest_WordRequestType)
|
||||
*p = x
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
|
|
@ -158,9 +151,7 @@ func (x *WordRequest_WordRequestType) UnmarshalJSON(data []byte) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*x = WordRequest_WordRequestType(value)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -209,7 +200,6 @@ func (m *Initialize) GetState() []byte {
|
|||
if m != nil {
|
||||
return m.State
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -217,7 +207,6 @@ func (m *Initialize) GetSkipPassphrase() bool {
|
|||
if m != nil && m.SkipPassphrase != nil {
|
||||
return *m.SkipPassphrase
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -321,7 +310,6 @@ func (m *Features) GetVendor() string {
|
|||
if m != nil && m.Vendor != nil {
|
||||
return *m.Vendor
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -329,7 +317,6 @@ func (m *Features) GetMajorVersion() uint32 {
|
|||
if m != nil && m.MajorVersion != nil {
|
||||
return *m.MajorVersion
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -337,7 +324,6 @@ func (m *Features) GetMinorVersion() uint32 {
|
|||
if m != nil && m.MinorVersion != nil {
|
||||
return *m.MinorVersion
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -345,7 +331,6 @@ func (m *Features) GetPatchVersion() uint32 {
|
|||
if m != nil && m.PatchVersion != nil {
|
||||
return *m.PatchVersion
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -353,7 +338,6 @@ func (m *Features) GetBootloaderMode() bool {
|
|||
if m != nil && m.BootloaderMode != nil {
|
||||
return *m.BootloaderMode
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -361,7 +345,6 @@ func (m *Features) GetDeviceId() string {
|
|||
if m != nil && m.DeviceId != nil {
|
||||
return *m.DeviceId
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -369,7 +352,6 @@ func (m *Features) GetPinProtection() bool {
|
|||
if m != nil && m.PinProtection != nil {
|
||||
return *m.PinProtection
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -377,7 +359,6 @@ func (m *Features) GetPassphraseProtection() bool {
|
|||
if m != nil && m.PassphraseProtection != nil {
|
||||
return *m.PassphraseProtection
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -385,7 +366,6 @@ func (m *Features) GetLanguage() string {
|
|||
if m != nil && m.Language != nil {
|
||||
return *m.Language
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -393,7 +373,6 @@ func (m *Features) GetLabel() string {
|
|||
if m != nil && m.Label != nil {
|
||||
return *m.Label
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -401,7 +380,6 @@ func (m *Features) GetInitialized() bool {
|
|||
if m != nil && m.Initialized != nil {
|
||||
return *m.Initialized
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -409,7 +387,6 @@ func (m *Features) GetRevision() []byte {
|
|||
if m != nil {
|
||||
return m.Revision
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -417,7 +394,6 @@ func (m *Features) GetBootloaderHash() []byte {
|
|||
if m != nil {
|
||||
return m.BootloaderHash
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -425,7 +401,6 @@ func (m *Features) GetImported() bool {
|
|||
if m != nil && m.Imported != nil {
|
||||
return *m.Imported
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -433,7 +408,6 @@ func (m *Features) GetPinCached() bool {
|
|||
if m != nil && m.PinCached != nil {
|
||||
return *m.PinCached
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -441,7 +415,6 @@ func (m *Features) GetPassphraseCached() bool {
|
|||
if m != nil && m.PassphraseCached != nil {
|
||||
return *m.PassphraseCached
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -449,7 +422,6 @@ func (m *Features) GetFirmwarePresent() bool {
|
|||
if m != nil && m.FirmwarePresent != nil {
|
||||
return *m.FirmwarePresent
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -457,7 +429,6 @@ func (m *Features) GetNeedsBackup() bool {
|
|||
if m != nil && m.NeedsBackup != nil {
|
||||
return *m.NeedsBackup
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -465,7 +436,6 @@ func (m *Features) GetFlags() uint32 {
|
|||
if m != nil && m.Flags != nil {
|
||||
return *m.Flags
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -473,7 +443,6 @@ func (m *Features) GetModel() string {
|
|||
if m != nil && m.Model != nil {
|
||||
return *m.Model
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -481,7 +450,6 @@ func (m *Features) GetFwMajor() uint32 {
|
|||
if m != nil && m.FwMajor != nil {
|
||||
return *m.FwMajor
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -489,7 +457,6 @@ func (m *Features) GetFwMinor() uint32 {
|
|||
if m != nil && m.FwMinor != nil {
|
||||
return *m.FwMinor
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -497,7 +464,6 @@ func (m *Features) GetFwPatch() uint32 {
|
|||
if m != nil && m.FwPatch != nil {
|
||||
return *m.FwPatch
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -505,7 +471,6 @@ func (m *Features) GetFwVendor() string {
|
|||
if m != nil && m.FwVendor != nil {
|
||||
return *m.FwVendor
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -513,7 +478,6 @@ func (m *Features) GetFwVendorKeys() []byte {
|
|||
if m != nil {
|
||||
return m.FwVendorKeys
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -521,7 +485,6 @@ func (m *Features) GetUnfinishedBackup() bool {
|
|||
if m != nil && m.UnfinishedBackup != nil {
|
||||
return *m.UnfinishedBackup
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -529,7 +492,6 @@ func (m *Features) GetNoBackup() bool {
|
|||
if m != nil && m.NoBackup != nil {
|
||||
return *m.NoBackup
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -615,7 +577,6 @@ func (m *ApplySettings) GetLanguage() string {
|
|||
if m != nil && m.Language != nil {
|
||||
return *m.Language
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -623,7 +584,6 @@ func (m *ApplySettings) GetLabel() string {
|
|||
if m != nil && m.Label != nil {
|
||||
return *m.Label
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -631,7 +591,6 @@ func (m *ApplySettings) GetUsePassphrase() bool {
|
|||
if m != nil && m.UsePassphrase != nil {
|
||||
return *m.UsePassphrase
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -639,7 +598,6 @@ func (m *ApplySettings) GetHomescreen() []byte {
|
|||
if m != nil {
|
||||
return m.Homescreen
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -647,7 +605,6 @@ func (m *ApplySettings) GetPassphraseSource() ApplySettings_PassphraseSourceType
|
|||
if m != nil && m.PassphraseSource != nil {
|
||||
return *m.PassphraseSource
|
||||
}
|
||||
|
||||
return ApplySettings_ASK
|
||||
}
|
||||
|
||||
|
|
@ -655,7 +612,6 @@ func (m *ApplySettings) GetAutoLockDelayMs() uint32 {
|
|||
if m != nil && m.AutoLockDelayMs != nil {
|
||||
return *m.AutoLockDelayMs
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -663,7 +619,6 @@ func (m *ApplySettings) GetDisplayRotation() uint32 {
|
|||
if m != nil && m.DisplayRotation != nil {
|
||||
return *m.DisplayRotation
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -708,7 +663,6 @@ func (m *ApplyFlags) GetFlags() uint32 {
|
|||
if m != nil && m.Flags != nil {
|
||||
return *m.Flags
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -753,7 +707,6 @@ func (m *ChangePin) GetRemove() bool {
|
|||
if m != nil && m.Remove != nil {
|
||||
return *m.Remove
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -800,7 +753,6 @@ func (m *Ping) GetMessage() string {
|
|||
if m != nil && m.Message != nil {
|
||||
return *m.Message
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -808,7 +760,6 @@ func (m *Ping) GetButtonProtection() bool {
|
|||
if m != nil && m.ButtonProtection != nil {
|
||||
return *m.ButtonProtection
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -816,7 +767,6 @@ func (m *Ping) GetPinProtection() bool {
|
|||
if m != nil && m.PinProtection != nil {
|
||||
return *m.PinProtection
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -824,7 +774,6 @@ func (m *Ping) GetPassphraseProtection() bool {
|
|||
if m != nil && m.PassphraseProtection != nil {
|
||||
return *m.PassphraseProtection
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -904,7 +853,6 @@ func (m *GetEntropy) GetSize() uint32 {
|
|||
if m != nil && m.Size != nil {
|
||||
return *m.Size
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -947,7 +895,6 @@ func (m *Entropy) GetEntropy() []byte {
|
|||
if m != nil {
|
||||
return m.Entropy
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1037,7 +984,6 @@ func (m *LoadDevice) GetMnemonic() string {
|
|||
if m != nil && m.Mnemonic != nil {
|
||||
return *m.Mnemonic
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -1045,7 +991,6 @@ func (m *LoadDevice) GetNode() *HDNodeType {
|
|||
if m != nil {
|
||||
return m.Node
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1053,7 +998,6 @@ func (m *LoadDevice) GetPin() string {
|
|||
if m != nil && m.Pin != nil {
|
||||
return *m.Pin
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -1061,7 +1005,6 @@ func (m *LoadDevice) GetPassphraseProtection() bool {
|
|||
if m != nil && m.PassphraseProtection != nil {
|
||||
return *m.PassphraseProtection
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -1069,7 +1012,6 @@ func (m *LoadDevice) GetLanguage() string {
|
|||
if m != nil && m.Language != nil {
|
||||
return *m.Language
|
||||
}
|
||||
|
||||
return Default_LoadDevice_Language
|
||||
}
|
||||
|
||||
|
|
@ -1077,7 +1019,6 @@ func (m *LoadDevice) GetLabel() string {
|
|||
if m != nil && m.Label != nil {
|
||||
return *m.Label
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -1085,7 +1026,6 @@ func (m *LoadDevice) GetSkipChecksum() bool {
|
|||
if m != nil && m.SkipChecksum != nil {
|
||||
return *m.SkipChecksum
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -1093,7 +1033,6 @@ func (m *LoadDevice) GetU2FCounter() uint32 {
|
|||
if m != nil && m.U2FCounter != nil {
|
||||
return *m.U2FCounter
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -1149,7 +1088,6 @@ func (m *ResetDevice) GetDisplayRandom() bool {
|
|||
if m != nil && m.DisplayRandom != nil {
|
||||
return *m.DisplayRandom
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -1157,7 +1095,6 @@ func (m *ResetDevice) GetStrength() uint32 {
|
|||
if m != nil && m.Strength != nil {
|
||||
return *m.Strength
|
||||
}
|
||||
|
||||
return Default_ResetDevice_Strength
|
||||
}
|
||||
|
||||
|
|
@ -1165,7 +1102,6 @@ func (m *ResetDevice) GetPassphraseProtection() bool {
|
|||
if m != nil && m.PassphraseProtection != nil {
|
||||
return *m.PassphraseProtection
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -1173,7 +1109,6 @@ func (m *ResetDevice) GetPinProtection() bool {
|
|||
if m != nil && m.PinProtection != nil {
|
||||
return *m.PinProtection
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -1181,7 +1116,6 @@ func (m *ResetDevice) GetLanguage() string {
|
|||
if m != nil && m.Language != nil {
|
||||
return *m.Language
|
||||
}
|
||||
|
||||
return Default_ResetDevice_Language
|
||||
}
|
||||
|
||||
|
|
@ -1189,7 +1123,6 @@ func (m *ResetDevice) GetLabel() string {
|
|||
if m != nil && m.Label != nil {
|
||||
return *m.Label
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -1197,7 +1130,6 @@ func (m *ResetDevice) GetU2FCounter() uint32 {
|
|||
if m != nil && m.U2FCounter != nil {
|
||||
return *m.U2FCounter
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -1205,7 +1137,6 @@ func (m *ResetDevice) GetSkipBackup() bool {
|
|||
if m != nil && m.SkipBackup != nil {
|
||||
return *m.SkipBackup
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -1213,7 +1144,6 @@ func (m *ResetDevice) GetNoBackup() bool {
|
|||
if m != nil && m.NoBackup != nil {
|
||||
return *m.NoBackup
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -1325,7 +1255,6 @@ func (m *EntropyAck) GetEntropy() []byte {
|
|||
if m != nil {
|
||||
return m.Entropy
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1381,7 +1310,6 @@ func (m *RecoveryDevice) GetWordCount() uint32 {
|
|||
if m != nil && m.WordCount != nil {
|
||||
return *m.WordCount
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -1389,7 +1317,6 @@ func (m *RecoveryDevice) GetPassphraseProtection() bool {
|
|||
if m != nil && m.PassphraseProtection != nil {
|
||||
return *m.PassphraseProtection
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -1397,7 +1324,6 @@ func (m *RecoveryDevice) GetPinProtection() bool {
|
|||
if m != nil && m.PinProtection != nil {
|
||||
return *m.PinProtection
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -1405,7 +1331,6 @@ func (m *RecoveryDevice) GetLanguage() string {
|
|||
if m != nil && m.Language != nil {
|
||||
return *m.Language
|
||||
}
|
||||
|
||||
return Default_RecoveryDevice_Language
|
||||
}
|
||||
|
||||
|
|
@ -1413,7 +1338,6 @@ func (m *RecoveryDevice) GetLabel() string {
|
|||
if m != nil && m.Label != nil {
|
||||
return *m.Label
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -1421,7 +1345,6 @@ func (m *RecoveryDevice) GetEnforceWordlist() bool {
|
|||
if m != nil && m.EnforceWordlist != nil {
|
||||
return *m.EnforceWordlist
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -1429,7 +1352,6 @@ func (m *RecoveryDevice) GetType() RecoveryDevice_RecoveryDeviceType {
|
|||
if m != nil && m.Type != nil {
|
||||
return *m.Type
|
||||
}
|
||||
|
||||
return RecoveryDevice_RecoveryDeviceType_ScrambledWords
|
||||
}
|
||||
|
||||
|
|
@ -1437,7 +1359,6 @@ func (m *RecoveryDevice) GetU2FCounter() uint32 {
|
|||
if m != nil && m.U2FCounter != nil {
|
||||
return *m.U2FCounter
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -1445,7 +1366,6 @@ func (m *RecoveryDevice) GetDryRun() bool {
|
|||
if m != nil && m.DryRun != nil {
|
||||
return *m.DryRun
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -1489,7 +1409,6 @@ func (m *WordRequest) GetType() WordRequest_WordRequestType {
|
|||
if m != nil && m.Type != nil {
|
||||
return *m.Type
|
||||
}
|
||||
|
||||
return WordRequest_WordRequestType_Plain
|
||||
}
|
||||
|
||||
|
|
@ -1534,7 +1453,6 @@ func (m *WordAck) GetWord() string {
|
|||
if m != nil && m.Word != nil {
|
||||
return *m.Word
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
|
|
@ -1578,7 +1496,6 @@ func (m *SetU2FCounter) GetU2FCounter() uint32 {
|
|||
if m != nil && m.U2FCounter != nil {
|
||||
return *m.U2FCounter
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -636,7 +636,6 @@ var MessageType_value = map[string]int32{
|
|||
func (x MessageType) Enum() *MessageType {
|
||||
p := new(MessageType)
|
||||
*p = x
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
|
|
@ -649,9 +648,7 @@ func (x *MessageType) UnmarshalJSON(data []byte) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*x = MessageType(value)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,6 +66,5 @@ func Name(kind uint16) string {
|
|||
if len(name) < 12 {
|
||||
return name
|
||||
}
|
||||
|
||||
return name[12:]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,7 +133,6 @@ func (w *wallet) Status() (string, error) {
|
|||
if w.device == nil {
|
||||
return "Closed", failure
|
||||
}
|
||||
|
||||
return status, failure
|
||||
}
|
||||
|
||||
|
|
@ -153,7 +152,6 @@ func (w *wallet) Open(passphrase string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w.device = device
|
||||
w.commsLock = make(chan struct{}, 1)
|
||||
w.commsLock <- struct{}{} // Enable lock
|
||||
|
|
@ -189,7 +187,6 @@ func (w *wallet) heartbeat() {
|
|||
errc chan error
|
||||
err error
|
||||
)
|
||||
|
||||
for errc == nil && err == nil {
|
||||
// Wait until termination is requested or the heartbeat cycle arrives
|
||||
select {
|
||||
|
|
@ -206,7 +203,6 @@ func (w *wallet) heartbeat() {
|
|||
w.stateLock.RUnlock()
|
||||
continue
|
||||
}
|
||||
|
||||
<-w.commsLock // Don't lock state while resolving version
|
||||
err = w.driver.Heartbeat()
|
||||
w.commsLock <- struct{}{}
|
||||
|
|
@ -237,7 +233,6 @@ func (w *wallet) Close() error {
|
|||
|
||||
// Terminate the health checks
|
||||
var herr error
|
||||
|
||||
if hQuit != nil {
|
||||
errc := make(chan error)
|
||||
hQuit <- errc
|
||||
|
|
@ -245,7 +240,6 @@ func (w *wallet) Close() error {
|
|||
}
|
||||
// Terminate the self-derivations
|
||||
var derr error
|
||||
|
||||
if dQuit != nil {
|
||||
errc := make(chan error)
|
||||
dQuit <- errc
|
||||
|
|
@ -262,11 +256,9 @@ func (w *wallet) Close() error {
|
|||
if err := w.close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if herr != nil {
|
||||
return herr
|
||||
}
|
||||
|
||||
return derr
|
||||
}
|
||||
|
||||
|
|
@ -284,7 +276,6 @@ func (w *wallet) close() error {
|
|||
w.device = nil
|
||||
|
||||
w.accounts, w.paths = nil, nil
|
||||
|
||||
return w.driver.Close()
|
||||
}
|
||||
|
||||
|
|
@ -307,7 +298,6 @@ func (w *wallet) Accounts() []accounts.Account {
|
|||
|
||||
cpy := make([]accounts.Account, len(w.accounts))
|
||||
copy(cpy, w.accounts)
|
||||
|
||||
return cpy
|
||||
}
|
||||
|
||||
|
|
@ -323,7 +313,6 @@ func (w *wallet) selfDerive() {
|
|||
errc chan error
|
||||
err error
|
||||
)
|
||||
|
||||
for errc == nil && err == nil {
|
||||
// Wait until either derivation or termination is requested
|
||||
select {
|
||||
|
|
@ -338,7 +327,6 @@ func (w *wallet) selfDerive() {
|
|||
if w.device == nil || w.deriveChain == nil {
|
||||
w.stateLock.RUnlock()
|
||||
reqc <- struct{}{}
|
||||
|
||||
continue
|
||||
}
|
||||
select {
|
||||
|
|
@ -346,7 +334,6 @@ func (w *wallet) selfDerive() {
|
|||
default:
|
||||
w.stateLock.RUnlock()
|
||||
reqc <- struct{}{}
|
||||
|
||||
continue
|
||||
}
|
||||
// Device lock obtained, derive the next batch of accounts
|
||||
|
|
@ -359,7 +346,6 @@ func (w *wallet) selfDerive() {
|
|||
|
||||
context = context.Background()
|
||||
)
|
||||
|
||||
for i := 0; i < len(nextAddrs); i++ {
|
||||
for empty := false; !empty; {
|
||||
// Retrieve the next derived Ethereum account
|
||||
|
|
@ -374,13 +360,11 @@ func (w *wallet) selfDerive() {
|
|||
balance *big.Int
|
||||
nonce uint64
|
||||
)
|
||||
|
||||
balance, err = w.deriveChain.BalanceAt(context, nextAddrs[i], nil)
|
||||
if err != nil {
|
||||
w.log.Warn("USB wallet balance retrieval failed", "err", err)
|
||||
break
|
||||
}
|
||||
|
||||
nonce, err = w.deriveChain.NonceAt(context, nextAddrs[i], nil)
|
||||
if err != nil {
|
||||
w.log.Warn("USB wallet nonce retrieval failed", "err", err)
|
||||
|
|
@ -390,19 +374,17 @@ func (w *wallet) selfDerive() {
|
|||
// unless the account was empty.
|
||||
path := make(accounts.DerivationPath, len(nextPaths[i]))
|
||||
copy(path[:], nextPaths[i][:])
|
||||
|
||||
if balance.Sign() == 0 && nonce == 0 {
|
||||
empty = true
|
||||
// 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
|
||||
// be shown to the user, even if we don't actively track it
|
||||
if i < len(nextAddrs)-1 {
|
||||
w.log.Info("Skipping tracking first account on legacy path, use personal.deriveAccount(<url>,<path>, false) to track",
|
||||
w.log.Info("Skipping trakcking first account on legacy path, use personal.deriveAccount(<url>,<path>, false) to track",
|
||||
"path", path, "address", nextAddrs[i])
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
paths = append(paths, path)
|
||||
account := accounts.Account{
|
||||
Address: nextAddrs[i],
|
||||
|
|
@ -441,7 +423,6 @@ func (w *wallet) selfDerive() {
|
|||
|
||||
// Notify the user of termination and loop after a bit of time (to avoid trashing)
|
||||
reqc <- struct{}{}
|
||||
|
||||
if err == nil {
|
||||
select {
|
||||
case errc = <-w.deriveQuit:
|
||||
|
|
@ -467,7 +448,6 @@ func (w *wallet) Contains(account accounts.Account) bool {
|
|||
defer w.stateLock.RUnlock()
|
||||
|
||||
_, exists := w.paths[account.Address]
|
||||
|
||||
return exists
|
||||
}
|
||||
|
||||
|
|
@ -482,7 +462,6 @@ func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
|
|||
w.stateLock.RUnlock()
|
||||
return accounts.Account{}, accounts.ErrWalletClosed
|
||||
}
|
||||
|
||||
<-w.commsLock // Avoid concurrent hardware access
|
||||
address, err := w.driver.Derive(path)
|
||||
w.commsLock <- struct{}{}
|
||||
|
|
@ -493,7 +472,6 @@ func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
|
|||
if err != nil {
|
||||
return accounts.Account{}, err
|
||||
}
|
||||
|
||||
account := accounts.Account{
|
||||
Address: address,
|
||||
URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)},
|
||||
|
|
@ -510,7 +488,6 @@ func (w *wallet) Derive(path accounts.DerivationPath, pin bool) (accounts.Accoun
|
|||
w.paths[address] = make(accounts.DerivationPath, len(path))
|
||||
copy(w.paths[address], path)
|
||||
}
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
||||
|
|
@ -537,7 +514,6 @@ func (w *wallet) SelfDerive(bases []accounts.DerivationPath, chain ethereum.Chai
|
|||
w.deriveNextPaths[i] = make(accounts.DerivationPath, len(base))
|
||||
copy(w.deriveNextPaths[i][:], base[:])
|
||||
}
|
||||
|
||||
w.deriveNextAddrs = make([]common.Address, len(bases))
|
||||
w.deriveChain = chain
|
||||
}
|
||||
|
|
@ -550,6 +526,7 @@ 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
|
||||
func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
|
||||
|
||||
// Unless we are doing 712 signing, simply dispatch to signHash
|
||||
if !(mimeType == accounts.MimetypeTypedData && len(data) == 66 && data[0] == 0x19 && data[1] == 0x01) {
|
||||
return w.signHash(account, crypto.Keccak256(data))
|
||||
|
|
@ -570,7 +547,6 @@ func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte
|
|||
}
|
||||
// All infos gathered and metadata checks out, request signing
|
||||
<-w.commsLock
|
||||
|
||||
defer func() { w.commsLock <- struct{}{} }()
|
||||
|
||||
// Ensure the device isn't screwed with while user confirmation is pending
|
||||
|
|
@ -589,7 +565,6 @@ func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return signature, nil
|
||||
}
|
||||
|
||||
|
|
@ -626,7 +601,6 @@ func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID
|
|||
}
|
||||
// All infos gathered and metadata checks out, request signing
|
||||
<-w.commsLock
|
||||
|
||||
defer func() { w.commsLock <- struct{}{} }()
|
||||
|
||||
// Ensure the device isn't screwed with while user confirmation is pending
|
||||
|
|
@ -645,11 +619,9 @@ func (w *wallet) SignTx(account accounts.Account, tx *types.Transaction, chainID
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if sender != account.Address {
|
||||
return nil, fmt.Errorf("signer mismatch: expected %s, got %s", account.Address.Hex(), sender.Hex())
|
||||
}
|
||||
|
||||
return signed, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ for:
|
|||
- go run build/ci.go lint
|
||||
- go run build/ci.go install -dlgo
|
||||
test_script:
|
||||
- go run build/ci.go test -dlgo
|
||||
- go run build/ci.go test -dlgo -coverage
|
||||
|
||||
# linux/386 is disabled.
|
||||
- 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 nsis -arch %GETH_ARCH% -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
||||
test_script:
|
||||
- go run build/ci.go test -dlgo -arch %GETH_ARCH% -cc %GETH_CC%
|
||||
- go run build/ci.go test -dlgo -arch %GETH_ARCH% -cc %GETH_CC% -coverage
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
// 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{}}
|
||||
)
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
// 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
|
||||
}
|
||||
|
|
@ -1,181 +0,0 @@
|
|||
// 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
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
// 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
|
||||
}
|
||||
|
|
@ -1,249 +0,0 @@
|
|||
// 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"`
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
#!/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
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
#!/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,6 +31,8 @@ Available commands are:
|
|||
importkeys -- imports signing keys from env
|
||||
debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package
|
||||
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
|
||||
|
||||
For all commands, -n prevents execution of external programs (dry run mode).
|
||||
|
|
@ -38,15 +40,18 @@ For all commands, -n prevents execution of external programs (dry run mode).
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
|
@ -73,6 +78,7 @@ var (
|
|||
executablePath("bootnode"),
|
||||
executablePath("evm"),
|
||||
executablePath("geth"),
|
||||
executablePath("puppeth"),
|
||||
executablePath("rlpdump"),
|
||||
executablePath("clef"),
|
||||
}
|
||||
|
|
@ -95,6 +101,10 @@ var (
|
|||
BinaryName: "geth",
|
||||
Description: "Ethereum CLI client.",
|
||||
},
|
||||
{
|
||||
BinaryName: "puppeth",
|
||||
Description: "Ethereum private network manager.",
|
||||
},
|
||||
{
|
||||
BinaryName: "rlpdump",
|
||||
Description: "Developer utility tool that prints RLP structures.",
|
||||
|
|
@ -120,15 +130,14 @@ var (
|
|||
// Distros for which packages are created.
|
||||
// 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:
|
||||
// wily, yakkety, zesty, artful, cosmic, disco, eoan, groovy, hirsuite, impish
|
||||
// wily, yakkety, zesty, artful, cosmic, disco, eoan, groovy, hirsuite
|
||||
debDistroGoBoots = map[string]string{
|
||||
"trusty": "golang-1.11", // EOL: 04/2024
|
||||
"xenial": "golang-go", // EOL: 04/2026
|
||||
"bionic": "golang-go", // EOL: 04/2028
|
||||
"focal": "golang-go", // EOL: 04/2030
|
||||
"jammy": "golang-go", // EOL: 04/2032
|
||||
"kinetic": "golang-go", // EOL: 07/2023
|
||||
"lunar": "golang-go", // EOL: 01/2024
|
||||
"impish": "golang-go", // EOL: 07/2022
|
||||
// "jammy": "golang-go", // EOL: 04/2027
|
||||
}
|
||||
|
||||
debGoBootPaths = map[string]string{
|
||||
|
|
@ -136,7 +145,7 @@ var (
|
|||
"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
|
||||
dlgoVersion = "1.20"
|
||||
|
|
@ -154,7 +163,7 @@ func executablePath(name string) string {
|
|||
func main() {
|
||||
log.SetFlags(log.Lshortfile)
|
||||
|
||||
if !common.FileExist(filepath.Join("build", "ci.go")) {
|
||||
if _, err := os.Stat(filepath.Join("build", "ci.go")); os.IsNotExist(err) {
|
||||
log.Fatal("this script must be run from the root of the repository")
|
||||
}
|
||||
if len(os.Args) < 2 {
|
||||
|
|
@ -175,6 +184,10 @@ func main() {
|
|||
doDebianSource(os.Args[2:])
|
||||
case "nsis":
|
||||
doWindowsInstaller(os.Args[2:])
|
||||
case "aar":
|
||||
doAndroidArchive(os.Args[2:])
|
||||
case "xcode":
|
||||
doXCodeFramework(os.Args[2:])
|
||||
case "purge":
|
||||
doPurge(os.Args[2:])
|
||||
default:
|
||||
|
|
@ -189,7 +202,6 @@ func doInstall(cmdline []string) {
|
|||
dlgo = flag.Bool("dlgo", false, "Download Go and build with it")
|
||||
arch = flag.String("arch", "", "Architecture to cross build for")
|
||||
cc = flag.String("cc", "", "C compiler to cross build with")
|
||||
staticlink = flag.Bool("static", false, "Create statically-linked executable")
|
||||
)
|
||||
flag.CommandLine.Parse(cmdline)
|
||||
|
||||
|
|
@ -200,12 +212,9 @@ func doInstall(cmdline []string) {
|
|||
tc.Root = build.DownloadGo(csdb, dlgoVersion)
|
||||
}
|
||||
|
||||
// Disable CLI markdown doc generation in release builds.
|
||||
buildTags := []string{"urfave_cli_no_docs"}
|
||||
|
||||
// Configure the build.
|
||||
env := build.Env()
|
||||
gobuild := tc.Go("build", buildFlags(env, *staticlink, buildTags)...)
|
||||
gobuild := tc.Go("build", buildFlags(env)...)
|
||||
|
||||
// arm64 CI builders are memory-constrained and can't handle concurrent builds,
|
||||
// better disable it. This check isn't the best, it should probably
|
||||
|
|
@ -238,35 +247,25 @@ func doInstall(cmdline []string) {
|
|||
}
|
||||
|
||||
// buildFlags returns the go tool flags for building.
|
||||
func buildFlags(env build.Environment, staticLinking bool, buildTags []string) (flags []string) {
|
||||
func buildFlags(env build.Environment) (flags []string) {
|
||||
var ld []string
|
||||
if env.Commit != "" {
|
||||
ld = append(ld, "-X", "github.com/maticnetwork/bor/internal/version.gitCommit="+env.Commit)
|
||||
ld = append(ld, "-X", "github.com/maticnetwork/bor/internal/version.gitDate="+env.Date)
|
||||
ld = append(ld, "-X", "main.gitCommit="+env.Commit)
|
||||
ld = append(ld, "-X", "main.gitDate="+env.Date)
|
||||
}
|
||||
// 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.
|
||||
if runtime.GOOS == "darwin" {
|
||||
ld = append(ld, "-s")
|
||||
}
|
||||
if runtime.GOOS == "linux" {
|
||||
// Enforce the stacksize to 8M, which is the case on most platforms apart from
|
||||
// alpine Linux.
|
||||
extld := []string{"-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 runtime.GOOS == "linux" {
|
||||
ld = append(ld, "-extldflags", "-Wl,-z,stack-size=0x800000")
|
||||
}
|
||||
if len(ld) > 0 {
|
||||
flags = append(flags, "-ldflags", strings.Join(ld, " "))
|
||||
}
|
||||
if len(buildTags) > 0 {
|
||||
flags = append(flags, "-tags", strings.Join(buildTags, ","))
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
|
|
@ -457,6 +456,10 @@ func maybeSkipArchive(env build.Environment) {
|
|||
log.Printf("skipping archive creation because this is a PR build")
|
||||
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.") {
|
||||
log.Printf("skipping archive creation because branch %q, tag %q is not on the inclusion list", env.Branch, env.Tag)
|
||||
os.Exit(0)
|
||||
|
|
@ -651,10 +654,10 @@ func doDebianSource(cmdline []string) {
|
|||
gpg.Stdin = bytes.NewReader(key)
|
||||
build.MustRun(gpg)
|
||||
}
|
||||
// Download and verify the Go source packages.
|
||||
var (
|
||||
gobundle = downloadGoSources(*cachedir)
|
||||
)
|
||||
|
||||
// Download and verify the Go source package.
|
||||
gobundle := downloadGoSources(*cachedir)
|
||||
|
||||
// Download all the dependencies needed to build the sources and run the ci script
|
||||
srcdepfetch := tc.Go("mod", "download")
|
||||
srcdepfetch.Env = append(srcdepfetch.Env, "GOPATH="+filepath.Join(*workdir, "modgopath"))
|
||||
|
|
@ -677,12 +680,12 @@ func doDebianSource(cmdline []string) {
|
|||
return
|
||||
}
|
||||
|
||||
// Add builder Go source code
|
||||
// Add Go source code
|
||||
if err := build.ExtractArchive(gobundle, canonicalPath); err != nil {
|
||||
log.Fatalf("Failed to extract builder Go sources: %v", err)
|
||||
log.Fatalf("Failed to extract Go sources: %v", err)
|
||||
}
|
||||
if err := os.Rename(filepath.Join(canonicalPath, "go"), filepath.Join(canonicalPath, ".go")); err != nil {
|
||||
log.Fatalf("Failed to rename builder Go source folder: %v", err)
|
||||
log.Fatalf("Failed to rename Go source folder: %v", err)
|
||||
}
|
||||
// Add all dependency modules in compressed form
|
||||
os.MkdirAll(filepath.Join(canonicalPath, ".mod", "cache"), 0755)
|
||||
|
|
@ -736,8 +739,8 @@ func ppaUpload(workdir, ppa, sshUser string, files []string) {
|
|||
var idfile string
|
||||
if sshkey := getenvBase64("PPA_SSH_KEY"); len(sshkey) > 0 {
|
||||
idfile = filepath.Join(workdir, "sshkey")
|
||||
if !common.FileExist(idfile) {
|
||||
os.WriteFile(idfile, sshkey, 0600)
|
||||
if _, err := os.Stat(idfile); os.IsNotExist(err) {
|
||||
ioutil.WriteFile(idfile, sshkey, 0600)
|
||||
}
|
||||
}
|
||||
// Upload
|
||||
|
|
@ -760,7 +763,7 @@ func makeWorkdir(wdflag string) string {
|
|||
if wdflag != "" {
|
||||
err = os.MkdirAll(wdflag, 0744)
|
||||
} else {
|
||||
wdflag, err = os.MkdirTemp("", "geth-build-")
|
||||
wdflag, err = ioutil.TempDir("", "geth-build-")
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
|
@ -958,10 +961,10 @@ func doWindowsInstaller(cmdline []string) {
|
|||
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)
|
||||
if err := cp.CopyFile(filepath.Join(*workdir, "SimpleFC.dll"), "build/nsis.simplefc.dll"); err != nil {
|
||||
log.Fatalf("Failed to copy SimpleFC.dll: %v", err)
|
||||
log.Fatal("Failed to copy SimpleFC.dll: %v", err)
|
||||
}
|
||||
if err := cp.CopyFile(filepath.Join(*workdir, "COPYING"), "COPYING"); err != nil {
|
||||
log.Fatalf("Failed to copy copyright note: %v", err)
|
||||
log.Fatal("Failed to copy copyright note: %v", err)
|
||||
}
|
||||
// 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
|
||||
|
|
@ -970,10 +973,7 @@ func doWindowsInstaller(cmdline []string) {
|
|||
if env.Commit != "" {
|
||||
version[2] += "-" + env.Commit[:8]
|
||||
}
|
||||
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)
|
||||
}
|
||||
installer, _ := filepath.Abs("geth-" + archiveBasename(*arch, params.ArchiveVersion(env.Commit)) + ".exe")
|
||||
build.MustRunCommand("makensis.exe",
|
||||
"/DOUTPUTFILE="+installer,
|
||||
"/DMAJORVERSION="+version[0],
|
||||
|
|
@ -988,6 +988,240 @@ 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
|
||||
|
||||
func doPurge(cmdline []string) {
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
_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
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
_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}}
|
||||
Standards-Version: 3.9.5
|
||||
Homepage: https://ethereum.org
|
||||
Vcs-Git: https://github.com/ethereum/go-ethereum.git
|
||||
Vcs-Git: git://github.com/ethereum/go-ethereum.git
|
||||
Vcs-Browser: https://github.com/ethereum/go-ethereum
|
||||
|
||||
Package: {{.Name}}
|
||||
|
|
|
|||
|
|
@ -1,5 +1 @@
|
|||
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,11 +16,7 @@ override_dh_auto_build:
|
|||
# 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
|
||||
# bootstrapping clashes with go modules, so build in a sibling folder.
|
||||
#
|
||||
# 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)
|
||||
(mv .go ../ && cd ../.go/src && ./make.bash)
|
||||
|
||||
# We can't download external go modules within Launchpad, so we're shipping the
|
||||
# entire dependency source cache with go-ethereum.
|
||||
|
|
|
|||
57
build/mvn.pom
Normal file
57
build/mvn.pom
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
<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>
|
||||
24
build/mvn.settings
Normal file
24
build/mvn.settings
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
<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>
|
||||
22
build/pod.podspec
Normal file
22
build/pod.podspec
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
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
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
// 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/>.
|
||||
|
||||
//go:build none
|
||||
// +build none
|
||||
|
||||
/*
|
||||
|
|
@ -40,6 +39,7 @@ import (
|
|||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
|
@ -68,9 +68,7 @@ var (
|
|||
"common/bitutil/bitutil",
|
||||
"common/prque/",
|
||||
"consensus/ethash/xor.go",
|
||||
"crypto/blake2b/",
|
||||
"crypto/bn256/",
|
||||
"crypto/bls12381/",
|
||||
"crypto/ecies/",
|
||||
"graphql/graphiql.go",
|
||||
"internal/jsre/deps",
|
||||
|
|
@ -243,7 +241,7 @@ func gitAuthors(files []string) []string {
|
|||
}
|
||||
|
||||
func readAuthors() []string {
|
||||
content, err := os.ReadFile("AUTHORS")
|
||||
content, err := ioutil.ReadFile("AUTHORS")
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
log.Fatalln("error reading AUTHORS:", err)
|
||||
}
|
||||
|
|
@ -307,7 +305,7 @@ func writeAuthors(files []string) {
|
|||
content.WriteString("\n")
|
||||
}
|
||||
fmt.Println("writing AUTHORS")
|
||||
if err := os.WriteFile("AUTHORS", content.Bytes(), 0644); err != nil {
|
||||
if err := ioutil.WriteFile("AUTHORS", content.Bytes(), 0644); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
|
@ -342,10 +340,7 @@ func isGenerated(file string) bool {
|
|||
}
|
||||
defer fd.Close()
|
||||
buf := make([]byte, 2048)
|
||||
n, err := fd.Read(buf)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
n, _ := fd.Read(buf)
|
||||
buf = buf[:n]
|
||||
for _, l := range bytes.Split(buf, []byte("\n")) {
|
||||
if bytes.HasPrefix(l, []byte("// Code generated")) {
|
||||
|
|
@ -386,7 +381,7 @@ func writeLicense(info *info) {
|
|||
if err != nil {
|
||||
log.Fatalf("error stat'ing %s: %v\n", info.file, err)
|
||||
}
|
||||
content, err := os.ReadFile(info.file)
|
||||
content, err := ioutil.ReadFile(info.file)
|
||||
if err != nil {
|
||||
log.Fatalf("error reading %s: %v\n", info.file, err)
|
||||
}
|
||||
|
|
@ -405,7 +400,7 @@ func writeLicense(info *info) {
|
|||
return
|
||||
}
|
||||
fmt.Println("writing", info.ShortLicense(), info.file)
|
||||
if err := os.WriteFile(info.file, buf.Bytes(), fi.Mode()); err != nil {
|
||||
if err := ioutil.WriteFile(info.file, buf.Bytes(), fi.Mode()); err != nil {
|
||||
log.Fatalf("error writing %s: %v", info.file, err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,6 @@ syncmode = "full"
|
|||
# evmtimeout = "5s"
|
||||
# txfeecap = 5.0
|
||||
# allow-unprotected-txs = false
|
||||
# enabledeprecatedpersonal = false
|
||||
# [jsonrpc.http]
|
||||
# enabled = false
|
||||
# port = 8545
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
// Copyright 2020 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
// Copyright 2019 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// The 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.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
// 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 main
|
||||
|
||||
|
|
@ -41,10 +41,8 @@ func parse(data []byte) {
|
|||
if err != nil {
|
||||
die(err)
|
||||
}
|
||||
|
||||
messages := apitypes.ValidationMessages{}
|
||||
db.ValidateCallData(nil, data, &messages)
|
||||
|
||||
for _, m := range messages.Messages {
|
||||
fmt.Printf("%v: %v\n", m.Typ, m.Message)
|
||||
}
|
||||
|
|
@ -58,12 +56,10 @@ func main() {
|
|||
switch {
|
||||
case flag.NArg() == 1:
|
||||
hexdata := flag.Arg(0)
|
||||
|
||||
data, err := hex.DecodeString(strings.TrimPrefix(hexdata, "0x"))
|
||||
if err != nil {
|
||||
die(err)
|
||||
}
|
||||
|
||||
parse(data)
|
||||
default:
|
||||
fmt.Fprintln(os.Stderr, "Error: one argument needed")
|
||||
|
|
|
|||
|
|
@ -19,94 +19,124 @@ package main
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common/compiler"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
// Git SHA1 commit hash of the release (set via linker flags)
|
||||
gitCommit = ""
|
||||
gitDate = ""
|
||||
|
||||
app *cli.App
|
||||
|
||||
// Flags needed by abigen
|
||||
abiFlag = &cli.StringFlag{
|
||||
abiFlag = cli.StringFlag{
|
||||
Name: "abi",
|
||||
Usage: "Path to the Ethereum contract ABI json to bind, - for STDIN",
|
||||
}
|
||||
binFlag = &cli.StringFlag{
|
||||
binFlag = cli.StringFlag{
|
||||
Name: "bin",
|
||||
Usage: "Path to the Ethereum contract bytecode (generate deploy method)",
|
||||
}
|
||||
typeFlag = &cli.StringFlag{
|
||||
typeFlag = cli.StringFlag{
|
||||
Name: "type",
|
||||
Usage: "Struct name for the binding (default = package name)",
|
||||
}
|
||||
jsonFlag = &cli.StringFlag{
|
||||
jsonFlag = cli.StringFlag{
|
||||
Name: "combined-json",
|
||||
Usage: "Path to the combined-json file generated by compiler, - for STDIN",
|
||||
Usage: "Path to the combined-json file generated by compiler",
|
||||
}
|
||||
excFlag = &cli.StringFlag{
|
||||
solFlag = cli.StringFlag{
|
||||
Name: "sol",
|
||||
Usage: "Path to the Ethereum contract Solidity source to build and bind",
|
||||
}
|
||||
solcFlag = cli.StringFlag{
|
||||
Name: "solc",
|
||||
Usage: "Solidity compiler to use if source builds are requested",
|
||||
Value: "solc",
|
||||
}
|
||||
vyFlag = cli.StringFlag{
|
||||
Name: "vy",
|
||||
Usage: "Path to the Ethereum contract Vyper source to build and bind",
|
||||
}
|
||||
vyperFlag = cli.StringFlag{
|
||||
Name: "vyper",
|
||||
Usage: "Vyper compiler to use if source builds are requested",
|
||||
Value: "vyper",
|
||||
}
|
||||
excFlag = cli.StringFlag{
|
||||
Name: "exc",
|
||||
Usage: "Comma separated types to exclude from binding",
|
||||
}
|
||||
pkgFlag = &cli.StringFlag{
|
||||
pkgFlag = cli.StringFlag{
|
||||
Name: "pkg",
|
||||
Usage: "Package name to generate the binding into",
|
||||
}
|
||||
outFlag = &cli.StringFlag{
|
||||
outFlag = cli.StringFlag{
|
||||
Name: "out",
|
||||
Usage: "Output file for the generated binding (default = stdout)",
|
||||
}
|
||||
langFlag = &cli.StringFlag{
|
||||
langFlag = cli.StringFlag{
|
||||
Name: "lang",
|
||||
Usage: "Destination language for the bindings (go)",
|
||||
Usage: "Destination language for the bindings (go, java, objc)",
|
||||
Value: "go",
|
||||
}
|
||||
aliasFlag = &cli.StringFlag{
|
||||
aliasFlag = cli.StringFlag{
|
||||
Name: "alias",
|
||||
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() {
|
||||
app.Name = "abigen"
|
||||
app = flags.NewApp(gitCommit, gitDate, "ethereum checkpoint helper tool")
|
||||
app.Flags = []cli.Flag{
|
||||
abiFlag,
|
||||
binFlag,
|
||||
typeFlag,
|
||||
jsonFlag,
|
||||
solFlag,
|
||||
solcFlag,
|
||||
vyFlag,
|
||||
vyperFlag,
|
||||
excFlag,
|
||||
pkgFlag,
|
||||
outFlag,
|
||||
langFlag,
|
||||
aliasFlag,
|
||||
}
|
||||
app.Action = abigen
|
||||
app.Action = utils.MigrateFlags(abigen)
|
||||
cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate
|
||||
}
|
||||
|
||||
func abigen(c *cli.Context) error {
|
||||
utils.CheckExclusive(c, abiFlag, jsonFlag) // Only one source can be selected.
|
||||
|
||||
if c.String(pkgFlag.Name) == "" {
|
||||
utils.CheckExclusive(c, abiFlag, jsonFlag, solFlag, vyFlag) // Only one source can be selected.
|
||||
if c.GlobalString(pkgFlag.Name) == "" {
|
||||
utils.Fatalf("No destination package specified (--pkg)")
|
||||
}
|
||||
|
||||
var lang bind.Lang
|
||||
|
||||
switch c.String(langFlag.Name) {
|
||||
switch c.GlobalString(langFlag.Name) {
|
||||
case "go":
|
||||
lang = bind.LangGo
|
||||
case "java":
|
||||
lang = bind.LangJava
|
||||
case "objc":
|
||||
lang = bind.LangObjC
|
||||
utils.Fatalf("Objc binding generation is uncompleted")
|
||||
default:
|
||||
utils.Fatalf("Unsupported destination language \"%s\" (--lang)", c.String(langFlag.Name))
|
||||
utils.Fatalf("Unsupported destination language \"%s\" (--lang)", c.GlobalString(langFlag.Name))
|
||||
}
|
||||
// If the entire solidity code was specified, build and bind based on that
|
||||
var (
|
||||
|
|
@ -117,78 +147,76 @@ func abigen(c *cli.Context) error {
|
|||
libs = make(map[string]string)
|
||||
aliases = make(map[string]string)
|
||||
)
|
||||
|
||||
// nolint:nestif
|
||||
if c.String(abiFlag.Name) != "" {
|
||||
if c.GlobalString(abiFlag.Name) != "" {
|
||||
// Load up the ABI, optional bytecode and type name from the parameters
|
||||
var (
|
||||
abi []byte
|
||||
err error
|
||||
)
|
||||
|
||||
input := c.String(abiFlag.Name)
|
||||
input := c.GlobalString(abiFlag.Name)
|
||||
if input == "-" {
|
||||
abi, err = io.ReadAll(os.Stdin)
|
||||
abi, err = ioutil.ReadAll(os.Stdin)
|
||||
} else {
|
||||
abi, err = os.ReadFile(input)
|
||||
abi, err = ioutil.ReadFile(input)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to read input ABI: %v", err)
|
||||
}
|
||||
|
||||
abis = append(abis, string(abi))
|
||||
|
||||
var bin []byte
|
||||
|
||||
if binFile := c.String(binFlag.Name); binFile != "" {
|
||||
if bin, err = os.ReadFile(binFile); err != nil {
|
||||
if binFile := c.GlobalString(binFlag.Name); binFile != "" {
|
||||
if bin, err = ioutil.ReadFile(binFile); err != nil {
|
||||
utils.Fatalf("Failed to read input bytecode: %v", err)
|
||||
}
|
||||
|
||||
if strings.Contains(string(bin), "//") {
|
||||
utils.Fatalf("Contract has additional library references, please use other mode(e.g. --combined-json) to catch library infos")
|
||||
}
|
||||
}
|
||||
|
||||
bins = append(bins, string(bin))
|
||||
|
||||
kind := c.String(typeFlag.Name)
|
||||
kind := c.GlobalString(typeFlag.Name)
|
||||
if kind == "" {
|
||||
kind = c.String(pkgFlag.Name)
|
||||
kind = c.GlobalString(pkgFlag.Name)
|
||||
}
|
||||
|
||||
types = append(types, kind)
|
||||
} else {
|
||||
// Generate the list of types to exclude from binding
|
||||
var exclude *nameFilter
|
||||
|
||||
if c.IsSet(excFlag.Name) {
|
||||
exclude := make(map[string]bool)
|
||||
for _, kind := range strings.Split(c.GlobalString(excFlag.Name), ",") {
|
||||
exclude[strings.ToLower(kind)] = true
|
||||
}
|
||||
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
|
||||
|
||||
if c.IsSet(jsonFlag.Name) {
|
||||
var (
|
||||
input = c.String(jsonFlag.Name)
|
||||
jsonOutput []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if input == "-" {
|
||||
jsonOutput, err = io.ReadAll(os.Stdin)
|
||||
} else {
|
||||
jsonOutput, err = os.ReadFile(input)
|
||||
}
|
||||
|
||||
switch {
|
||||
case c.GlobalIsSet(solFlag.Name):
|
||||
contracts, err = compiler.CompileSolidity(c.GlobalString(solcFlag.Name), c.GlobalString(solFlag.Name))
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to read combined-json: %v", err)
|
||||
utils.Fatalf("Failed to build Solidity contract: %v", err)
|
||||
}
|
||||
case c.GlobalIsSet(vyFlag.Name):
|
||||
output, err := compiler.CompileVyper(c.GlobalString(vyperFlag.Name), c.GlobalString(vyFlag.Name))
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to build Vyper contract: %v", err)
|
||||
}
|
||||
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 {
|
||||
utils.Fatalf("Failed to read combined-json from compiler: %v", err)
|
||||
}
|
||||
contracts, err = compiler.ParseCombinedJSON(jsonOutput, "", "", "", "")
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to read contract information from json output: %v", err)
|
||||
|
|
@ -196,61 +224,48 @@ func abigen(c *cli.Context) error {
|
|||
}
|
||||
// Gather all non-excluded contract for binding
|
||||
for name, contract := range contracts {
|
||||
// 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)
|
||||
if exclude[strings.ToLower(name)] {
|
||||
continue
|
||||
}
|
||||
|
||||
abi, err := json.Marshal(contract.Info.AbiDefinition) // Flatten the compiler parse
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to parse ABIs from compiler output: %v", err)
|
||||
}
|
||||
|
||||
abis = append(abis, string(abi))
|
||||
bins = append(bins, contract.Code)
|
||||
sigs = append(sigs, contract.Hashes)
|
||||
types = append(types, typeName)
|
||||
nameParts := strings.Split(name, ":")
|
||||
types = append(types, nameParts[len(nameParts)-1])
|
||||
|
||||
// Derive the library placeholder which is a 34 character prefix of the
|
||||
// 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
|
||||
libPattern := crypto.Keccak256Hash([]byte(name)).String()[2:36]
|
||||
libs[libPattern] = nameParts[len(nameParts)-1]
|
||||
}
|
||||
}
|
||||
// Extract all aliases from the flags
|
||||
if c.IsSet(aliasFlag.Name) {
|
||||
if c.GlobalIsSet(aliasFlag.Name) {
|
||||
// We support multi-versions for aliasing
|
||||
// e.g.
|
||||
// foo=bar,foo2=bar2
|
||||
// foo:bar,foo2:bar2
|
||||
re := regexp.MustCompile(`(?:(\w+)[:=](\w+))`)
|
||||
|
||||
submatches := re.FindAllStringSubmatch(c.String(aliasFlag.Name), -1)
|
||||
submatches := re.FindAllStringSubmatch(c.GlobalString(aliasFlag.Name), -1)
|
||||
for _, match := range submatches {
|
||||
aliases[match[1]] = match[2]
|
||||
}
|
||||
}
|
||||
// Generate the contract binding
|
||||
code, err := bind.Bind(types, abis, bins, sigs, c.String(pkgFlag.Name), lang, libs, aliases)
|
||||
code, err := bind.Bind(types, abis, bins, sigs, c.GlobalString(pkgFlag.Name), lang, libs, aliases)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to generate ABI binding: %v", err)
|
||||
}
|
||||
// Either flush it out to a file or display on the standard output
|
||||
if !c.IsSet(outFlag.Name) {
|
||||
if !c.GlobalIsSet(outFlag.Name) {
|
||||
fmt.Printf("%s\n", code)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := os.WriteFile(c.String(outFlag.Name), []byte(code), 0600); err != nil {
|
||||
if err := ioutil.WriteFile(c.GlobalString(outFlag.Name), []byte(code), 0600); err != nil {
|
||||
utils.Fatalf("Failed to write ABI binding: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,61 +0,0 @@
|
|||
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]
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -40,7 +40,7 @@ func main() {
|
|||
writeAddr = flag.Bool("writeaddress", false, "write out the node's public key and quit")
|
||||
nodeKeyFile = flag.String("nodekey", "", "private key filename")
|
||||
nodeKeyHex = flag.String("nodekeyhex", "", "private key as hex (for testing)")
|
||||
natdesc = flag.String("nat", "none", "port mapping mechanism (any|none|upnp|pmp|pmp:<IP>|extip:<IP>)")
|
||||
natdesc = flag.String("nat", "none", "port mapping mechanism (any|none|upnp|pmp|extip:<IP>)")
|
||||
netrestrict = flag.String("netrestrict", "", "restrict network communication to the given IP networks (CIDR masks)")
|
||||
runv5 = flag.Bool("v5", false, "run a v5 topic discovery bootnode")
|
||||
verbosity = flag.Int("verbosity", int(log.LvlInfo), "log verbosity (0-5)")
|
||||
|
|
@ -49,7 +49,6 @@ func main() {
|
|||
nodeKey *ecdsa.PrivateKey
|
||||
err error
|
||||
)
|
||||
|
||||
flag.Parse()
|
||||
|
||||
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
|
||||
|
|
@ -61,18 +60,15 @@ func main() {
|
|||
if err != nil {
|
||||
utils.Fatalf("-nat: %v", err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case *genKey != "":
|
||||
nodeKey, err = crypto.GenerateKey()
|
||||
if err != nil {
|
||||
utils.Fatalf("could not generate key: %v", err)
|
||||
}
|
||||
|
||||
if err = crypto.SaveECDSA(*genKey, nodeKey); err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
|
||||
if !*writeAddr {
|
||||
return
|
||||
}
|
||||
|
|
@ -107,7 +103,6 @@ func main() {
|
|||
if err != nil {
|
||||
utils.Fatalf("-ResolveUDPAddr: %v", err)
|
||||
}
|
||||
|
||||
conn, err := net.ListenUDP("udp", addr)
|
||||
if err != nil {
|
||||
utils.Fatalf("-ListenUDP: %v", err)
|
||||
|
|
@ -118,7 +113,6 @@ func main() {
|
|||
if !realaddr.IP.IsLoopback() {
|
||||
go nat.Map(natm, nil, "udp", realaddr.Port, realaddr.Port, "ethereum discovery")
|
||||
}
|
||||
|
||||
if ext, err := natm.ExternalIP(); err == nil {
|
||||
realaddr = &net.UDPAddr{IP: ext, Port: realaddr.Port}
|
||||
}
|
||||
|
|
@ -132,7 +126,6 @@ func main() {
|
|||
PrivateKey: nodeKey,
|
||||
NetRestrict: restrictList,
|
||||
}
|
||||
|
||||
if *runv5 {
|
||||
if _, err := discover.ListenV5(conn, ln, cfg); err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
|
|
@ -150,7 +143,6 @@ func printNotice(nodeKey *ecdsa.PublicKey, addr net.UDPAddr) {
|
|||
if addr.IP.IsUnspecified() {
|
||||
addr.IP = net.IP{127, 0, 0, 1}
|
||||
}
|
||||
|
||||
n := enode.NewV4(nodeKey, addr.IP, 0, addr.Port)
|
||||
fmt.Println(n.URLv4())
|
||||
fmt.Println("Note: you're using cmd/bootnode, a developer tool.")
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue